Trade Routing Information

Trade routing information is available via:

      TradeSubscription

      Submessages of InformationRequest

Information Requests

InformationRequest allows requesting the following trade routing-related data:

      List of authorized accounts: InformationRequest.accounts_request, see AccountsRequest. Example here.

      List of FCM currency rates: InformationRequest.currency_rates_request or InformationRequest.currency_rate_sources_request, see CurrencyRatesRequest or CurrencyRateSourcesRequest.

      List of accounts balances: InformationRequest.last_statement_balances_request, see LastStatementBalancesRequest.

      Orders history: InformationRequest.historical_orders_request, see HistoricalOrdersRequest.

      Entitlements for order placement: Know what is allowed before sending an order and getting rejected: InformationRequest.order_entitlement_request, see OrderEntitlementRequest.

      Get order status by order ID: Retrieve a list of order transactions if the client does not request them in trade subscription: InformationRequest.order_status_request, see OrderStatusRequest.

      Account risk parameters: For stricter calculations on the client side (only some parameters are available): InformationRequest.account_risk_parameters_request, see AccountRiskParametersRequest.

To subscribe, set InformationRequest.subscribe = true.

When subscribed:

      The client first receives an initial snapshot (complete dataset)

      Subsequent updates are sent as changes occur, either:

Incremental (delta changes), or

Full refreshes (depending on request type)

Note: Some requests don’t support subscriptions — these only return a one-time snapshot.

Accounts Request

AccountsRequest and AccountsReport provide an account information tree:

      First level - Brokerage

      Second level - SalesSeries (a set of accounts combined by brokerage based on some rule)

      Third level - Account

Example here.

Account Status Considerations:

      If Account.is_unauthorized = true is received, the account data is not going to be updated after this.

      If Account.account_connection_status is not empty, the status should be ACCOUNT_CONNECTION_STATUS_CONNECTED (4) to get trade routing information for the account. AccountLogonRoutineClient must be used for changing connection status.

      If Account.cleared_by_statements = false, positions, balances, and account summaries are not provided.

      If Account.supports_exchange_balances_subscription = true, trade subscription on exchange balances will return data.

      If Account.supports_exchange_positions_subscription = true, trade subscription on exchange positions will return data.

Orders History

HistoricalOrdersRequest allows retrieving orders from previous days (unlike TradeSubscription, which returns current day orders) up to 30 days ago.

Note: It does not support subscription.

  // Only orders from specified business date (inclusively) are to be returned
  // (date only value in time format).
  // Working and parked orders of the current day are returned despite the
  // from_date, to_date values.
  // If order has not been cleared for several days, it will be present in
  // a report if one of those days hits the requested range. E.g., order was
  // filled on Friday, statement happened on Monday, if request is done for
  // a Saturday, order will be present in the report.

Trade Subscriptions

TradeSubscription client request to get information about orders, positions, account summaries and exchange positions/balances (if supported).

TradeSubscription has a required field subscribe, so only requests with updates are possible.

Message flow

Note: Here and further, data statuses are one of the following: OrderStatus, PositionStatus, AccountSummaryStatus, ExchangeBalanceStatus, ExchangePositionStatus.

Client sends TradeSubscription with subscribe=true, with one or more subscription_scopes.

Server responses:

1.  TradeSubscriptionStatus with status_code = 0 (STATUS_CODE_SUCCESS) to confirm the subscription or with status_code >= 100 if the subscription has issues.

     Note: Additional TradeSubscriptionStatus(es) can be sent in any time indicating data problem or resolving the problem (STATUS_CODE_DISCONNECTED and then STATUS_CODE_SUCCESS) or subscription termination (status_code >= 100).

2.  Zero or more data statuses with is_snapshot=true, depending on TradeSubscription.subscription_scopes and trader state (e.g., account may have no current day orders, so zero OrderStatuses are sent).

3.  After all initial account snapshots are sent, the server sends TradeSnapshotCompletion for each subscription scope to notify that the client has the complete state.

4.  After that, the server sends data statuses if trade routing information changes.

Example without data statuses here.

Client applications should be ready to receive data statuses with is_snapshot=false even before TradeSnapshotCompletion for accounts which already received snapshot. Similar, data statuses with is_snapshot=true can be received after TradeSnapshotCompletion for some subscription scopes, see protocol comments for details. Messages can be aggregated in one ServerMsg, so clients may need to process fields of ServerMsg in a specific order.

The same data can be requested in different subscriptions (with different subscription IDs). Data statuses in this case can be combined into one, meaning data is sent only once per connection. Due to aggregation, excessive data may be sent in one subscription but is required for another. It is the client’s responsibility to distribute status correctly.

To unsubscribe, send TradeSubscription with subscription_id and subscribe=false. The server responds with TradeSubscriptionStatus with status_code = 0 (STATUS_CODE_SUCCESS) to confirm the drop.

Publication types

Subscription can be done for different subsets of accounts. TradeSubscription.publication_type allows control over this. If omitted, the subscription is applied to all authorized accounts. Based on the field value, related parameters must be set by currently available account information:

      PUBLICATION_TYPE_ACCOUNTS: TradeSubscription.account_ids

      PUBLICATION_TYPE_SALES_SERIES: TradeSubscription.brokerage_id and TradeSubscription.sales_series_number

      PUBLICATION_TYPE_BROKERAGE: TradeSubscription.brokerage_id

Note: If an account is authorized for a client, there is no need to resubscribe if the publication type includes it. For example, if a subscription has a publication type of PUBLICATION_TYPE_BROKERAGE and the client is authorized for an account from this brokerage, a snapshot (not necessarily with is_snapshot = true) and updates for the account are sent in the subscription.

Contract Metadata in Status

Data status may include a previously unknown contract_id. To retrieve complete contract metadata and subscribe to updates, use ContractMetadataRequest.

Both OrderStatus and PositionStatus contain a contract_metadata field. This field is populated at least the first time either of these statuses is sent for a given contract_id.

For more details, see the comments on OrderStatus.contract_metadata or PositionStatus.contract_metadata.

Scope: Orders

To subscribe to orders, add 1 (SUBSCRIPTION_SCOPE_ORDERS) to TradeSubscription.subscription_scopes. Response: OrderStatus.

Use OrderStatus.chain_order_id + OrderStatus.account_id as a key for updates. You can use OrderStatus.status_utc_timestamp to reorder order status modifications. If OrderStatus.status_utc_timestamp are equal for the order status, use TransactionStatus.trans_id as next sorting criteria. The greater TransactionStatus.trans_id, the later it happened. If TransactionStatus.trans_id are equal for the order, any can be used.

For (partial) fills, transactions contain one or more Trade records, keyed by Trade.trade_id.

Note: WebAPI server may send duplicates, it is client responsibility to discover them and form correct order book.

To receive updates only, set TradeSubscription.skip_orders_snapshot=True. To filter snapshot data, use TradeSubscription.order_snapshot_filter.

The WebAPI server sends OrderStatus with an initialized last_statement_date when orders can be removed.

Important: Clients must subscribe to the orders scope if they plan to send OrderRequest, as subscription is required to receive the request result.

Example

{
  "order_statuses": [
    {
      "subscription_ids": [
        5
      ],
      "is_snapshot": false,
      "status": 1,
      "order_id": "1809769617",
      "chain_order_id": "1809769617",
      "status_utc_time": 227988204,
      "submission_utc_time": 227988204,
      "fill_cnt": 0,
      "scaled_avg_fill_price": 0,
      "order": {
        "account_id": 123456,
        "when_utc_time": 227788000,
        "contract_id": 1,
        "cl_order_id": "client unique order id",
        "order_type": 1,
        "duration": 1,
        "side": 1,
        "is_manual": true,
        "is_user_attribute_checked": false,
        "when_utc_timestamp": {
          "seconds": 1739914600
        },
        "qty": {
          "significand": 1
        },
        "execution_source_code": "Y",
        "is_care_order": false
      },
      "transaction_statuses": [
        {
          "status": 1,
          "trans_id": 394602971392,
          "trans_utc_time": 227988204,
          "cl_order_id": "client unique order id",
          "trans_utc_timestamp": {
            "seconds": 1739914800,
            "nanos": 204300000
          },
          "mifid_execution_decision": "NORE",
          "execution_source_code": "Y",
          "is_automated": false,
          "mifid_execution_decision_is_algo": false,
          "username": "test user"
        }
      ],
      "entered_by_user": "test user",
      "first_statement_date": 149988000,
      "account_id": 123456,
      "status_utc_timestamp": {
        "seconds": 1739914800,
        "nanos": 204300000
      },
      "submission_utc_timestamp": {
        "seconds": 1739914800,
        "nanos": 204268000
      },
      "avg_fill_price_correct": 0,
      "fill_qty": {
        "significand": 0
      },
      "remaining_qty": {
        "significand": 1
      },
      "mifid_execution_decision": "NORE",
      "mifid_execution_decision_is_algo": false,
      "operator_id": "4567"
    }
  ]
}

Scope: Positions

To subscribe to positions, add 2 (SUBSCRIPTION_SCOPE_POSITIONS) to TradeSubscription.subscription_scopes. Response: PositionStatus. PositionStatus is grouped by account/contract pair.

Default matching algorithm: FIFO (intraday first). Additional parameters for contracts with POSITION_TRACKING_TYPE_NET_POSITION tracking type:

      matching_algorithm

      match_intraday_first

      historical_matching_algorithm

Supported algorithms: FIFO, LIFO, HBHS (High Buy, High Sell).

For non-POSITION_TRACKING_TYPE_NET_POSITION contracts, both BUY and SELL positions can exist simultaneously. For POSITION_TRACKING_TYPE_NET_POSITION, positions create purchase and sales group in such case.

Remove open positions when OpenPosition.qty=0. Use OpenPosition.id for matching. Remove purchase and sales groups when PurchaseAndSalesGroup has no MatchedTrade, or all MatchedTrade.qty=0. Use PurchaseAndSalesGroup.id for matching.

Example

{
  "position_statuses": [
    {
      "subscription_ids": [
        0
      ],
      "account_id": 123456,
      "contract_id": 1,
      "is_short_open_position": true,
      "open_positions": [
        {
          "id": 1,
          "price_correct": 0,
          "trade_date": -60930000,
          "statement_date": -60930000,
          "is_aggregated": false,
          "is_short": false,
          "qty": {
            "significand": 0
          }
        }
      ],
      "purchase_and_sales_groups": [
        {
          "id": 1,
          "realized_profit_loss": 6012.5,
          "matched_trades": [
            {
              "is_short": false,
              "price": 5816.5,
              "trade_date": -492930000,
              "statement_date": -320130000,
              "is_aggregated": false,
              "is_yesterday": true,
              "qty": {
                "significand": 1
              }
            },
            {
              "is_short": true,
              "price": 5936.75,
              "trade_date": -60930000,
              "statement_date": 25470000,
              "trade_utc_time": 5330000,
              "is_aggregated": false,
              "trade_utc_timestamp": {
                "seconds": 1741631060
              },
              "is_yesterday": false,
              "qty": {
                "significand": 1
              }
            }
          ]
        }
      ]
    }
  ]
}

Scope: Account Summary

To subscribe to account summary, add 4 (SUBSCRIPTION_SCOPE_ACCOUNT_SUMMARY) to TradeSubscription.subscription_scopes. Response: AccountSummaryStatus.

Each status contains all requested info for one account. Clients must fillTradeSubscription.account_summary_parameters to specify requested fields. If multiple subscriptions request different fields, a union of requested fields is returned.

Account summary supports per-account completion. When AccountSummaryStatus.is_snapshot=true for an account, updates for that account will be received even before TradeSnapshotCompletion.

Remove AccountSummaryStatus for an account if AccountSummaryStatus.deleted=true is received.

See AccountSummaryStatus for more details.

Example

{
  "account_summary_statuses": [
    {
      "subscription_ids": [
        0
      ],
      "is_snapshot": true,
      "account_id": 123456,
      "currency": "USD",
      "current_balance": 150798.1069,
      "profit_loss": 6012.5
    }
  ]
}

Scope: Exchange Positions

To subscribe to exchange positions, add 5 (SUBSCRIPTION_SCOPE_EXCHANGE_POSITIONS) to TradeSubscription.subscription_scopes. Response: ExchangePositionStatus.

Account must have Account.supports_exchange_positions_subscription=true to receive this data.

Important: Snapshots can be received at any time, not just as the first message. Use ExchangePositionStatus.is_complete to determine if the full snapshot/update is received.

Example

{
  "exchange_position_statuses": [
    {
      "subscription_ids": 1,
      "is_snapshot": true,
      "is_complete": true,
      "account_id": 1,
      "positions": [
        {
          "is_snapshot": true,
          "contract_id": 1,
          "realized_profit_loss": 5,
          "open_positions": [
            {
              "id": "op1",
              "is_short": false,
              "qty": {
                "significand": 10
              },
              "price": 7,
              "extra_attributes": {
                "name": "op name1",
                "display_name": "op dname1",
                "value": "op value1"
              }
            }
          ],
          "extra_attributes": {
            "name": "name1",
            "display_name": "dname1",
            "value": "value1"
          }
        },
        {
          "is_snapshot": true,
          "contract_id": 2,
          "realized_profit_loss": 100
        }
      ]
    }
  ]
}

Scope: Exchange Balances

To subscribe to exchange balances, add 6 (SUBSCRIPTION_SCOPE_EXCHANGE_BALANCES) to TradeSubscription.subscription_scopes. Response: ExchangeBalanceStatus.

Account must have Account.supports_exchange_balances_subscription=true to receive this data.

Important: Snapshots can be received at any time. See ExchangeBalanceStatus for more details.

Example

{
  "exchange_balance_statuses": {
    "subscription_ids": 1,
    "is_snapshot": true,
    "account_id": 1,
    "balances": [
      {
        "currency": "BTC",
        "cash_balance": 1000,
        "extra_attributes": {
          "name": "extra_attribute",
          "display_name": "name",
          "value": "value"
        }
      }
    ]
  }
}

Positions and Balances vs. Exchange Positions and Exchange Balances

There are two ways to get positions and balances:

1.  For accounts that are cleared by the Back Office statement (Account.cleared_by_statements = true):

Positions

LastStatementBalancesRequest

2.  For accounts that support the ‘exchange’ variant, which is designed as a streaming one and currently transfers data directly from the exchange (Account.supports_exchange_balances_subscription = true, Account.supports_exchange_positions_subscription = true):

Exchange positions

Exchange balances

Last statement balances are updated only after clearing or an explicit statement (e.g., when the broker checks all account trading activity for the day), while exchange balances are updated as often as the exchange does.

Similarly, for positions: realized profit and loss is applied to the balance (trading_account_2.Balance.balance) only after clearing by the Back Office (which is usually once per day) for regular positions, while exchange positions can be ‘cleared’ as often as the exchange does, ultimately after each trade.