Skip to main content

Broker adapter

The Broker integration overview defines the production boundary. The owning composition creates the TradingControllerApi, controls the adapter session, and fixes structural operation support when the controller is constructed.

Import broker contracts and controller APIs from @tradescript/pro/sdk. Mount React charts and trading widgets from @tradescript/pro/react.

Adapter versus controller

TradingBrokerAdapterTradingControllerApi
OwnerYour applicationThe chart/composition that creates it, or your host when injected
JobMap backend truth and operationsMaintain normalized state and drive trading surfaces
Passed asbroker={broker} when the receiver should create ittrading={trading} when an existing controller must be shared
MinimumgetState, placeOrderCreated around one valid adapter
TeardownRelease backend resources in disconnectOwner awaits disconnect(), then destroys its chart or composition

One chart or composition may own a controller created from broker. For several charts or standalone widgets in the same broker session, create one composition root and pass its trading controller to every surface. Passing the same broker independently to several chart mounts can create several controllers, adapter subscriptions, and backend sessions.

An injected controller remains owned by the host that created it. A child widget must not disconnect or destroy that shared authority.

Own the lifecycle

connect() hands the adapter a TradingHost when the optional adapter method is implemented. It does not call getState(). The owner must explicitly load one fresh snapshot after every initial connection or reconnect. The path below shows an adapter that implements the optional connect and disconnect session methods.

trading.disconnect() does not remove the controller's callback registered through adapter.subscribe. The adapter's disconnect() must stop its backend session and prevent later pushes. Destroying the owning composition releases the controller's local listeners and adapter subscription definitively.

Always destroy the owner even when adapter disconnection rejects:

teardown.ts
import type { TradingControllerApi } from '@tradescript/pro/sdk';

declare const trading: TradingControllerApi;
declare const mounted: { destroy(): void };

export async function teardownTradingOwner(): Promise<void> {
try {
await trading.disconnect();
} finally {
mounted.destroy();
}
}

For an injected controller, the host that created it performs the equivalent trading.destroy() in its own finally; child widgets only destroy themselves.

Implement stream ordering and the snapshot boundary in State and events.

Verify ownership and teardown:

  • One owning composition creates exactly one controller and backend session.
  • Additional surfaces receive trading, not another copy of broker.
  • connect() followed by getState() produces one complete initial snapshot.
  • disconnect() stops adapter sockets, timers, requests, and server subscriptions.
  • Destroying the owner releases the adapter subscription and controller listeners even when backend disconnection fails.

Add structural capabilities in stages

Only implement operations the backend can support truthfully.

StageAdapter methodsCheckpoint
Connectionconnect, disconnect, getConnectionStatusRemount creates one session; teardown leaves none.
Accounts and ruleslistAccounts, setActiveAccount, isTradable, getTradingSymbolInfoState and UI match the exact account and symbol route.
Order operationspreviewOrder, modifyOrder, cancellation methodsEach adapter resolution has the documented controller effect.
Position operationspreview, modify, close, reverse, and flatten methodsPartial quantities and protection survive unchanged.
Account Managermetadata, table loaders, subscriptions, and actionsEvery declared page and action has backend support.
OptionsresolveOptionContractBroker contract ids and routes remain attached to exact legs.

trading.getOperationSupport() is a synchronous structural manifest. The controller builds it when it is created from adapter method presence and its configured risk request factories; production entitlements filter it when read. Adding methods to the adapter object later does not widen the existing controller.

await trading.getFeatures() is separate asynchronous broker/account metadata. Features may narrow what the UI should offer in the current session, but they do not mutate or widen getOperationSupport().

See Accounts, capabilities, and symbol rules for the complete precedence model.

Production checklist

  • Declare executionEnvironment explicitly; never infer it from an account name or id.
  • Runtime-validate network responses before they become SDK contracts.
  • Preserve exact account, symbol, order, position, execution, and contract ids.
  • Keep authentication, authorization, risk, and final validation on the backend.
  • Treat uncertain transport outcomes as unknown until authoritative state is reconciled.
  • Publish each backend change through exactly one event lane.
  • Log correlation evidence without credentials or sensitive account payloads.

Failure modes

SymptomFirst check
trading.adapter-contract-invalidConfirm getState and placeOrder are functions before controller construction.
Duplicate sessionsConfirm several surfaces were not independently given broker.
Missing actionInspect getOperationSupport(), then the separate runtime/account and symbol gates.
Accepted order never appearsConfirm an authoritative orders or state update followed the acknowledgement.
Duplicate event deliveryConfirm the same backend change did not use both connect(host) and subscribe.
State goes backwardsTest the snapshot/stream boundary during reconnect.

Continue with Test and troubleshoot for executable verification and log inspection.

The full contract is in the Trading API reference.

Next steps

Continue with State and events.