FIX Protocol Integration
FIX is how most institutional order flow and a large share of institutional market data moves. It is a stateful, sequenced, session-oriented protocol over raw TCP. The chart never speaks it. Your backend does, and publishes the result over the gateway.
Why FIX terminates server-side
A FIX session is a long-lived, ordered, resumable conversation between two named
counterparties. Sequence numbers persist across reconnects and often across
days. A browser tab cannot own that state, and a SenderCompID credential in a
browser is a credential you have published.
One session is shared by every user of your product. Per-user identity is carried inside your gateway, not inside FIX.
Session layer
Before any business message flows, the session must be established and kept alive. Your engine handles this; you configure it.
The rules that matter operationally:
- Every message carries
MsgSeqNum(34). A gap is a protocol error, not a hint — the engine must issue aResendRequestand stop processing until the gap is filled. - Sequence numbers reset only when both sides agree, usually at a scheduled daily session boundary or via
ResetSeqNumFlag(141=Y) on logon. - The message store must be durable. An engine that loses its store cannot recover a session, and the counterparty will refuse the logon.
HeartBtInt(108) is negotiated at logon. A missed heartbeat plus an unansweredTestRequestmeans the session is dead and must be re-established.
Use a proven engine rather than parsing tag=value yourself: QuickFIX/J for the JVM, quickfix-go, QuickFIX/n for .NET, or a commercial engine where latency and support obligations justify it. Newer venues may offer SBE or FAST encodings instead of classic tag=value; the session semantics above are unchanged.
Market data over FIX
A market data session is a subscription conversation: you request, they snapshot, then they stream increments.
Request fields
| Tag | Field | Use |
|---|---|---|
| 262 | MDReqID | Correlates every response with this subscription |
| 263 | SubscriptionRequestType | 0 snapshot, 1 snapshot plus updates, 2 unsubscribe |
| 264 | MarketDepth | 1 top of book, N levels, 0 full book |
| 265 | MDUpdateType | 0 full refresh, 1 incremental |
| 269 | MDEntryType | 0 bid, 1 offer, 2 trade, 4 open, 5 close, 7 high, 8 low |
Mapping entries to SDK types
Each MDEntryType lands in a different chart contract.
MDEntryType (269) | Carries | Becomes |
|---|---|---|
0 Bid, 1 Offer with depth 1 | MDEntryPx (270), MDEntrySize (271) | Quote.bid / Quote.ask |
0 Bid, 1 Offer with depth > 1 | Price levels per side | SdkMarketDepth.bids / .asks as DepthLevel |
2 Trade | Print price, size, time | TimeAndSalesEntry, and the aggregated OHLCV bar |
4, 5, 7, 8 | Session open, close, high, low | Quote.open, .previousClose, .high, .low |
MDUpdateAction (279) drives book maintenance: 0 inserts a level, 1 changes
its size, 2 deletes it. Apply deltas to a book you hold in the gateway and
publish whole normalized frames to the chart — the SDK consumes book state, not
FIX deltas.
Most FIX market data sessions publish trades, not bars. Aggregate prints into
OHLCV in the gateway keyed on the bar's opening time, then serve them through
loadBars and subscribeRealTimeBars like any other source. See
Market data types.
Order routing over FIX
NewOrderSingle from a TradingOrderDraft
| Tag | FIX field | Draft field |
|---|---|---|
| 11 | ClOrdID | Your generated id, unique per session-day |
| 1 | Account | accountId |
| 55 | Symbol | symbol.brokerSymbol — the execution identity, never the display ticker |
| 54 | Side | side: buy → 1, sell → 2 |
| 38 | OrderQty | quantity |
| 40 | OrdType | type: market → 1, limit → 2, stop → 3, stop-limit → 4 |
| 44 | Price | price |
| 99 | StopPx | stopPrice |
| 59 | TimeInForce | duration: day → 0, GTC → 1, IOC → 3, FOK → 4 |
| 60 | TransactTime | Submission time, UTC |
ClOrdID is yours and immutable; OrderID (37) is the broker's. Keep both. A
cancel or replace references OrigClOrdID (41) and carries a new ClOrdID, so
a chain of amendments needs the full lineage to reconcile.
ExecutionReport to order state
ExecType (150) says what just happened; OrdStatus (39) says where the order
now stands. Drive TradingOrder.status from OrdStatus.
OrdStatus (39) | TradingOrderStatus |
|---|---|
A Pending New | pre-submitted |
0 New | working |
1 Partially filled | partially-filled |
2 Filled | filled |
6 Pending Cancel | cancelling |
E Pending Replace | modifying |
4 Canceled | cancelled |
C Expired | expired |
8 Rejected | rejected |
Every ExecType=F also produces a fill. Map LastPx (31), LastQty (32),
ExecID (17), and TransactTime (60) into a TradingExecution, and let
CumQty (14), LeavesQty (151), and AvgPx (6) update the order record.
Text (58) and OrdRejReason (103) carry the rejection message the trader
needs to see.
Emit each of these as a typed event so the chart updates without a reload:
import type { TradingEvent, TradingExecution, TradingOrder } from '@tradescript/pro/sdk';
export function onExecutionReport(
order: TradingOrder,
fill: TradingExecution | undefined,
emit: (event: TradingEvent) => void,
book: { orders: TradingOrder[]; executions: TradingExecution[] },
): void {
book.orders = [...book.orders.filter((o) => o.id !== order.id), order];
emit({ type: 'orders', orders: book.orders });
if (fill) {
book.executions = [...book.executions, fill];
emit({ type: 'executions', executions: book.executions });
}
}
Topologies
| Topology | What you connect to | Trade-off |
|---|---|---|
| Broker FIX | Your broker's order-routing session | Simplest institutional path; broker owns risk, clearing, and market access |
| Vendor FIX proxy | A provider that normalizes many venues behind one session | One integration for many markets; adds a hop and a dependency |
| Direct venue | The exchange's own FIX or binary gateway | Lowest latency; you own membership, certification, conformance, and risk controls |
Direct venue access is a regulatory and operational commitment, not just a technical one — exchange membership or sponsored access, mandated pre-trade risk checks, conformance testing, and certification per venue. Most funds reach production faster through a broker or vendor session and move venue-direct only where latency demonstrably pays for it.
Colocation
When the session layer is colocated and the chart is not, split them. Run FIX engines in the venue's datacenter, publish normalized frames over your private network to a regional gateway tier, and let browsers connect to the gateway. The chart contracts are unchanged; only the hop count differs.
Drop copy
A drop-copy session is a separate read-only FIX feed of your own executions,
independent of the order session. Use it as the authoritative source for
TradingState.executions and position reconciliation. If the order session
disconnects mid-fill, drop copy is how the chart still shows the truth.
Checklist
- Durable, backed-up message store per session; sequence state survives restarts.
- Scheduled session start and end times matching the counterparty's contract.
ResendRequestandPossDupFlaghandling that never double-counts a fill.ClOrdIDuniqueness enforced per session-day, with the amendment chain retained.- Book snapshots sequence-matched before publication; a gap invalidates the book.
- Separate certification and production credentials, and separate
SenderCompIDs. - Every rejection surfaced with
Text(58) intact — never swallowed into a generic failure.
Next steps
- Build the data gateway — where normalized FIX output is published.
- Broker integration — the complete production path.
- Interactive Brokers — a worked example including IBKR's FIX CTCI option.