Trading providers
Overview
The execution side of the platform: a trading provider hosts accounts, accepts orders, streams every state change as an event, and the trading service aggregates them all into one unified view.
On this page
What a trading provider is
Market data — bars, ticks, depth — is modelled by IDataProvider and covered in the
Market data connections chapter. Order routing is a separate concern
with its own contract, ITradingProvider (namespace TradeStrike.Pipeline.Trading).
A trading provider owns a set of accounts under one ProviderKey, accepts order placement,
cancellation and modification, and pushes out a stream of TradingEvents as state evolves.
The built-in simulator, the futures brokers (Rithmic, Tradovate, CQG) and the crypto venues are all
implementations of this one interface. A real broker connection typically implements both an
IDataProvider (prices) and an ITradingProvider (execution), sharing the same
provider key so accounts and instruments line up.
ITradingProvider is
not auto-registered by dropping a DLL. Trading providers are constructed by the host's connection
layer, started, and then handed to the platform's ITradingService via
AddProvider. You can implement and test a broker integration against the contract in isolation,
but wiring a brand-new venue into the running app is a host-side step. This chapter documents the contract
end to end so your provider behaves exactly like the built-ins.
Two seams: provider and service
There are two interfaces, and it matters which one you are looking at:
INTERFACE ITradingProvider
One trading-side data source. Owns accounts under a single ProviderKey, executes order
operations, and emits events. You implement this when integrating a venue.
INTERFACE ITradingService
The seam every consumer talks to — chart trader, strategies, log tab, risk rules. Aggregates over N providers and presents one merged view. You consume this; the host provides it.
The service routes order operations to the right provider transparently: a caller identifies an order or
account by id and the service dispatches. Routing is keyed solely on
AccountId.ProviderKey — a request for an account whose provider isn't registered throws
InvalidOperationException (that's a configuration bug, not a runtime condition to swallow).
ITradingService.csusing TradeStrike.Pipeline.Trading;
// Aggregated snapshots across every registered provider.
IReadOnlyList<Account> accounts = service.Accounts;
IReadOnlyList<Order> orders = service.OpenOrders;
IReadOnlyList<Position> positions = service.Positions;
// Lookup by id (cross-provider). Null when nothing hosts it.
Account? acct = service.GetAccount(accountId);
Order? order = service.GetOrder(orderId);
// What may the UI offer for this account? None when its provider isn't registered.
TradingProviderCapabilities caps = service.GetCapabilities(accountId);
// One merged event stream, fanned out from all providers.
service.EventEmitted += OnTradingEvent;
The capability model
A provider rarely supports everything at once — a phase-one integration might only discover accounts
and stream positions, with order placement coming later. Rather than throwing
NotSupportedException when the user clicks an unsupported action, a provider declares a
fine-grained TradingProviderCapabilities bitmask and the host greys out actions it does not
advertise. This matches the convention traders expect: the button is simply unavailable, not an error dialog.
TradingProviderCapabilities.csusing TradeStrike.Pipeline.Trading;
[Flags]
public enum TradingProviderCapabilities
{
None = 0,
PlaceOrders = 1 << 0, // submit via PlaceAsync
CancelOrders = 1 << 1, // cancel via CancelAsync
ModifyOrders = 1 << 2, // modify via ModifyAsync (price / qty / TIF)
FlattenPositions = 1 << 3, // flatten via FlattenAsync
AccountDiscovery = 1 << 4, // streams AccountSnapshotEvent / AccountRemovedEvent
PositionStream = 1 << 5, // streams PositionUpdateEvent (live qty + PnL)
FillsStream = 1 << 6, // streams FillEvent per execution
Brackets = 1 << 7, // supports BracketSpec attached to orders
AssetBalances = 1 << 8, // streams BalanceSnapshotEvent (per-asset crypto wallets)
Full = PlaceOrders | CancelOrders | ModifyOrders | FlattenPositions
| AccountDiscovery | PositionStream | FillsStream | Brackets | AssetBalances,
}
ITradingService.GetCapabilities returns None for an account whose provider isn't
registered (e.g. disconnected mid-session), so the UI gates safely as if nothing is supported. Provider
keys themselves follow a convention — "<venue>" or
"<venue>:<qualifier>" — documented under TradingProviderKeys;
use TradingProviderKeys.VenueOf(key) when you need the venue semantics rather than the exact
instance.
Connection lifecycle
The provider is constructed and started by the connection layer, then registered with the service.
The service does not start or stop providers — that lifecycle belongs to whoever constructed
it. AddProvider throws if a provider with the same ProviderKey is already
registered (keys are unique per service); RemoveProvider detaches a provider's stream but does
not stop the provider itself.
graph TD;
A[Connection layer constructs provider]-->B[provider.StartAsync];
B-->C[service.AddProvider];
C-->D[events flow into merged stream];
D-->E[service.RemoveProvider detaches stream];
E-->F[provider.StopAsync closes session];
EventEmitted after startup
gets only future events — the stream is not replayed. On attach, read the snapshot
properties (Accounts, OpenOrders, Positions) to learn current state,
then follow the stream for changes.
Event-driven by design
Order operations are async and return quickly — the real outcome arrives later on the event
stream, not as the method's return value. PlaceAsync hands you an OrderId as soon
as the request is accepted locally; whether the order works, fills, or is rejected by the broker comes
through EventEmitted as an OrderUpdateEvent. This snapshot-plus-events shape is
what keeps the UI binding-friendly and reconciliation-safe across reconnects. The
Order & account updates page covers the full event model.
Namespaces & where things live
| Concern | Namespace | Key types |
|---|---|---|
| Core trading | TradeStrike.Pipeline.Trading |
ITradingProvider, ITradingService, Order, OrderRequest, Position, Account, TradingEvent
|
| Risk validation | TradeStrike.Pipeline.Trading.Risk |
IRiskValidator, RiskLimits, RiskRejection
|
| Commissions | TradeStrike.Pipeline.Trading.Commissions |
ICommissionModel, CommissionRate, RateCommissionModel
|
| Brackets | TradeStrike.Pipeline.Trading.Brackets |
BracketPreset, IBracketPresetStore
|
| Simulation feed | TradeStrike.Pipeline.Trading.Sim |
IPriceFeed, TickQuote
|
Automated trade management (ATM) — the IAtmOrderGateway port and AtmStrategy
model that a strategy uses to manage protective orders — lives under
TradeStrike.Pipeline.Trading.Atm but is documented in the
Strategies → Advanced trade management page, not here.