# Combos

> The combo channels, their commands and the models of their replies and pushes.

Source: https://docs.stxapp.io/sdks/python/reference/combos/

## `RfqChannel`

Your player channel, `rfq:<user_id>`.

Returned by `stx.STXWebSocket.rfq`. Pushes arrive with `data`
set to `stx.combos.BestQuote` (`best_quote`),
`stx.combos.ComboPlacement` (`placed`) or
`stx.combos.NegotiationClosed` (`negotiation_closed`).

A request belongs to the channel that opened it: leaving the channel or
losing the connection cancels it, and a reconnect does not restore it.

Each command raises `stx.STXComboException` carrying the
exchange's `reason` when it is refused.

### Methods

#### `accept()`

```python
accept(rfq_id: str, quote_id: str, amount: Money, *, timeout: Optional[float] = ACCEPT_TIMEOUT) -> ComboPlacement
```

Accept the best quote on `rfq_id` if it is `amount` or more.

Pass the `quote_id` and `amount` from the `best_quote` you are
accepting. The best live quote is placed when its amount is at least
`amount`, so the result can be better than the one you saw. When
the best has fallen below `amount` this raises
`stx.STXComboPriceMovedException`, whose `best_quote` is the
live best, and the request stays open. The exchange can take up to
30 seconds to answer.

Returns the order placed for you; `rfq_id` is filled in.

| Parameter | Type | Description |
|---|---|---|
| `rfq_id` | `str` |  |
| `quote_id` | `str` |  |
| `amount` | `Money` |  |
| `timeout` | `Optional[float]` |  |

#### `cancel()`

```python
cancel(rfq_id: str, *, timeout: Optional[float] = None) -> CancelledRequest
```

End the request `rfq_id` and drop every quote on it.

| Parameter | Type | Description |
|---|---|---|
| `rfq_id` | `str` |  |
| `timeout` | `Optional[float]` |  |

#### `cash_out()`

```python
cash_out(market_id: str, *, timeout: Optional[float] = None) -> CashOut
```

Ask makers for a quote on what you hold of the combo `market_id`.

The exchange sets the size: your position less what your resting
sells already offer. Accept a quote as for a request.

| Parameter | Type | Description |
|---|---|---|
| `market_id` | `str` |  |
| `timeout` | `Optional[float]` |  |

#### `request_quote()`

```python
request_quote(legs: Sequence[LegLike], amount: Money, *, timeout: Optional[float] = None) -> QuoteRequest
```

Ask makers for a quote on `legs` for a stake of `amount`.

Each leg is a `stx.combos.ComboLeg`, a mapping with
`market_id` and `outcome`, or a `(market_id, outcome)` pair;
`outcome` is `"won"` or `"lost"`. `amount` is in whole cents,
as a string or `Decimal`. The request lives five minutes.

Example:

```python
request = await rfq.request_quote(
    [("5f0c9d7e-2b8a-4c61-9e3f-1a7d4b2c8e90", "won"),
     ("b3e81f42-7c5d-4a09-8d16-6f2e9a0c4b37", "won")],
    amount="25.00",
)
print(request.rfq_id)
```

| Parameter | Type | Description |
|---|---|---|
| `legs` | `Sequence[LegLike]` |  |
| `amount` | `Money` |  |
| `timeout` | `Optional[float]` |  |

#### `snapshot()`

```python
snapshot(rfq_id: str, *, timeout: Optional[float] = None) -> RfqSnapshot
```

The request `rfq_id` as it stands now, with the live best quote.

Unlike the `best_quote` push it is not delayed by the opening
window or the push cadence.

| Parameter | Type | Description |
|---|---|---|
| `rfq_id` | `str` |  |
| `timeout` | `Optional[float]` |  |

## `ComboQuoteChannel`

Your maker channel, `combo_quote:<user_id>`.

Returned by `stx.STXWebSocket.combo_quote`. Every request opened
after the join arrives as `rfq_opened`; earlier ones are not replayed.
Pushes arrive with `data` set to `stx.combos.RfqOpened`,
`stx.combos.RfqClosed`, `stx.combos.QuoteAccepted`,
`stx.combos.MakerPlacement` (`placed`) or
`stx.combos.NegotiationClosed`.

A quote belongs to the channel that placed it: leaving the channel or
losing the connection withdraws it.

### Methods

#### `quote()`

```python
quote(rfq_id: str, amount: Money, expires_in_ms: int, *, client_order_id: Optional[str] = None, timeout: Optional[float] = None) -> ComboQuote
```

Quote `amount` on the request `rfq_id` for `expires_in_ms`.

On a buy, `amount` is what the player wins; on a cash out, what
you pay for the whole size. `client_order_id` is copied onto your
order if the quote is accepted. To change a quote, withdraw it and
quote again.

Example:

```python
quote = await makers.quote(opened.rfq_id, "180.00", 30_000,
                           client_order_id="mm-0417")
```

| Parameter | Type | Description |
|---|---|---|
| `rfq_id` | `str` |  |
| `amount` | `Money` |  |
| `expires_in_ms` | `int` |  |
| `client_order_id` | `Optional[str]` |  |
| `timeout` | `Optional[float]` |  |

#### `withdraw_quote()`

```python
withdraw_quote(rfq_id: str, quote_id: str, *, timeout: Optional[float] = None) -> WithdrawnQuote
```

Withdraw your quote `quote_id` from the request `rfq_id`.

| Parameter | Type | Description |
|---|---|---|
| `rfq_id` | `str` |  |
| `quote_id` | `str` |  |
| `timeout` | `Optional[float]` |  |

## `ComboLeg`

One leg of a combo: a market and the outcome the combo needs of it.

`short_name` is filled in on `rfq_opened` and ignored when you send
a leg.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `market_id` | `Optional[str]` |  |
| `outcome` | `Optional[str]` |  |
| `short_name` | `Optional[str]` |  |

## `ComboJoinReply`

The reply to joining either combo channel.

`external_id` is your account's pseudonym, the one makers see on your
requests.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `external_id` | `Optional[str]` |  |

## `QuoteRequest`

The reply to `RfqChannel.request_quote`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |

## `CashOut`

The reply to `RfqChannel.cash_out`. `quantity` is the number of
contracts being sold, set by the exchange.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `quantity` | `Optional[str]` |  |

## `BestQuote`

The best live quote on a request.

The `best_quote` push, and the `best_quote` field of
`RfqSnapshot`. `quote_id`, `amount` and `expires_at` are
`None` when nothing is live. `amount` is what you win on a buy, or
what you are paid for the whole size on a cash out.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `quote_id` | `Optional[str]` |  |
| `amount` | `Optional[str]` |  |
| `expires_at` | `Optional[int]` |  |

## `ComboPlacement`

Your order on the combo's market, placed by an accepted quote.

The reply to `RfqChannel.accept` and the player's `placed` push.
`quantity` is what was placed, not what filled: fills arrive on the
`orders` and `fills` channels.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `order_id` | `Optional[str]` |  |
| `quantity` | `Optional[str]` |  |
| `price` | `Optional[str]` |  |

## `RfqSnapshot`

The reply to `RfqChannel.snapshot`: a request as it stands now.

`status` is `open`, or `binding` while an accept is being placed.
`side` is `buy` for a request and `sell` for a cash out.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `status` | `Optional[str]` |  |
| `side` | `Optional[str]` |  |
| `amount` | `Optional[str]` |  |
| `quantity` | `Optional[str]` |  |
| `expires_at` | `Optional[int]` |  |
| `best_quote` | `Optional[BestQuote]` |  |

## `CancelledRequest`

The reply to `RfqChannel.cancel`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `cancelled` | `Optional[str]` |  |

## `NegotiationClosed`

`negotiation_closed`: a request, or one of your quotes, ended without
your order being placed.

On the player's channel it names the request and the `reason`
(`cancelled`, `expired`, `leg_not_quotable`, `unknown_leg`,
`player_refused` or `maker_refused`). On the maker's channel it is
sent once per quote and also carries `quote_id` and the player's
`external_id`; the reason can also be `placed_elsewhere`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `reason` | `Optional[str]` |  |
| `external_id` | `Optional[str]` |  |
| `quote_id` | `Optional[str]` |  |

## `RfqOpened`

`rfq_opened`: a player opened a request. Sent to every maker.

`side` is the player's side. On `buy`, `amount` is the stake and
`market_id` and `quantity` are `None`; on `sell` (a cash out),
`market_id` and `quantity` are set and `amount` is `None`. Price
against `max_price` and `tick_size`, not a constant.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `external_id` | `Optional[str]` |  |
| `side` | `Optional[str]` |  |
| `amount` | `Optional[str]` |  |
| `quantity` | `Optional[str]` |  |
| `max_price` | `Optional[str]` |  |
| `tick_size` | `Optional[str]` |  |
| `expires_at` | `Optional[int]` |  |
| `legs` | `Optional[List[ComboLeg]]` |  |

## `RfqClosed`

`rfq_closed`: a request ended, for whatever reason. Sent to every maker.

`reason` is `placed`, `cancelled`, `expired`,
`leg_not_quotable`, `unknown_leg`, `player_refused`,
`maker_refused` or `ended`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `reason` | `Optional[str]` |  |

## `ComboQuote`

The reply to `ComboQuoteChannel.quote`. `amount` is as recorded
by the exchange; `external_id` is the player's.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `quote_id` | `Optional[str]` |  |
| `amount` | `Optional[str]` |  |
| `external_id` | `Optional[str]` |  |

## `WithdrawnQuote`

The reply to `ComboQuoteChannel.withdraw_quote`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `withdrawn` | `Optional[str]` |  |

## `QuoteAccepted`

`quote_accepted`: your quote is about to be placed. `placed` or
`negotiation_closed` follows, or nothing when the attempt left the
request open and your quote standing.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `external_id` | `Optional[str]` |  |
| `quote_id` | `Optional[str]` |  |

## `MakerPlacement`

The maker's `placed` push: your quote was accepted and your order
placed. Both sides' orders share `quantity` and `price`.

### Attributes

| Attribute | Type | Description |
|---|---|---|
| `rfq_id` | `Optional[str]` |  |
| `market_id` | `Optional[str]` |  |
| `external_id` | `Optional[str]` |  |
| `quote_id` | `Optional[str]` |  |
| `order_id` | `Optional[str]` |  |
| `quantity` | `Optional[str]` |  |
| `price` | `Optional[str]` |  |
