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
TradingBrokerAdapter | TradingControllerApi | |
|---|---|---|
| Owner | Your application | The chart/composition that creates it, or your host when injected |
| Job | Map backend truth and operations | Maintain normalized state and drive trading surfaces |
| Passed as | broker={broker} when the receiver should create it | trading={trading} when an existing controller must be shared |
| Minimum | getState, placeOrder | Created around one valid adapter |
| Teardown | Release backend resources in disconnect | Owner 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:
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 ofbroker. connect()followed bygetState()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.
| Stage | Adapter methods | Checkpoint |
|---|---|---|
| Connection | connect, disconnect, getConnectionStatus | Remount creates one session; teardown leaves none. |
| Accounts and rules | listAccounts, setActiveAccount, isTradable, getTradingSymbolInfo | State and UI match the exact account and symbol route. |
| Order operations | previewOrder, modifyOrder, cancellation methods | Each adapter resolution has the documented controller effect. |
| Position operations | preview, modify, close, reverse, and flatten methods | Partial quantities and protection survive unchanged. |
| Account Manager | metadata, table loaders, subscriptions, and actions | Every declared page and action has backend support. |
| Options | resolveOptionContract | Broker 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
executionEnvironmentexplicitly; 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
| Symptom | First check |
|---|---|
trading.adapter-contract-invalid | Confirm getState and placeOrder are functions before controller construction. |
| Duplicate sessions | Confirm several surfaces were not independently given broker. |
| Missing action | Inspect getOperationSupport(), then the separate runtime/account and symbol gates. |
| Accepted order never appears | Confirm an authoritative orders or state update followed the acknowledgement. |
| Duplicate event delivery | Confirm the same backend change did not use both connect(host) and subscribe. |
| State goes backwards | Test 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.