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 does. Client id values are ignored; clients may omit them.
| Payload field | Type | Requirement |
|---|---|---|
action | string | subscribe or unsubscribe |
channel | string | One of the channels in the 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 uses the WebSocket order row schema and its envelope. 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.
| 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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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 uses the WebSocket position row schema and its envelope. 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.
| 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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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 uses AccountBalanceDTO and its envelope. 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.
| 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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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 uses AccountStateDTO, AccountStateStrategyDTO and its envelope. 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.
| 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 |
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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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.
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.
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 require strategy_id. Only strategy.signal and strategy.live_fills have replay cursors. See Recovery for cursor scope and replay limits.
strategy.signal carries one WebSocket order/signal row:
{
"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 carries an array of WebSocket position rows. Every message replaces the entire strategy position collection, including the initial snapshot:
{
"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:
{
"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.
Completed fills arrive as strategy.live_fills, using StrategyFillDTO:
{
"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.
To print a current subscription request for an interactive websocat session:
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_REQUESTPaste the printed JSON into the connected terminal and continue acknowledging heartbeats. For an unattended client, use the complete receive-loop example. Duplicate subscriptions return ALREADY_EXISTS; an unknown unsubscribe returns NOT_FOUND. See Errors for validation, revocation and retries.