# Subscribing to Channels

Send `subscription.request` after receiving `connection.connected`. Use a distinct
`request_id` to correlate the acknowledgement or error. Heartbeats, other acknowledgements
and data from existing subscriptions can arrive while a request is pending.

The examples use the WebSocket message envelope and camelCase payload fields. Account names,
person IDs and strategy IDs are illustrative; replace them with values you can access.
`DU123456` and broker ID `111` represent an example Interactive Brokers demo account.
Generate current UTC timestamps when sending requests, as the
[complete connection example](/guides/websocket-connect#persistent-multi-channel-python-client) does.
Client `id` values are ignored; clients may omit them.

## Subscription Fields

| Payload field | Type | Requirement |
|  --- | --- | --- |
| `action` | string | `subscribe` or `unsubscribe` |
| `channel` | string | One of the channels in the [overview](/guides/websocket-overview) |
| `strategy_id` | integer | Required for strategy channels |
| `account` | string | Required for all account channels. Exact account name, at most 45 characters |
| `broker_id` | integer | Required and positive for all account channels |
| `person_id` | integer | Optional account authorization person. Omitted means the session person; an explicit value must be positive and authorized |
| `resume_from` | integer | Optional cursor for `strategy.signal` or `strategy.live_fills`. Rejected for account channels |


`account` and `broker_id` identify the account. The resolved `person_id` must match the selected
account owner. A subscription acknowledgement includes that
resolved person even when the request omitted it. Subscription identity is the channel plus
account/broker pair on this connection; a different `person_id` does not create another
subscription for the same identity. Unsubscribe addresses that identity and acknowledges the
resolved person.

A successful account subscription receives an acknowledgement followed by its initial snapshot.
Data from other channels can still arrive while a request is pending. An empty snapshot is
successful. If the initial snapshot is unavailable, the server returns a correlated
`SERVICE_UNAVAILABLE`; retry with backoff.

## Account Orders

`account.orders` uses [the WebSocket order row schema](/apis/general/swagger/schemas/websocketorderstatusdto)
and [its envelope](/apis/general/swagger/schemas/accountordersmessage). Its initial array
contains the complete current working set. Later arrays contain changed complete rows.

Use this channel to track new account orders, partial fills, final fills and cancellations.
The default refresh interval is one second. Updates arrive when order data changes;
an unchanged account produces no message.

A working limit order can arrive before any fill. You do not need a `strategy.signal`
subscription to receive it. A strategy signal must result in an order for this account before
it can appear here; account settings and instrument eligibility can prevent order creation.

### Order Fields

| Payload field | Type | Description |
|  --- | --- | --- |
| `id` | integer | Stable account order-row ID. Use this field to replace a row in local state |
| `orderId` | string | Broker order ID |
| `brokerId` | integer | Broker identifier, also present as `broker_id` in the envelope |
| `strategyId` | integer | Strategy associated with the account order |
| `strategyName` | string | Strategy display name, when available |
| `signalId` | integer | Associated C2 signal ID, when available |
| `signalType` | integer | `1`: strategy signal, `4`: manual signal, `5`: manual override, `6`: automatic stop loss |
| `orderStatus` | string | Current status. Working statuses and removal rules are described below |
| `orderType` | string | `1`: market, `2`: limit, `3`: stop |
| `side` | string | `1`: buy, `2`: sell |
| `openClose` | string | `O`: opening order, `C`: closing order |
| `orderQuantity` | number | Unsigned order quantity |
| `filledQuantity` | number | Cumulative filled quantity, including zero for an unfilled order |
| `avgFillPrice` | number | Average fill price, when available. Omitted before a fill |
| `limit`, `stop` | number | Applicable limit or stop price. Omitted when not applicable |
| `tif` | string | `0`: day, `1`: good till canceled |
| `postedDate` | string | UTC date and time when the order was posted |
| `parentSignalId`, `cancelReplaceSignalId` | integer | Related parent or cancel/replace signal, when present |
| `c2Symbol`, `exchangeSymbol` | object | C2 and broker instrument identifiers |
| `info` | string | Additional order information, when available |


Subscribe with the session person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_23b8d8677dd04cb09e9984d88086",
  "request_id": "orders-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.orders",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Subscribe with an explicit authorized person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_f09c9383e79a43dc915954f5ed0e",
  "request_id": "orders-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.orders",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

The successful acknowledgement echoes the resolved identity:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_0b152d1f950b474987beeaed830a",
  "request_id": "orders-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.orders",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

Unsubscribe using the same channel, account and broker:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_1cbc50755abf4a32b680cc4d8022",
  "request_id": "orders-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.orders",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Successful unsubscribe acknowledgement:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_d18b19350fec4a489002b0190632",
  "request_id": "orders-unsub123456789",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.orders",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

An unauthorized subscription returns a correlated error:


```json
{
  "msg_type": "error",
  "id": "msg_fb641e8278324b2880965fe95904",
  "request_id": "orders-subunsub123456789",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "code": "FORBIDDEN",
    "field": "payload.account",
    "message": "Not authorized to subscribe to account"
  }
}
```

Initial working-order snapshot:


```json
{
  "msg_type": "account.orders",
  "id": "msg_32b576f235004226b815a9b711ee",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "brokerId": 111,
      "orderStatus": "0",
      "filledQuantity": 0,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9001,
      "orderId": "broker-order-42",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456789,
      "orderType": "1",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "0",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

An account with no working orders receives:


```json
{
  "msg_type": "account.orders",
  "id": "msg_8bac8b61ed184c4a95a6e38c319b",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

A new unfilled limit order also arrives as an update. `orderType: "2"` identifies the
limit order, `limit` supplies its price, and `filledQuantity` remains zero. `avgFillPrice` is
absent until a fill price is available:


```json
{
  "msg_type": "account.orders",
  "id": "msg_6921452a0fe04c419c2da5fc04a8",
  "timestamp": "2026-10-26T15:00:01Z",
  "v": 4,
  "payload": [
    {
      "brokerId": 111,
      "orderStatus": "0",
      "filledQuantity": 0,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9002,
      "orderId": "broker-order-43",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456790,
      "orderType": "2",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "1",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      },
      "limit": 189.5
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

A partial fill changes the complete row:


```json
{
  "msg_type": "account.orders",
  "id": "msg_1e45ae9891304e93889781c3f936",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "brokerId": 111,
      "orderStatus": "1",
      "filledQuantity": 40,
      "avgFillPrice": 190.25,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9001,
      "orderId": "broker-order-42",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456789,
      "orderType": "1",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "0",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

A final fill also arrives as an order update. It leaves the working set:


```json
{
  "msg_type": "account.orders",
  "id": "msg_9a1b9213098a4cd7a2101f7efb40",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "brokerId": 111,
      "orderStatus": "2",
      "filledQuantity": 100,
      "avgFillPrice": 190.25,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9001,
      "orderId": "broker-order-42",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456789,
      "orderType": "1",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "0",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

Other transitions out of the working set include `U` and canceled status `4`:


```json
{
  "msg_type": "account.orders",
  "id": "msg_c8a2f2551fdd49cc9c45d8a231a0",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "brokerId": 111,
      "orderStatus": "U",
      "filledQuantity": 40,
      "avgFillPrice": 190.25,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9001,
      "orderId": "broker-order-42",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456789,
      "orderType": "1",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "0",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    },
    {
      "brokerId": 111,
      "orderStatus": "4",
      "filledQuantity": 0,
      "postedDate": "2026-10-26T15:00:00Z",
      "signalType": 1,
      "id": 9002,
      "orderId": "broker-order-43",
      "strategyId": 12345678,
      "strategyName": "Example strategy",
      "signalId": 456789,
      "orderType": "1",
      "side": "1",
      "openClose": "O",
      "orderQuantity": 100,
      "tif": "0",
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

Use the order's `id` as its identity within the
account/broker pair. `orderId` is the broker order ID and is not the row merge key. Retain only
working statuses `V`, `R`, `0`, `1`, `6`, `A` and `E`; remove any row whose new status is outside
that set. A terminal update for an unknown local row can be ignored. Replace a changed row
completely, including clearing previously present fields that are now absent.

The channel reports the latest observed order state. Intermediate changes may not appear,
so do not treat the messages as a complete order-event history.

## Account Positions

`account.positions` uses [the WebSocket position row schema](/apis/general/swagger/schemas/websocketpositiondto)
and [its envelope](/apis/general/swagger/schemas/accountpositionsmessage). Use it to maintain the
positions for one account across its strategies. The default refresh interval is one second.
The initial array replaces your account position collection. Later arrays contain changed
complete rows, including zero-quantity rows for positions to remove.

### Position Fields

| Payload field | Type | Description |
|  --- | --- | --- |
| `strategyId` | integer | Strategy associated with the position. Part of the position identity |
| `strategyName` | string | Strategy display name, when available |
| `quantity` | number | Signed net quantity. Positive means long, negative means short, and zero removes the position |
| `avgPx` | number | Position average price |
| `currency` | string | Three-character instrument currency code |
| `openedDate` | string | UTC date and time when the position opened |
| `c2Symbol` | object | C2 instrument identifier and available descriptive fields |
| `exchangeSymbol` | object | Broker instrument fields used to identify the position, as described below |


Subscribe with the session person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_7119dc28c14642f0bbe46cd48e65",
  "request_id": "positions-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.positions",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Subscribe with an explicit authorized person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_3cb23e3d76ab4feaac8751cfa13c",
  "request_id": "positions-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.positions",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

The successful acknowledgement echoes the resolved identity:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_c3e5626ac4564d4fa3b83421b85b",
  "request_id": "positions-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.positions",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

Unsubscribe using the same channel, account and broker:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_d18b4a9ab5054503aa9d07785ac4",
  "request_id": "positions-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.positions",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Successful unsubscribe acknowledgement:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_19ca1e53f89a482a80599c1d3c4f",
  "request_id": "positions-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.positions",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

An unauthorized subscription returns a correlated error:


```json
{
  "msg_type": "error",
  "id": "msg_8cef49b5d0d74ac6ad1b2e6c8c56",
  "request_id": "positions-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "code": "FORBIDDEN",
    "field": "payload.account",
    "message": "Not authorized to subscribe to account"
  }
}
```

Initial position snapshot:


```json
{
  "msg_type": "account.positions",
  "id": "msg_cefe88ce3d854a4585a03db93155",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "strategyName": "Example strategy",
      "currency": "USD",
      "openedDate": "2026-10-26T15:00:00Z",
      "strategyId": 12345678,
      "quantity": 40,
      "avgPx": 190.25,
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

An account with no positions receives:


```json
{
  "msg_type": "account.positions",
  "id": "msg_af97f48034664cff809ba8aedf95",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

A position change replaces the complete row:


```json
{
  "msg_type": "account.positions",
  "id": "msg_2e0ac3a03dd3412da7f52549a35e",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "strategyName": "Example strategy",
      "currency": "USD",
      "openedDate": "2026-10-26T15:00:00Z",
      "strategyId": 12345678,
      "quantity": 100,
      "avgPx": 190.25,
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

When a position closes, the server sends its prior identity with `quantity: 0`.
Remove it from local state:


```json
{
  "msg_type": "account.positions",
  "id": "msg_8d8c0928f05d4360a86bbadbf5cf",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "strategyName": "Example strategy",
      "currency": "USD",
      "openedDate": "2026-10-26T15:00:00Z",
      "strategyId": 12345678,
      "quantity": 0,
      "avgPx": 190.25,
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "underlying": "AAPL",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ],
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

For the same connection/account subscribed to both orders and positions, observed partial and
final fill order updates precede the resulting live position update. Initial snapshots are
baselines and do not replay past fills. A positions-only subscriber does not need an orders
subscription. Updates can omit intermediate states; there is no cross-socket ordering guarantee.

## Account Balance

`account.balance` uses [AccountBalanceDTO](/apis/general/swagger/schemas/accountbalancedto)
and [its envelope](/apis/general/swagger/schemas/accountbalancemessage). Use it to monitor
broker-reported account balances and margin values. The default refresh interval is 60 seconds.
The initial snapshot and each later update contain the complete selected balance object.

### Balance Fields

| Payload field | Type | Description |
|  --- | --- | --- |
| `currency` | string | Account currency |
| `cash` | number or null | Broker-reported cash balance |
| `netLiquidationValue` | number or null | Broker-reported net liquidation value |
| `buyingPower` | number or null | Broker-reported buying power |
| `initialMargin` | number or null | Broker-reported initial margin |
| `maintenanceMargin` | number or null | Broker-reported maintenance margin |
| `forexMargin` | number or null | Broker-reported Forex margin |
| `lastUpdatedBrokerCashValue` | string | Date and time of the broker cash update, when available |


Subscribe with the session person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_21827e2b07284e16a822f2aa19a0",
  "request_id": "balance-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.balance",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Subscribe with an explicit authorized person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_ad36638076cd4063af683e4633b4",
  "request_id": "balance-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.balance",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

The successful acknowledgement echoes the resolved identity:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_8b2546aca4d8439c8ed1066c3907",
  "request_id": "balance-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.balance",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

Unsubscribe using the same channel, account and broker:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_7ab4f825a8d7402c80bc37b8f6e5",
  "request_id": "balance-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.balance",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Successful unsubscribe acknowledgement:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_e91369effdc445848116c0d71d3b",
  "request_id": "balance-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.balance",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

An unauthorized subscription returns a correlated error:


```json
{
  "msg_type": "error",
  "id": "msg_6b60e56b069647cc9125336ad45b",
  "request_id": "balance-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "code": "FORBIDDEN",
    "field": "payload.account",
    "message": "Not authorized to subscribe to account"
  }
}
```

Initial selected balance object:


```json
{
  "msg_type": "account.balance",
  "id": "msg_f88e0f0fccfa46688909808dae31",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "currency": "USD",
    "cash": 90000,
    "netLiquidationValue": 100000,
    "buyingPower": 200000,
    "initialMargin": 10000,
    "maintenanceMargin": 8000,
    "forexMargin": null,
    "lastUpdatedBrokerCashValue": "2026-10-26T15:00:00Z"
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

A change sends the complete selected object, including unchanged selected fields:


```json
{
  "msg_type": "account.balance",
  "id": "msg_7ce4801c246f49e694de8d316720",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "currency": "USD",
    "cash": 90500,
    "netLiquidationValue": 100000,
    "buyingPower": 200000,
    "initialMargin": 10000,
    "maintenanceMargin": 8000,
    "forexMargin": null,
    "lastUpdatedBrokerCashValue": "2026-10-26T15:00:00Z"
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

An account with unavailable broker values still includes the numeric properties:


```json
{
  "msg_type": "account.balance",
  "id": "msg_8f561569a0284b79b2c4ba7c6959",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "currency": "USD",
    "cash": 100000,
    "netLiquidationValue": null,
    "buyingPower": null,
    "initialMargin": null,
    "maintenanceMargin": null,
    "forexMargin": null,
    "lastUpdatedBrokerCashValue": "2026-10-25T15:00:00Z"
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

`lastUpdatedBrokerCashValue` describes the broker cash update. It can be older than the
envelope timestamp, which describes the WebSocket message.

Replace the whole object on every balance data message. A new `null` clears a previously known
amount; do not keep the old number. Unchanged balance values do not produce messages.

## Account State

`account.state` uses [AccountStateDTO](/apis/general/swagger/schemas/accountstatedto),
[AccountStateStrategyDTO](/apis/general/swagger/schemas/accountstatestrategydto) and
[its envelope](/apis/general/swagger/schemas/accountstatemessage). Use it to monitor account
enablement, broker authorization and the Autotrade settings of allocated strategies. The
default refresh interval is 60 seconds. Each snapshot or update contains the complete state
object and its current `strategies` collection.

### Account State Fields

| Payload field | Type | Description |
|  --- | --- | --- |
| `personId` | integer | Account owner's person ID |
| `account`, `brokerId` | string, integer | Account identity, also present in the message envelope |
| `connection` | string | Broker API client platform name |
| `siteDomain` | string | WhiteLabel domain associated with the account, when available |
| `firstBrokerActivationDate` | string | Date and time of the first broker activation, when available |
| `brokerAuthorizationStatus` | string | `Approved`, `Pending` or `Declined`. Omitted when unknown |
| `isSyncEnabled` | boolean | Whether AutoSync is enabled |
| `syncDisabledReason` | string | Reason AutoSync is disabled, when available |
| `isBrokerEnabled` | boolean | Broker connection enabled flag. Can be false during disconnection or broker maintenance |
| `isAutotradeDisabled` | boolean | Whether Autotrading is disabled for the account |
| `isAutotradeDisabledReason` | string | Available reason for disabled Autotrading or broker connection |
| `isConfirmationNeeded` | boolean | Whether each signal requires manual confirmation |
| `strategies` | array | Current selected settings for allocated strategies. An empty array clears the collection |


### Strategy Settings

Each entry in `strategies` describes one allocation. Optional limits can be absent, and
disabled asset classes omit their associated limits.

| Strategy field | Type | Description |
|  --- | --- | --- |
| `strategyId`, `strategyName` | integer, string | Strategy identity and display name |
| `isTradeLeader` | boolean | Whether this is a BrokerTransmit setup |
| `scalingPercentage` | number | Autotrade scaling percentage |
| `isStrategySetupApproved` | boolean | Approval of this strategy setup, independently of whether trading is enabled |
| `closingOrdersOnly` | boolean | Whether only closing orders are allowed |
| `settingsLastUpdateUTC` | string | UTC date and time of the last strategy settings update |
| `enabledFutures` | boolean | Whether futures are enabled for Autotrade |
| `minFutures`, `maxFutures` | integer | Available minimum and maximum scaled futures quantities |
| `enabledForex` | boolean | Whether Forex is enabled for Autotrade |
| `forexLotSize` | integer | Available Forex lot size |
| `minForex`, `maxForex` | integer | Available minimum and maximum scaled Forex quantities |
| `enabledStocks` | boolean | Whether stocks are enabled for Autotrade |
| `maxStocksDollars` | integer | Available maximum currency value of scaled stock orders |
| `enabledOptions` | boolean | Whether options are enabled for Autotrade |
| `minOptions`, `maxOptions` | integer | Available minimum and maximum scaled option quantities |


Subscribe with the session person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_934fe7721ea44da880ced3cddb92",
  "request_id": "state-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.state",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Subscribe with an explicit authorized person:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_c04d9cffe07244c58c076a5afbae",
  "request_id": "state-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.state",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

The successful acknowledgement echoes the resolved identity:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_124125586384407ebba0c51d47b3",
  "request_id": "state-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "subscribe",
    "channel": "account.state",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

Unsubscribe using the same channel, account and broker:


```json
{
  "msg_type": "subscription.request",
  "id": "msg_da9e7bb19b8341d190de5718b7ca",
  "request_id": "state-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.state",
    "account": "DU123456",
    "broker_id": 111
  }
}
```

Successful unsubscribe acknowledgement:


```json
{
  "msg_type": "subscription.ack",
  "id": "msg_97ad3d68a9e249f4831fb7ab4793",
  "request_id": "state-unsub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "action": "unsubscribe",
    "channel": "account.state",
    "account": "DU123456",
    "broker_id": 111,
    "person_id": 123456
  }
}
```

An unauthorized subscription returns a correlated error:


```json
{
  "msg_type": "error",
  "id": "msg_62067e9ee424467c8285b1feeb2e",
  "request_id": "state-sub",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "code": "FORBIDDEN",
    "field": "payload.account",
    "message": "Not authorized to subscribe to account"
  }
}
```

Initial selected state with no allocated strategies:


```json
{
  "msg_type": "account.state",
  "id": "msg_44b30445b0b344bba18af0192823",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "personId": 123456,
    "account": "DU123456",
    "brokerId": 111,
    "connection": "Example broker API",
    "siteDomain": "example.test",
    "firstBrokerActivationDate": "2026-10-25T15:00:00Z",
    "brokerAuthorizationStatus": "Approved",
    "isSyncEnabled": true,
    "isBrokerEnabled": true,
    "isAutotradeDisabled": false,
    "isConfirmationNeeded": false,
    "strategies": []
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": true
}
```

Adding or changing an allocated strategy sends the complete selected state:


```json
{
  "msg_type": "account.state",
  "id": "msg_64f239ba8bf64875b8728de2ac09",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "personId": 123456,
    "account": "DU123456",
    "brokerId": 111,
    "connection": "Example broker API",
    "siteDomain": "example.test",
    "firstBrokerActivationDate": "2026-10-25T15:00:00Z",
    "brokerAuthorizationStatus": "Approved",
    "isSyncEnabled": true,
    "isBrokerEnabled": true,
    "isAutotradeDisabled": false,
    "isConfirmationNeeded": false,
    "strategies": [
      {
        "strategyId": 12345678,
        "strategyName": "Example strategy",
        "isTradeLeader": false,
        "scalingPercentage": 100,
        "isStrategySetupApproved": true,
        "closingOrdersOnly": false,
        "settingsLastUpdateUTC": "2026-10-26T15:00:00Z",
        "enabledFutures": true,
        "minFutures": 1,
        "maxFutures": 5,
        "enabledForex": true,
        "forexLotSize": 1000,
        "minForex": 1000,
        "maxForex": 100000,
        "enabledStocks": true,
        "maxStocksDollars": 50000,
        "enabledOptions": true,
        "minOptions": 1,
        "maxOptions": 10
      }
    ]
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

Removing the final strategy sends the complete object with an empty `strategies` collection:


```json
{
  "msg_type": "account.state",
  "id": "msg_6ae0f485878a4a6d9c9cd44de7c5",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "personId": 123456,
    "account": "DU123456",
    "brokerId": 111,
    "connection": "Example broker API",
    "siteDomain": "example.test",
    "firstBrokerActivationDate": "2026-10-25T15:00:00Z",
    "brokerAuthorizationStatus": "Approved",
    "isSyncEnabled": true,
    "isBrokerEnabled": true,
    "isAutotradeDisabled": false,
    "isConfirmationNeeded": false,
    "strategies": []
  },
  "account": "DU123456",
  "broker_id": 111,
  "is_snapshot": false
}
```

`isStrategySetupApproved` is the approval of an individual strategy setup. It does not mean
trading is currently enabled, and it is separate from account `brokerAuthorizationStatus`:

| Broker authorization | Meaning |
|  --- | --- |
| `Approved` | Broker account authorization is approved |
| `Declined` | Broker account authorization is declined |
| `Pending` | Broker account authorization is pending |
| Absent | Authorization status is unavailable |


Replace the full state object, including its complete `strategies` collection, on every
state data message. Removed strategies and newly absent optional properties must disappear
from client state. Balance values belong
to `account.balance` and are not duplicated here.

## Consuming Account Snapshots and Updates

For each account channel, `is_snapshot: true` replaces local state for that account/broker pair.
On `false`, order and position arrays contain complete row replacements by identity; they are
not JSON field patches. Balance and state updates replace their complete objects.

The following consumer preserves account/broker/strategy scoping. Clear these caches on reconnect or mark them stale
until replacement snapshots arrive. Stop using a channel's cached state after server-initiated
unsubscribe. Invoke this function from the persistent receive loop in [Connecting](/guides/websocket-connect).


```python
WORKING_ORDER_STATUSES = frozenset({"V", "R", "0", "1", "6", "A", "E"})
account_orders = {}
account_positions = {}
account_balances = {}
account_states = {}

def position_key(message, row):
    symbol = row["exchangeSymbol"]
    fields = ("securityType", "symbol", "currency", "maturityMonthYear",
              "putOrCall", "strikePrice")
    # Treat missing optional symbol fields consistently when identifying a position.
    exchange_key = tuple("" if symbol.get(field) is None else symbol[field]
                         for field in fields)
    return (message["account"], message["broker_id"], row["strategyId"], exchange_key)

def consume_account(message):
    channel = message["msg_type"]
    account = (message["account"], message["broker_id"])
    snapshot = message["is_snapshot"]
    payload = message["payload"]
    if channel == "account.orders":
        rows = {} if snapshot else account_orders[account]
        for row in payload:
            if row["orderStatus"] in WORKING_ORDER_STATUSES:
                rows[row["id"]] = dict(row)  # Whole row, so absent fields clear old values.
            else:
                rows.pop(row["id"], None)
        account_orders[account] = rows
    elif channel == "account.positions":
        rows = {} if snapshot else account_positions[account]
        for row in payload:
            identity = position_key(message, row)
            if row["quantity"] == 0:
                rows.pop(identity, None)
            else:
                rows[identity] = dict(row)
        account_positions[account] = rows
    elif channel == "account.balance":
        account_balances[account] = dict(payload)
    elif channel == "account.state":
        account_states[account] = dict(payload)
```

## Strategy Channels

Strategy channels require `strategy_id`. Only `strategy.signal` and `strategy.live_fills`
have replay cursors. See [Recovery](/guides/websocket-gap-fill) for cursor scope and replay limits.

### Strategy Signals

`strategy.signal` carries one [WebSocket order/signal row](/apis/general/swagger/schemas/websocketorderstatusdto):


```json
{
  "msg_type": "strategy.signal",
  "id": "msg_46943cae203b4635a8f218380f8f",
  "seq_id": 98765,
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "orderStatus": "0",
    "postedDate": "2026-10-26T15:00:00Z",
    "signalType": 1,
    "id": 9001,
    "strategyId": 12345678,
    "signalId": 456789,
    "orderType": "1",
    "side": "1",
    "openClose": "O",
    "orderQuantity": 100,
    "tif": "0",
    "c2Symbol": {
      "fullSymbol": "AAPL",
      "symbolType": "stock",
      "description": "Apple Inc."
    },
    "exchangeSymbol": {
      "symbol": "AAPL",
      "currency": "USD",
      "securityExchange": "DEFAULT",
      "securityType": "CS",
      "priceMultiplier": 1
    }
  }
}
```

Payload fields use camelCase. A working signal's unavailable `filledQuantity` is omitted.
`orderStatus` values include `0` (working), `2` (filled) and `4` (canceled). Cancel signals
carry identity/status fields; unavailable order-detail fields are omitted. Fill signals
include their filled quantity. `tif` uses the existing wire values such as `0` for Day.
Store the last successfully processed `seq_id` for each strategy signal stream.

### Strategy Positions

`strategy.positions` carries an array of [WebSocket position rows](/apis/general/swagger/schemas/websocketpositiondto).
Every message replaces the entire strategy position collection, including the initial snapshot:


```json
{
  "msg_type": "strategy.positions",
  "id": "msg_dae6df6ef4e34dcda0ac37a0da0e",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": [
    {
      "strategyName": "Example strategy",
      "currency": "USD",
      "openedDate": "2026-10-26T15:00:00Z",
      "strategyId": 12345678,
      "quantity": 100,
      "avgPx": 190.25,
      "c2Symbol": {
        "fullSymbol": "AAPL",
        "symbolType": "stock",
        "description": "Apple Inc."
      },
      "exchangeSymbol": {
        "symbol": "AAPL",
        "currency": "USD",
        "securityExchange": "DEFAULT",
        "securityType": "CS",
        "priceMultiplier": 1
      }
    }
  ]
}
```

When no positions remain, replace the collection with the empty array:


```json
{
  "msg_type": "strategy.positions",
  "id": "msg_36eddb8b56024a4c8e39eddb4075",
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": []
}
```

This channel has no `seq_id` replay. Its full-array replacement semantics differ from
`account.positions`, whose later arrays contain changed rows and zero-quantity closures.

### Live Autotrade Fills

Completed fills arrive as `strategy.live_fills`, using [StrategyFillDTO](/apis/general/swagger/schemas/strategyfilldto):


```json
{
  "msg_type": "strategy.live_fills",
  "id": "msg_d60126a689bc49ba9c0dce7e3e4d",
  "seq_id": 87654,
  "timestamp": "2026-10-26T15:00:00Z",
  "v": 4,
  "payload": {
    "id": 87654,
    "strategy_id": 12345678,
    "signal_id": 456789,
    "account": "v1_example_opaque_account_token",
    "account_type": "Live",
    "quantity": 100,
    "avg_price": 190.25,
    "utc_date_time": "2026-10-26T15:00:00Z"
  }
}
```

Partial fills are excluded from this strategy fill stream. Its `seq_id` is independent of
the signal cursor. The payload `account` is an opaque token, unlike the exact broker account
name in account-channel envelopes. Keep the entire token; its prefix identifies the token format.

## Sending Current Timestamps

To print a current subscription request for an interactive `websocat` session:


```bash
python3 - <<'PY_REQUEST'
import json
from datetime import datetime, timezone
print(json.dumps({
    "msg_type": "subscription.request",
    "request_id": "signal-sub-1",
    "timestamp": datetime.now(timezone.utc).isoformat(),
    "payload": {"action": "subscribe", "channel": "strategy.signal", "strategy_id": 12345678}
}))
PY_REQUEST
```

Paste the printed JSON into the connected terminal and continue acknowledging heartbeats.
For an unattended client, use the [complete receive-loop example](/guides/websocket-connect).
Duplicate subscriptions return `ALREADY_EXISTS`; an unknown unsubscribe returns `NOT_FOUND`.
See [Errors](/guides/websocket-errors) for validation, revocation and retries.