# Orders Channel


Source: https://docs.stxapp.io/websockets/channels/orders/

Topic: `orders:{user_id}`

Delivers your orders as they are accepted and filled. Money arrives as dollar
strings and contract counts as quantity strings; see
[Wire format](/websockets/channels/wire-format/).

## Order object

### Identity

- `id` : The unique id of the order.
- `market_id` : The id of the market.
- `client_order_id` : The id you supplied when placing the order, if any.
- `fix_order` : `true` if the order arrived over FIX rather than REST.

### Sizing

- `quantity` : The number of contracts to buy or sell, as a quantity string.
  Null on an order sized by `amount` instead.
- `filled` : How many of the order's contracts have been filled so far, as a
  quantity string.
- `filled_percentage` : The same, as an integer percentage (0–100), truncated.
- `amount` : Order size in dollars, for an order entered by amount rather than by
  `quantity`, which only an STX app does. **Null on an order sized by `quantity`**; the two are
  alternatives, not a value and its derivation.
- `filled_amount` : The portion of `amount` that has been filled. Only meaningful on
  an order sized by `amount`; it reads `"0.0000"` otherwise.

### Pricing

- `price` : The order's price, as a dollar string. Null when `order_type` is
  `market`. Stored to seven decimal places, so this field can be wider than the
  usual four.
- `avg_price` : The average price the filled portion traded at, as a dollar string
  rounded to the cent. Null until something fills.
- `total_value` : Total premium across every fill on this order: the sum of
  `filled × price` over its trades. `"0.0000"` until something fills.
- `odds_type` : Legacy. `decimal` or `american`, when an STX app entered the order
  in odds rather than price. Null for orders placed through the API.
- `odds_value` : Legacy. The odds that app order was entered at, as a string. Null
  for orders placed through the API.

### Lifecycle

- `action` : Whether the order is a `buy` or a `sell`.
- `order_type` : `limit` or `market`. A limit order sets a ceiling for a buy or a
  floor for a sell. If it cannot fill completely it rests on the book for the
  remainder. A market order has no price limit and either fills completely or has
  its remainder cancelled.
- `status` : One of `created`, `requested`, `accepted`, `delayed`, `open`, `filled`,
  `rejected`, `cancelled` or `partially_cancelled`; see
  [Market and order status](/concepts/market-status/).
- `time` : When the order was created, ISO 8601.
- `inserted_at` : The same instant, as an integer in Unix microseconds.
- `accepted_at` : When the matching engine accepted the order, as Unix
  microseconds. Null while the order is still pending.
- `expires_at` : When the contracts this order trades expire, as Unix
  microseconds: the market's expiration, copied onto the order when it is
  placed. Null when the market has no event. Not the same as `expiration_time`,
  which is when a `good_till_time` order stops resting on the book.
- `cancellation_reason` : Why the order was cancelled: `by_player`,
  `market_closed`, `market_cancelled`, `expired`, `by_operator`, `auto_matching`,
  `on_disconnect`, `negative_balance`, `insufficient_assets`,
  `member_position_limit`, `admin_trade_cancelled` or `admin_trade_price_change`.
  Null unless `status` is `cancelled` or `partially_cancelled`.
- `rejection_reason` : Why the order was rejected, e.g. `insufficient_assets`,
  `account_limits_reached`, `market_liability_limit`, `member_position_limit`,
  `match_with_self`, `invalid_geo_location`, `wrong_market_state` or
  `validation_error`. Null unless `status` is `rejected`.

### Risk controls

See [Risk controls](/risk-controls/) for what these do.

- `expiration` : `good_till_start`, `good_till_time`, or null for an order that
  rests until cancelled.
- `expiration_time` : When a `good_till_time` order expires, as Unix microseconds.
  Null for every other `expiration`.
- `delayed_until` : When an order held by the in-play delay reaches the book, as
  Unix microseconds. Null when no delay applies.
- `placed_pre_start` : Whether the order was placed before the event started.

### Provenance

- `ip_address` : The IP address the order was placed from. Null if none was
  recorded.
- `device_id` : The device id the order was placed from. Null if none was recorded.
- `ux_action` : The intent the order was placed with in the app: `buy_yes`,
  `buy_no`, `sell_yes` or `sell_no`. Null for an order placed through the API.

## Use Cases

| Use case | Message to send |
| --- | --- |
| Join and receive your open orders | `["3","3","orders:<user_id>","phx_join",{}]` |
| Join filtered to two markets (see [filtering](/websockets/channels/wire-format/#filtering)) | `["3","3","orders:<user_id>","phx_join",{"market_ids":["<uuid>","<uuid>"]}]` |
| Join with `cancel_on_disconnect` enabled (see [Risk controls](/risk-controls/)) | `["3","3","orders:<user_id>","phx_join",{"cancel_on_disconnect":true,"ping_timeout":5000}]` |
| Change the market filter without rejoining | `["3","4","orders:<user_id>","select_market_ids",{"market_ids":["<uuid>"]}]` |
| Keep a `cancel_on_disconnect` session alive | `["3","5","orders:<user_id>","ping",{}]` |

### Joining

```json
["3","3","orders:<user_id>","phx_join",{}]
```

The reply echoes the filter that was applied, and the `cancel_on_disconnect`
settings when you asked for them:

```json
{"status":"ok","response":{"selected_market_ids":null}}
```

```json
{"status":"ok","response":{"selected_market_ids":null,"cancel_on_disconnect":true,"ping_timeout":5000}}
```

`ping_timeout` is clamped to 5000–20000 ms, so read the value back from the reply
rather than assuming the one you sent was honored.

### Initial response after joining

```json
[null, null, "orders:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "all_orders", { "orders": [Order]}]
```

`orders` is a list of `Order` objects, described above, empty if you have none
open, and also empty when a `market_ids` filter matches nothing.

```json
[
  null,
  null,
  "orders:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "all_orders",
  {
    "orders": [
      {
        "id": "7dc6671e-2852-44f4-803d-4405d8a85407",
        "market_id": "002b030c-5ac2-41f4-92de-175e666b0a70",
        "client_order_id": null,
        "fix_order": false,
        "quantity": "20.00",
        "filled": "0.00",
        "filled_percentage": 0,
        "price": "0.1000",
        "avg_price": null,
        "amount": null,
        "filled_amount": "0.0000",
        "total_value": "0.0000",
        "odds_type": null,
        "odds_value": null,
        "action": "sell",
        "order_type": "limit",
        "status": "open",
        "time": "2026-11-06T21:34:30.376858Z",
        "inserted_at": 1794173670376858,
        "accepted_at": 1794173670381204,
        "expires_at": 1825736399999999,
        "cancellation_reason": null,
        "rejection_reason": null,
        "expiration": null,
        "expiration_time": null,
        "delayed_until": null,
        "placed_pre_start": true,
        "ip_address": "203.0.113.42",
        "device_id": "web-chrome-114",
        "ux_action": null
      }
    ]
  }
]
```

### Pushed when an order is accepted or filled

```json
[null, null, "orders:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "new_open_order", Order]
```

One frame per order, carrying the whole object rather than a diff.

```json
[
  null,
  null,
  "orders:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "new_open_order",
  {
    "id": "7dc6671e-2852-44f4-803d-4405d8a85407",
    "market_id": "002b030c-5ac2-41f4-92de-175e666b0a70",
    "client_order_id": null,
    "fix_order": false,
    "quantity": "20.00",
    "filled": "10.00",
    "filled_percentage": 50,
    "price": "0.1000",
    "avg_price": "0.1000",
    "amount": null,
    "filled_amount": "0.0000",
    "total_value": "1.0000",
    "odds_type": null,
    "odds_value": null,
    "action": "sell",
    "order_type": "limit",
    "status": "open",
    "time": "2026-11-06T21:34:30.376858Z",
    "inserted_at": 1794173670376858,
    "accepted_at": 1794173670381204,
    "expires_at": 1825736399999999,
    "cancellation_reason": null,
    "rejection_reason": null,
    "expiration": null,
    "expiration_time": null,
    "delayed_until": null,
    "placed_pre_start": true,
    "ip_address": "203.0.113.42",
    "device_id": "web-chrome-114",
    "ux_action": null
  }
]
```

:::tip[The fill itself arrives on another channel]
`new_open_order` tells you the order's state changed. The execution that caused it
(price, fee, premium) arrives separately on [`fills`](/websockets/channels/fills/), and the
two channels are not ordered relative to each other. Key off `order_id` and
reconcile rather than assuming arrival order.
:::
