Skip to content
Last updated

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 does. Client id values are ignored; clients may omit them.

Subscription Fields

Payload fieldTypeRequirement
actionstringsubscribe or unsubscribe
channelstringOne of the channels in the overview
strategy_idintegerRequired for strategy channels
accountstringRequired for all account channels. Exact account name, at most 45 characters
broker_idintegerRequired and positive for all account channels
person_idintegerOptional account authorization person. Omitted means the session person; an explicit value must be positive and authorized
resume_fromintegerOptional 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 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.

Order Fields

Payload fieldTypeDescription
idintegerStable account order-row ID. Use this field to replace a row in local state
orderIdstringBroker order ID
brokerIdintegerBroker identifier, also present as broker_id in the envelope
strategyIdintegerStrategy associated with the account order
strategyNamestringStrategy display name, when available
signalIdintegerAssociated C2 signal ID, when available
signalTypeinteger1: strategy signal, 4: manual signal, 5: manual override, 6: automatic stop loss
orderStatusstringCurrent status. Working statuses and removal rules are described below
orderTypestring1: market, 2: limit, 3: stop
sidestring1: buy, 2: sell
openClosestringO: opening order, C: closing order
orderQuantitynumberUnsigned order quantity
filledQuantitynumberCumulative filled quantity, including zero for an unfilled order
avgFillPricenumberAverage fill price, when available. Omitted before a fill
limit, stopnumberApplicable limit or stop price. Omitted when not applicable
tifstring0: day, 1: good till canceled
postedDatestringUTC date and time when the order was posted
parentSignalId, cancelReplaceSignalIdintegerRelated parent or cancel/replace signal, when present
c2Symbol, exchangeSymbolobjectC2 and broker instrument identifiers
infostringAdditional 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

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.

Position Fields

Payload fieldTypeDescription
strategyIdintegerStrategy associated with the position. Part of the position identity
strategyNamestringStrategy display name, when available
quantitynumberSigned net quantity. Positive means long, negative means short, and zero removes the position
avgPxnumberPosition average price
currencystringThree-character instrument currency code
openedDatestringUTC date and time when the position opened
c2SymbolobjectC2 instrument identifier and available descriptive fields
exchangeSymbolobjectBroker 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

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.

Balance Fields

Payload fieldTypeDescription
currencystringAccount currency
cashnumber or nullBroker-reported cash balance
netLiquidationValuenumber or nullBroker-reported net liquidation value
buyingPowernumber or nullBroker-reported buying power
initialMarginnumber or nullBroker-reported initial margin
maintenanceMarginnumber or nullBroker-reported maintenance margin
forexMarginnumber or nullBroker-reported Forex margin
lastUpdatedBrokerCashValuestringDate 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

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.

Account State Fields

Payload fieldTypeDescription
personIdintegerAccount owner's person ID
account, brokerIdstring, integerAccount identity, also present in the message envelope
connectionstringBroker API client platform name
siteDomainstringWhiteLabel domain associated with the account, when available
firstBrokerActivationDatestringDate and time of the first broker activation, when available
brokerAuthorizationStatusstringApproved, Pending or Declined. Omitted when unknown
isSyncEnabledbooleanWhether AutoSync is enabled
syncDisabledReasonstringReason AutoSync is disabled, when available
isBrokerEnabledbooleanBroker connection enabled flag. Can be false during disconnection or broker maintenance
isAutotradeDisabledbooleanWhether Autotrading is disabled for the account
isAutotradeDisabledReasonstringAvailable reason for disabled Autotrading or broker connection
isConfirmationNeededbooleanWhether each signal requires manual confirmation
strategiesarrayCurrent 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 fieldTypeDescription
strategyId, strategyNameinteger, stringStrategy identity and display name
isTradeLeaderbooleanWhether this is a BrokerTransmit setup
scalingPercentagenumberAutotrade scaling percentage
isStrategySetupApprovedbooleanApproval of this strategy setup, independently of whether trading is enabled
closingOrdersOnlybooleanWhether only closing orders are allowed
settingsLastUpdateUTCstringUTC date and time of the last strategy settings update
enabledFuturesbooleanWhether futures are enabled for Autotrade
minFutures, maxFuturesintegerAvailable minimum and maximum scaled futures quantities
enabledForexbooleanWhether Forex is enabled for Autotrade
forexLotSizeintegerAvailable Forex lot size
minForex, maxForexintegerAvailable minimum and maximum scaled Forex quantities
enabledStocksbooleanWhether stocks are enabled for Autotrade
maxStocksDollarsintegerAvailable maximum currency value of scaled stock orders
enabledOptionsbooleanWhether options are enabled for Autotrade
minOptions, maxOptionsintegerAvailable 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 authorizationMeaning
ApprovedBroker account authorization is approved
DeclinedBroker account authorization is declined
PendingBroker account authorization is pending
AbsentAuthorization 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.

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 for cursor scope and replay limits.

Strategy Signals

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

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.

Live Autotrade Fills

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.

Sending Current Timestamps

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_REQUEST

Paste 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.