Skip to content
Get Started

FIX

Who this document is for

This is the complete reference for connecting to the account's FIX gateway. It is written for two audiences, and both get everything they need from this one document:

  • Trading clients / algos — order entry, market data, reference data, position and trade queries.
  • Clearing firms / risk desks / drop-copy consumers — real-time, read-only visibility into all account activity (see section 11, Drop Copy), without needing to place a single order.

Every message type, every field, and every enumerated value the gateway sends or accepts is documented below — nothing is left implicit. Section 14 is an explicit, exhaustive list of what is not supported, so there should be no ambiguity about gateway behavior after reading this document. If something you need isn't covered here, it isn't implemented — contact your account representative rather than assuming.

Table of Contents

  1. Overview & Connectivity
  2. Session Layer
  3. Authentication & Authorization
  4. Order Entry
  5. Execution Reports & Cancel Rejects
  6. Market Data
  7. Reference Data
  8. Positions
  9. Trade Capture
  10. Error Handling
  11. Drop Copy — Guide for Clearing Firms and Drop-Copy Consumers
  12. Common Message Sequences
  13. Reconnection & Reconciliation
  14. Known Limitations
  15. Appendix A — Tag Index
  16. Appendix B — Enumerations Reference

1. Overview & Connectivity

  • Protocol: FIX 4.x over raw TCP. One TCP connection = one FIX session; no multiplexing of multiple logical sessions over one socket.
  • FIX version: not fixed server-side. On Logon, the server reads the client's own BeginString(8) and echoes that exact value back on every reply for the rest of the session. This gateway is built and tested against FIX.4.4 — send BeginString=FIX.4.4 unless your account representative tells you otherwise. The client's declared version is not validated against a supported list, so a mismatched version will not be rejected outright, but field behavior described in this document assumes FIX.4.4 semantics.
  • Message framing: standard FIX tag=value, SOH(\x01)-delimited, with BodyLength(9) and a trailing CheckSum(10) computed normally.
  • Connection details (host, port, credentials, and your SenderCompID/TargetCompID convention) are provided per account by your account representative — they are not fixed values in this document.
  • Header fields on every server message: BeginString(8) (echoed, see above), BodyLength(9), MsgType(35), SenderCompID(49)/TargetCompID(56) (swapped from what you sent — see below), MsgSeqNum(34) (incrementing per session, starting at 1), SendingTime(52), CheckSum(10).
  • CompID swap: the server's SenderCompID(49) on every reply is your TargetCompID(56) from Logon; the server's TargetCompID(56) is your SenderCompID(49) from Logon. This is standard FIX convention.
  • Sequence numbers: incoming MsgSeqNum gaps are logged and the server resynchronizes to whatever it next receives — they are not enforced or rejected. Do not rely on the gateway to detect or refuse a sequence gap on your side; see section 13 for how to reconcile state safely regardless.

2. Session Layer

Logon (35=A) — client → server

Tag Name Req'd Values/Format Notes
98 EncryptMethod Y 0 only Any value sent is ignored; the server always replies with 0.
108 HeartBtInt Y seconds, int Drives the server's heartbeat cadence for this session (see Session liveness below).
553 Username N string If absent, SenderCompID(49) is used as the username instead — see Auth fallback below.
554 Password Y string Always required, regardless of which identifier path is used.
49 SenderCompID Y string Becomes the login identifier if tag 553 is absent.
56 TargetCompID Y string

Auth fallback: if Username(553) is not sent, the server uses SenderCompID(49) as the username. This exists because many FIX engines don't expose tag 553 directly — set your account's username into SenderCompID and it works without needing 553 at all.

Reject: FIX has no dedicated logon-reject message type. On bad credentials, unknown user, or an account that is already logged in on this transport, the server replies with Logout(35=5) carrying Text(58) with a human-readable reason, then closes the TCP connection. There is no machine-readable reject code on this path — parse Text(58), or check with your account representative if a login is failing unexpectedly.

Success reply — server → client, Logon(35=A): EncryptMethod(98)=0, HeartBtInt(108) echoed back.

One session per account per transport: an account can have exactly one active FIX session at a time. A second Logon attempt with the same credentials while a session is already active is rejected (see above) until the first session logs out or times out.

Heartbeat (35=0)

Bidirectional. You may send one when idle, or in reply to a TestRequest; the server does the same (see Session liveness). TestReqID(112) is echoed back only when a Heartbeat is sent in reply to a TestRequest.

TestRequest (35=1) — client → server

TestReqID(112) required. The server always replies with Heartbeat(35=0) echoing the same TestReqID.

ResendRequest (35=2) — client → server

BeginSeqNo(7) is accepted but not used to select content. The server always replies with SequenceReset(35=4), GapFillFlag(123)=Y, NewSeqNo(36)=its next outbound sequence number. No message replay is performed — this is gap-fill only, never actual history. See section 13 for how to reconcile state that was genuinely missed.

SequenceReset (35=4) — client → server

NewSeqNo(36) sets the server's expected next inbound sequence number. No reply is sent.

Logout (35=5)

Either direction. The server replies with its own Logout(35=5), then closes the TCP connection. The account's login slot for this transport is released so a new Logon can succeed — this release is not necessarily instantaneous with the Logout reply, so a reconnect attempt immediately after logging out should retry briefly on a rejected Logon rather than treat one failure as final.

Session liveness (server-driven)

  • The server sends an unsolicited Heartbeat(0) after HeartBtInt seconds of send-idle (nothing sent to you).
  • The server sends TestRequest(1) after 1.5 × HeartBtInt seconds of receive-idle (nothing received from you).
  • The server disconnects after 2 × HeartBtInt seconds of receive-idle with no reply to that TestRequest.

Set HeartBtInt to a value your engine can reliably keep up with — there is no minimum or maximum enforced, but a very small value will cause spurious disconnects under any network jitter.

3. Authentication & Authorization

Credentials are matched against server-side account records by Username/SenderCompID + Password. Each account has a role, and some requests are restricted by role — a request from a role that isn't permitted for that action is explicitly rejected with the appropriate reject message for that request type, never silently dropped.

Action TRADER VIEWER CLEARER / ADMIN
Market data subscribe (MarketDataRequest) ❌ (MarketDataRequestReject)
Submit / cancel / replace / mass-cancel orders ❌ (explicit reject on the specific request)
Order / position / trade status queries, reference data
Real-time ExecutionReport copies for all account activity

VIEWER is the natural role for a clearing firm or drop-copy consumer: it has full read access to everything happening on the account (orders, fills, positions, trade history, in real time) and is structurally incapable of submitting, canceling, or replacing an order, or subscribing to market data. See section 11.

Every logged-in session, regardless of role, receives the same real-time copy of account activity — this is not a VIEWER-only behavior, any role can be used as a drop-copy feed if desired, though VIEWER is recommended for a pure risk/clearing connection specifically because it cannot also act on the account.

4. Order Entry

NewOrderSingle (35=D) — client → server

Tag Name Req'd Values/Format Notes
11 ClOrdID Y string Your unique identifier for this order.
55 Symbol Y string Must match a known instrument — otherwise rejected via BusinessMessageReject(j), not ExecutionReport (see section 10).
54 Side Y 1=Buy, 2=Sell
40 OrdType Y see table below
38 OrderQty Y int, >0
44 Price conditional decimal Required for OrdType 2 (Limit) and Limit+IOC.
99 StopPx conditional decimal Required for OrdType 3 (Stop) and 4 (Stop Limit).
59 TimeInForce N 0 (Day, default) or 3 (IOC) Any other value (1=GTC, 2=OPG, 4=FOK, 5=GTX, 6=GTD) is rejected outright, not silently downgraded to Day.
1138 DisplayQty N int, >0 Iceberg display quantity. Omit for a fully-displayed order.
110 MinQty N int, >0 Minimum fill quantity.
21 HandlInst N not enforced Accepted but not validated.
60 TransactTime N not read Accepted but not used — the server stamps its own time.

OrdType(40) accepted combinations:

OrdType TimeInForce Meaning Requires
1 0 Market
2 0 Limit Price(44)
3 0 Stop StopPx(99)
4 0 Stop Limit Price(44) + StopPx(99)
2 3 Limit, Immediate-or-Cancel — maps to the account's native "FAK" order type: partial fills allowed, unfilled remainder killed immediately, never rests on the book Price(44)

Any other OrdType/TimeInForce combination — including IOC combined with Stop or Stop Limit — is rejected. There is no true all-or-none Fill-or-Kill, GTC, GTD, OPG, or GTX support; see section 14.

Reply: ExecutionReport(8), ExecType/OrdStatus=8 (Rejected), for any order-level reject the gateway's own field/role validation catches before submission (VIEWER role, Side not set, unsupported TimeInForce, unsupported OrdType/TimeInForce combination, OrderQty not > 0), or a reject from the account's own pre-trade risk/validation checks (price outside the instrument's price band, off-tick price, size/risk limits). The latter carries OrdRejReason(103) — see Appendix B for the full value table and which side (gateway pre-trade check vs. the exchange itself, asynchronously) can produce which values. An unknown Symbol is handled differently: it gets BusinessMessageReject(j) instead of an ExecutionReport, since the instrument itself, not the order's fields, is what's invalid.

OrderCancelRequest (35=F) — client → server

Tag Name Req'd Notes
11 ClOrdID Y This cancel's own ID — becomes the "current" ClOrdID for the order afterward, for any later cancel/replace using OrigClOrdID.
41 OrigClOrdID conditional Required if OrderID(37) is not sent.
37 OrderID conditional Primary lookup key. If present, used directly; if absent, the order is looked up by matching OrigClOrdID against the last-known ClOrdID for each of this session's open orders.

Scope: session-scoped — you can only cancel an order this same session originally submitted (or amended). See section 13 for what this means across a reconnect.

Reply: ExecutionReport(8) on success, OrderCancelReject(9) on failure (unknown order, VIEWER role, or a genuine backend rejection of the cancel attempt) — see section 5.

OrderCancelReplaceRequest (35=G) — client → server

Same dual lookup as OrderCancelRequest (OrderID primary, OrigClOrdID fallback), same session scope. Only Price(44) and OrderQty(38) can be amended — TimeInForce and StopPx are not applied even if sent. Omit a field to leave that part of the order unchanged.

Reply: ExecutionReport(8) on success, OrderCancelReject(9) on failure.

OrderStatusRequest (35=H) — client → server

Dual lookup: OrderID(37) primary, ClOrdID(11) fallback if OrderID is absent or not found.

Scope: account-wide — works for any order on the account, regardless of which session originally submitted it, including orders from a prior, now-disconnected session. This is the key reconciliation tool after a reconnect — see section 13.

Reply: ExecutionReport(8) reflecting the order's current state, or BusinessMessageReject(j) if the order can't be found.

OrderMassStatusRequest (35=AF) — client → server

Tag Name Req'd Notes
55 Symbol N Optional filter.
54 Side N Optional filter (1/2).

Scope: account-wide — reports every open order on the account (not just this session's own), narrowed by Symbol/Side if given. There is no CME-style MassStatusReqType scoping beyond that (the tag is not read).

Reply: one ExecutionReport(8) per matching order. No matching orders → no reply at all (unlike TradeCaptureReportRequest/RequestForPositions, which always send at least one reply — this asymmetry is a known limitation, see section 14).

OrderMassCancelRequest (35=q) — client → server

Same Symbol/Side filters as OrderMassStatusRequest, but session-scoped, unlike the status queries above — only cancels orders this session itself submitted or amended (the same safety boundary as OrderCancelRequest/OrderCancelReplaceRequest). There is no CME-style MassCancelRequestType scoping beyond Symbol/Side (the tag is not read). VIEWER role → BusinessMessageReject(j), BusinessRejectReason=6 (Not authorized).

Reply: one ExecutionReport(8) per matching order, reflecting whatever actually happened to it — OrdStatus=4 (Canceled) if it canceled, its prior unchanged OrdStatus if that specific order's cancel attempt failed. This lets you tell a genuine per-order failure apart from silence, rather than guessing from an incomplete batch. No matching orders → no reply.

5. Execution Reports & Cancel Rejects

ExecutionReport (35=8) — server → client

Tag Name Always sent? Notes
1 Account Y
37 OrderID Y "NONE" if the order was rejected pre-submission (never reached the account).
11 ClOrdID usually Omitted only if no ClOrdID is on record for the order.
17 ExecID Y
150 ExecType Y See code table below.
39 OrdStatus Y See code table below.
103 OrdRejReason on reject Present only when ExecType=8. See Appendix B for the value table and source (gateway pre-trade check vs. exchange, asynchronously).
55 Symbol Y
54 Side Y 1/2
40 OrdType Y
59 TimeInForce Y 0 or 3.
38 OrderQty Y
1138 DisplayQty conditional Present if the order was submitted with one.
110 MinQty conditional Present if the order was submitted with one.
44 Price conditional Present for Limit, Stop Limit, and Limit+IOC.
99 StopPx conditional Present for Stop and Stop Limit.
151 LeavesQty Y
14 CumQty Y
6 AvgPx Y 0 until the first fill.
32 LastQty on fill Present only when this specific report represents a fill.
31 LastPx on fill
60 TransactTime Y

ExecType(150) / OrdStatus(39) code pairs actually used (both fields always carry the same code, except the Rejected-cancel/replace case noted):

Code Meaning When
A Pending New Order accepted for pre-trade risk check, not yet acknowledged by the exchange.
0 New Acknowledged by the exchange.
6 Pending Cancel Cancel submitted, not yet confirmed.
4 Canceled
E Pending Replace Replace submitted, not yet confirmed.
5 Replaced
8 Rejected A new order was rejected (OrdStatus is also 8); or a cancel/replace attempt against a live order was rejected asynchronously by the exchange itself (ExecType=8 but OrdStatus stays 0 — the order is still live and unaffected). See the important note below.
2 Filled
1 Partially Filled

Important — two different shapes for a rejected cancel/replace: a cancel or replace request can be rejected in two structurally different ways, and they arrive as two different message types:

  1. Synchronous — the gateway itself (or the account's own immediate validation) rejects your OrderCancelRequest/OrderCancelReplaceRequest right away → you get OrderCancelReject(9), CxlRejReason(102) — see below.
  2. Asynchronous — your cancel/replace request was accepted and forwarded, but the exchange itself, some time later, rejects it (e.g. the order had already been filled) → this arrives as an unsolicited ExecutionReport(8), ExecType=8/OrdStatus=0 (still New/live), with OrdRejReason(103) carrying the exchange's own reason — not an OrderCancelReject. If you are building reconciliation logic that only watches for OrderCancelReject(9) to detect a failed cancel, you will miss this case — watch ExecType=8 on ExecutionReport as well.

OrderCancelReject (35=9) — server → client

Tag Name Notes
1 Account
37 OrderID "NONE" if unknown.
11 ClOrdID
41 OrigClOrdID
39 OrdStatus Always 8.
434 CxlRejResponseTo 1=in response to OrderCancelRequest, 2=in response to OrderCancelReplaceRequest.
102 CxlRejReason See Appendix B for the full value table.
58 Text Human-readable reason — always present, read this alongside the numeric code.

This message is only ever sent synchronously, as the direct reply to your own OrderCancelRequest/OrderCancelReplaceRequest on the same session. It is sent directly to the requesting session in every case. A genuine backend rejection of a cancel/replace against a real, previously-live order is also copied to every other logged-in session on the account (the same account-wide broadcast as ExecutionReport — see section 11), since it's real risk information a drop-copy observer needs. A VIEWER-role reject or an "unknown order" reject (a session-scoped lookup miss, not a real trading event) is sent only to the requesting session, not broadcast.

6. Market Data

MarketDataRequest (35=V) — client → server

Tag Name Req'd Notes
262 MDReqID Y Echoed on every reply for this subscription.
263 SubscriptionRequestType Y 1=Subscribe. 2=Unsubscribe. 0=Snapshot-only is treated as Subscribe (there is no one-shot-only mode).
264 MarketDepth N 1=top-of-book only. Any other value (including absent, or 0) also enables full order-book depth updates.
146 NoRelatedSym N Multi-symbol is supported — every Symbol(55) occurrence in the message is honored regardless of what this count field says.
55 Symbol Y One or more, for a watchlist-style subscription in a single message.
267 NoMDEntryTypes N
269 MDEntryType N 0=Bid, 1=Offer. Every occurrence is read; if none is given, both sides are sent.

TRADER role only — VIEWER/CLEARER/ADMIN get MarketDataRequestReject. Unsubscribe (SubscriptionRequestType=2) gets no reply. An unknown symbol also gets MarketDataRequestReject.

MarketDataRequestReject(35=Y) carries MDReqID(262) and Text(58) only — there is no numeric MDReqRejReason code; parse Text for the cause ("Not authorized...", "Unknown symbol", "No Symbol specified").

MarketDataSnapshotFullRefresh (35=W) — server → client

Sent once per symbol, immediately on a successful subscribe. MDReqID(262), Symbol(55), NoMDEntries(268), then per entry: MDEntryType(269), MDEntryPx(270), MDEntrySize(271).

MarketDataIncrementalRefresh (35=X) — server → client

Sent on every book change for a subscribed symbol. Two independent update streams run per subscription:

  • Top-of-book: always active regardless of MarketDepth. One message per change, with an entry for whichever side(s) actually changed.
  • Full depth: active unless MarketDepth=1 was sent on subscribe. One message per price-level change, and each entry additionally carries MDPriceLevel(1023) (1-based).

Each entry carries: MDUpdateAction(279), MDEntryType(269), Symbol(55), and — except on a Delete — MDEntryPx(270)/MDEntrySize(271). MDUpdateAction is computed per entry from whether that price level/side was previously empty: 0=New (was empty, now has size), 1=Change (had size both before and after), 2=Delete (now empty; price/size are omitted on this action).

7. Reference Data

SecurityListRequest (35=x) → SecurityList (35=y)

Tag Name Notes
320 SecurityReqID Echoed back.
559 SecurityListRequestType 0 combined with Symbol(55) → single-instrument filter. Any other value (or absent) → full tradeable-instrument universe dump.

Response: SecurityReqID(320), NoRelatedSym(146), then per instrument: Symbol(55), SecurityExchange(207).

SecurityDefinitionRequest (35=c) → SecurityDefinition (35=d)

Request: SecurityReqID(320), Symbol(55). Single-instrument lookup only.

Response fields: SecurityReqID(320), SecurityResponseID(322) (server-generated), TotNoRelatedSym(393) (1=found, 0=not found), Symbol(55), SecurityExchange(207), SecurityType(167) — FUT, OPT, or MLEGMinPriceIncrement(969), ContractMultiplier(231), and LowLimitPrice(1148)/HighLimitPrice(1149) (the trading day's allowable price band, omitted if the instrument has none configured). Not found → Text="Unknown symbol", no instrument fields sent.

LowLimitPrice/HighLimitPrice are exactly the band NewOrderSingle's own pre-trade risk check validates against — a price outside this range is rejected with OrdRejReason=16 (see Appendix B).

8. Positions

RequestForPositions (35=AN) → PositionReport (35=AP)

Request: PosReqID(710), optional — echoed back on every reply. SubscriptionRequestType(263) selects the mode:

Value Meaning
0 or absent Snapshot — one-time reply only.
1 Snapshot + Updates — replies now with the current snapshot, then pushes a fresh PositionReport for a symbol every time a fill changes that symbol's position, until unsubscribed or the session ends.
2 Unsubscribe — no reply, same convention as MarketDataRequest's unsubscribe.

Response fields:

Tag Name Notes
710 PosReqID Echoed if you sent one. On a subscription push, this is the PosReqID from the original subscribe request.
721 PosMaintRptID Server-generated, unique per report.
728 PosReqResult 0=Valid, 2=No positions found.
727 TotalNumPosReports Same value on every report in a batch. 1 on a subscription push (a single symbol, not a re-snapshot of everything).
325 UnsolicitedIndicator N for a direct reply to your request, Y for a subscription push triggered by a fill.
55 Symbol
715 ClearingBusinessDate YYYYMMDD, current UTC date.
702 NoPositions 1
703 PosType "TOT" (Total Transaction Qty).
704 LongQty Gross filled long quantity for this symbol — not netted against ShortQty.
705 ShortQty Gross filled short quantity for this symbol — not netted against LongQty. A symbol can carry both simultaneously (e.g. 2 long fills + 2 short fills reports LongQty=2, ShortQty=2, not a netted 0/0).
753 NoPosAmt 1
707 PosAmtType "FMTM" (Final Mark-to-Market).
708 PosAmt Open/unrealized P&L for this symbol.

One PositionReport per symbol with activity. Zero positions on the account → a single report with PosReqResult=2, no position/amount fields. Margin and risk-limit figures are deliberately not sent over FIX — see section 14.

Push behavior is strictly opt-in via SubscriptionRequestType=1 — the server never sends an unsolicited PositionReport unless a session has explicitly subscribed. Send SubscriptionRequestType=2 to stop.

9. Trade Capture

TradeCaptureReportRequest (35=AD) → TradeCaptureReport (35=AE)

Tag Name Req'd Notes
568 TradeRequestID N Echoed back.
55 Symbol N Optional filter.
37 OrderID N Optional filter.

Response, one per matching fill: TradeRequestID(568), TradeReportID(571, server-generated), TradeReportTransType(487)=0 (New), TotNumTradeReports(748) (same value on every report in the batch), LastRptRequested(912) (Y only on the final report in the batch), ExecID(17), OrderID(37), Symbol(55), Side(54), LastPx(31), LastQty(32), TransactTime(60).

Zero matches, or the trade log isn't available, → TradeCaptureReportRequestAck(35=AQ) instead, with TotNumTradeReports=0, LastRptRequested=Y (and Text="Trade log not available" specifically when the log itself couldn't be reached).

Scope: account-wide, not scoped to the requesting session — a brand-new connection that has never placed an order itself can send an unfiltered request and get the account's complete trade history. This is the primary tool for a clearing firm's trade reconciliation — see section 11.

10. Error Handling

  • Reject (35=3) — session-level, sent for any application message received before Logon has completed successfully: RefSeqNum(45), SessionRejectReason(373)=99 (Other — no more specific FIX 4.4 code fits "not logged on"), Text(58)="Not logged on".
  • BusinessMessageReject (35=j) — for a business-level problem not tied to a specific order's fields (unknown instrument, unknown ID, unsupported message, unauthorized request): RefMsgType(372), RefSeqNum(45), BusinessRejectReason(380), Text(58).
Cause BusinessRejectReason(380)
Recognized but unsupported MsgType (anything not listed in this document) 3 = Unsupported Message Type
Unknown Symbol on NewOrderSingle 2 = Unknown Security
OrderStatusRequest for an order that can't be found 1 = Unknown ID
Unauthorized OrderMassCancelRequest (VIEWER role) 6 = Not authorized
Exchange-side error tied to an order, pushed unsolicited (not a reject of your own inbound request) Passed through directly from the exchange's own reject reason, unclamped — the exchange defines codes well beyond FIX's own 07 range for this field (e.g. throttle limits, kill switch, risk controls), and those are preserved rather than folded into a generic 0 = Other.

See Appendix B for the complete BusinessRejectReason enum, and section 5 for OrdRejReason(103)/CxlRejReason(102), which cover order- and cancel-specific rejects instead.

11. Drop Copy — Guide for Clearing Firms and Drop-Copy Consumers

This section is a self-contained guide for a firm that only needs read-only, real-time visibility into account activity — no order entry. It restates material from earlier sections in one place, specifically for this use case.

Setup

  1. Log on (Logon, section 2) with a VIEWER-role account. VIEWER is recommended because it is structurally incapable of order entry or market data subscription — there's nothing to lock down on your end, the role does it. Any role technically receives the same real-time copy (see section 3), but VIEWER is the correct choice for a pure clearing/risk connection.
  2. That's it — no subscription message is required for order/trade activity. It starts flowing immediately and automatically, for every order on the account, regardless of which other session submitted it.
  3. Optionally, also subscribe to live position updates: RequestForPositions(AN), SubscriptionRequestType=1 (section 8).

What you receive, unsolicited, in real time

Message When Notes
ExecutionReport(8) Every order-lifecycle event on the account: new-order acks, fills (partial and full), cancels, replaces, and rejects — both new-order rejects and asynchronous exchange-side cancel/replace rejects (ExecType=8, see the note in section 5). Not filtered by which session submitted the order — this is the entire account's activity.
OrderCancelReject(9) Only for a genuine backend rejection of a cancel/replace against a real, previously-live order — see section 5 for exactly which case this is (as opposed to a VIEWER-role or unknown-order reject, which is not broadcast, since it isn't a real account event).
BusinessMessageReject(j) An exchange-side error tied to a specific order, unsolicited — see section 10.
PositionReport(AP) Only if you subscribed (SubscriptionRequestType=1) — pushed whenever a fill changes a symbol's position. Opt-in, not automatic.

Reconciliation

For anything that might have been missed (a disconnect, a gap, a message that arrived before your system was ready to log it), do not rely on the live feed alone — poll the account-wide, request/reply endpoints instead, all of which work identically regardless of which session originally created the activity:

  • OrderStatusRequest(H) / OrderMassStatusRequest(AF) — current state of any order, or every open order.
  • TradeCaptureReportRequest(AD) — complete fill history, optionally filtered by Symbol/OrderID.
  • RequestForPositions(AN) — current position and open P&L per symbol.

See section 13 for the full reconciliation workflow after a disconnect. ResendRequest does not replay missed messages (see section 2) — the three account-wide endpoints above are the supported way to recover state, not sequence-number-based replay.

12. Common Message Sequences

(a) Session establishment 1. Client → Logon(A) 2. Server → Logon(A)

(b) Discover tradeable instruments 1. Client → SecurityListRequest(x) 2. Server → SecurityList(y) 3. (optional, per instrument) Client → SecurityDefinitionRequest(c) → Server → SecurityDefinition(d)

(c) Subscribe to market data 1. Client → MarketDataRequest(V), SubscriptionRequestType=1 2. Server → MarketDataSnapshotFullRefresh(W) 3. Server → MarketDataIncrementalRefresh(X) — repeated, on every book change

(d) Place and monitor a limit order through fill 1. Client → NewOrderSingle(D), OrdType=2 2. Server → ExecutionReport(8), ExecType=A (Pending New) 3. Server → ExecutionReport(8), ExecType=0 (New) — once acknowledged 4. Server → ExecutionReport(8), ExecType=2 or 1 (Filled / Partially Filled) — on fill(s)

(e) Amend then cancel a working order 1. Client → OrderCancelReplaceRequest(G), OrderID from step (d) 2. Server → ExecutionReport(8), ExecType=5 (Replaced) 3. Client → OrderCancelRequest(F), OrderID (or OrigClOrdID = the replace's own ClOrdID) 4. Server → ExecutionReport(8), ExecType=4 (Canceled)

(f) Query order status after a disconnect/reconnect 1. Client → Logon(A) on a new session 2. Client → OrderStatusRequest(H) or OrderMassStatusRequest(AF) - Works the same as on the original session — these are account-wide (section 4), so orders submitted on a prior session are still visible. Cancel/replace of those orders is not — see section 13.

(g) Pull trade history (works the same on a brand-new session, e.g. right after Logon on first connect) 1. Client → TradeCaptureReportRequest(AD), optional Symbol/OrderID filter 2. Server → TradeCaptureReport(AE) × N, or TradeCaptureReportRequestAck(AQ) if none match

(h) Pull current positions 1. Client → RequestForPositions(AN) 2. Server → PositionReport(AP) × N (one per symbol with activity), or × 1 with PosReqResult=2 if none

(h2) Subscribe to position updates 1. Client → RequestForPositions(AN), SubscriptionRequestType=1 2. Server → PositionReport(AP) × N, same as a plain pull 3. Server → PositionReport(AP) again, whenever a fill changes a symbol's position, until the client sends RequestForPositions with SubscriptionRequestType=2, or the session ends

(i) Clean session teardown 1. Client → Logout(5) 2. Server → Logout(5), connection closed

(j) Drop copy — passive real-time monitoring of account activity

See section 11 for the complete guide. In short: 1. Client → Logon(A), VIEWER role recommended 2. Server → ExecutionReport(8) / OrderCancelReject(9) / BusinessMessageReject(j) pushed unsolicited as account activity happens 3. No further action needed — this is passive.

13. Reconnection & Reconciliation

A FIX session is not persistent across a disconnect — there is no message replay (ResendRequest is gap-fill only, see section 2), and order mutation requests (OrderCancelRequest/OrderCancelReplaceRequest/OrderMassCancelRequest) are scoped to the session that originally submitted or amended the order. This means, concretely, after a reconnect on a new session:

  • Still works, unchanged, account-wide: OrderStatusRequest, OrderMassStatusRequest, RequestForPositions, TradeCaptureReportRequest — every one of these sees the whole account's state and history, including everything from before the disconnect.
  • Does not carry over: the ability to OrderCancelRequest/OrderCancelReplaceRequest/OrderMassCancelRequest an order that was submitted on the previous session, from the new session. If you need to cancel or amend an order across a reconnect, that specific limitation applies — contact your account representative if this is a blocking requirement for your workflow.
  • Not replayed: any ExecutionReport/OrderCancelReject/BusinessMessageReject pushed while you were disconnected is gone — it will not be resent. Reconcile using the account-wide endpoints above (they reflect current state, which is sufficient to detect anything you missed) rather than expecting the missed messages themselves to reappear.

Recommended reconnect sequence: LogonOrderStatusRequest/OrderMassStatusRequest (confirm every order you believe is open still is, and its current LeavesQty/CumQty) → RequestForPositions (confirm positions match your own books) → TradeCaptureReportRequest if you need to backfill fill-level detail for the disconnected window.

14. Known Limitations

This is an exhaustive list — if a capability isn't mentioned here or elsewhere in this document as unsupported, and it isn't working as documented, that's a genuine issue to report, not an undocumented gap.

  • TimeInForce: only Day(0) and IOC(3) are supported. GTC, GTD, OPG, GTX, and true all-or-none FOK are rejected outright on NewOrderSingle — the account has no execution model for them (orders always live for a single session/day; IOC/FAK is the only "kill" semantic supported).
  • No party-identity fields: PartyID/PartyRole/SenderSubID/TargetSubID and similar CME-style identity/give-up fields are not read or sent. This gateway is a client-facing account gateway, not a direct exchange session — there is exactly one account per FIX session.
  • No margin/risk-limit figures over FIX: not available on PositionReport or anywhere else in this protocol. This matches common DMA vendor practice (margin is typically a separate REST/portal concern, not a FIX concern) — it is not a planned addition.
  • Order mutation is session-scoped; queries are not: OrderCancelRequest/OrderCancelReplaceRequest/OrderMassCancelRequest only apply to orders the same session submitted or amended — no cross-reconnect visibility for mutation. OrderStatusRequest/OrderMassStatusRequest, positions, and trade history are all account-wide. See section 13.
  • No fine-grained mass request typing: OrderMassStatusRequest/OrderMassCancelRequest support only Symbol/Side narrowing — there is no CME-style MassStatusReqType/MassCancelRequestType scoping beyond that (the tags are not read).
  • OrderMassStatusRequest sends no reply when nothing matches — unlike TradeCaptureReportRequest/RequestForPositions, which always reply with at least an empty/zero-result message. Treat "no ExecutionReports arrived" as your zero-match signal for this one request type specifically.
  • OrderCancelReplaceRequest can only amend Price and OrderQty — not TimeInForce or StopPx, even if sent.
  • ResendRequest never replays real message history — it always answers with a gap-fill (SequenceReset, GapFillFlag=Y). See section 13 for how to recover state instead.
  • No numeric MDReqRejReason on MarketDataRequestReject — only free-text Text(58).
  • DisplayQty/MinQty are accepted and echoed, but not otherwise enforced by the gateway itself beyond passing them through to the account/exchange as submitted.
  • No multi-leg, quoting, or allocation workflows: NewOrderMultileg, NewOrderCross, MassQuote, QuoteCancel, allocation instructions, and similar are not implemented. A recognized-but-unsupported MsgType gets BusinessMessageReject(j), BusinessRejectReason=3.

Appendix A — Tag Index

Every tag this gateway reads or writes, numeric order. "Req'd" means required somewhere in this document — see the relevant section for the exact conditions.

Tag Name Used in
1 Account ExecutionReport
6 AvgPx ExecutionReport
7 BeginSeqNo ResendRequest
8 BeginString header (all messages)
9 BodyLength header (all messages)
10 CheckSum trailer (all messages)
11 ClOrdID NewOrderSingle, OrderCancelRequest, OrderCancelReplaceRequest, OrderStatusRequest, ExecutionReport, OrderCancelReject
14 CumQty ExecutionReport
15 Currency (accepted, not enforced)
17 ExecID ExecutionReport, TradeCaptureReport
21 HandlInst NewOrderSingle (accepted, not enforced)
31 LastPx ExecutionReport, TradeCaptureReport
32 LastQty ExecutionReport, TradeCaptureReport
34 MsgSeqNum header (all messages)
35 MsgType header (all messages)
36 NewSeqNo SequenceReset
37 OrderID most order/trade messages
38 OrderQty NewOrderSingle, OrderCancelReplaceRequest, ExecutionReport
39 OrdStatus ExecutionReport, OrderCancelReject
40 OrdType NewOrderSingle, ExecutionReport
41 OrigClOrdID OrderCancelRequest, OrderCancelReplaceRequest, OrderCancelReject
44 Price NewOrderSingle, OrderCancelReplaceRequest, ExecutionReport
45 RefSeqNum Reject, BusinessMessageReject
49 SenderCompID header (all messages)
52 SendingTime header (all messages)
54 Side NewOrderSingle, OrderMassStatusRequest, OrderMassCancelRequest, ExecutionReport, TradeCaptureReport, MarketDataRequest filter
55 Symbol most messages
56 TargetCompID header (all messages)
58 Text Logout, Reject, BusinessMessageReject, OrderCancelReject, MarketDataRequestReject, TradeCaptureReportRequestAck
59 TimeInForce NewOrderSingle, ExecutionReport
60 TransactTime NewOrderSingle (accepted, not read), ExecutionReport, TradeCaptureReport
98 EncryptMethod Logon
99 StopPx NewOrderSingle, ExecutionReport
102 CxlRejReason OrderCancelReject
103 OrdRejReason ExecutionReport
108 HeartBtInt Logon
110 MinQty NewOrderSingle, ExecutionReport
112 TestReqID TestRequest, Heartbeat
123 GapFillFlag SequenceReset
146 NoRelatedSym MarketDataRequest, SecurityList
150 ExecType ExecutionReport
151 LeavesQty ExecutionReport
167 SecurityType SecurityDefinition
207 SecurityExchange SecurityList, SecurityDefinition
231 ContractMultiplier SecurityDefinition
262 MDReqID MarketDataRequest, MarketDataSnapshotFullRefresh, MarketDataIncrementalRefresh, MarketDataRequestReject
263 SubscriptionRequestType MarketDataRequest, RequestForPositions
264 MarketDepth MarketDataRequest
267 NoMDEntryTypes MarketDataRequest
268 NoMDEntries MarketDataSnapshotFullRefresh, MarketDataIncrementalRefresh
269 MDEntryType MarketDataRequest, MarketDataSnapshotFullRefresh, MarketDataIncrementalRefresh
270 MDEntryPx MarketDataSnapshotFullRefresh, MarketDataIncrementalRefresh
271 MDEntrySize MarketDataSnapshotFullRefresh, MarketDataIncrementalRefresh
279 MDUpdateAction MarketDataIncrementalRefresh
320 SecurityReqID SecurityListRequest, SecurityList, SecurityDefinitionRequest, SecurityDefinition
322 SecurityResponseID SecurityDefinition
325 UnsolicitedIndicator PositionReport
372 RefMsgType BusinessMessageReject
373 SessionRejectReason Reject
380 BusinessRejectReason BusinessMessageReject
393 TotNoRelatedSym SecurityDefinition
434 CxlRejResponseTo OrderCancelReject
487 TradeReportTransType TradeCaptureReport
553 Username Logon
554 Password Logon
559 SecurityListRequestType SecurityListRequest
568 TradeRequestID TradeCaptureReportRequest, TradeCaptureReport, TradeCaptureReportRequestAck
569 TradeRequestType (accepted, not enforced)
571 TradeReportID TradeCaptureReport
584 MassStatusReqID (accepted, not enforced)
585 MassStatusReqType (accepted, not enforced — no scoping beyond Symbol/Side)
702 NoPositions PositionReport
703 PosType PositionReport
704 LongQty PositionReport
705 ShortQty PositionReport
707 PosAmtType PositionReport
708 PosAmt PositionReport
710 PosReqID RequestForPositions, PositionReport
715 ClearingBusinessDate PositionReport
721 PosMaintRptID PositionReport
724 PosReqType (accepted, not enforced)
727 TotalNumPosReports PositionReport
728 PosReqResult PositionReport
748 TotNumTradeReports TradeCaptureReport, TradeCaptureReportRequestAck
753 NoPosAmt PositionReport
912 LastRptRequested TradeCaptureReport, TradeCaptureReportRequestAck
969 MinPriceIncrement SecurityDefinition
1023 MDPriceLevel MarketDataIncrementalRefresh (full-depth entries only)
1138 DisplayQty NewOrderSingle, ExecutionReport
1148 LowLimitPrice SecurityDefinition
1149 HighLimitPrice SecurityDefinition

Appendix B — Enumerations Reference

OrdType (40)

Value Meaning
1 Market
2 Limit (also Limit+IOC when combined with TimeInForce=3)
3 Stop
4 Stop Limit

TimeInForce (59)

Value Meaning Supported?
0 Day ✅ (default)
1 GTC ❌ rejected
2 At the Opening ❌ rejected
3 IOC ✅ (native FAK)
4 FOK ❌ rejected
5 GTX ❌ rejected
6 GTD ❌ rejected

Side (54)

Value Meaning
1 Buy
2 Sell

ExecType (150) / OrdStatus (39)

See the full table with context in section 5. Codes used: A PendingNew, 0 New, 6 PendingCancel, 4 Canceled, E PendingReplace, 5 Replaced, 8 Rejected, 2 Filled, 1 PartiallyFilled.

OrdRejReason (103)

Present on ExecutionReport only when ExecType=8. Two different sources feed this tag:

  • Gateway pre-trade check (synchronous, on your own NewOrderSingle): can currently produce 1, 3, 11, 13, or 16 from the table below, or 0 as a generic fallback for anything else the check catches.
  • Exchange itself, asynchronously (a NewOrderSingle that was accepted and forwarded, or a cancel/replace the exchange later rejects — see the note in section 5): passes through the exchange's own reason code directly and unclamped. The exchange doesn't publish a single standard value table for this field (its real reject-code references are organized per market segment), so treat any value here as the exchange's own code, not necessarily one of the standard FIX values below.

The table below is the standard FIX 4.4 enum, useful as a reference for the gateway pre-trade check values above — it is not an exhaustive list of what the exchange itself may send asynchronously:

Value Meaning
0 Broker / Exchange Option
1 Unknown symbol
2 Exchange closed
3 Order exceeds limit
4 Too late to enter
5 Unknown Order
6 Duplicate Order
7 Duplicate of a verbally communicated order
8 Stale Order
9 Trade Along required
10 Invalid Investor ID
11 Unsupported order characteristic
12 Surveillance Option
13 Incorrect quantity
14 Incorrect allocated quantity
15 Unknown account(s)
16 Price exceeds current price band
18 Non-displayed limit order not allowed on this market
19 Order not risk-tested
99 Other

CxlRejReason (102)

Present on OrderCancelReject. Unlike OrdRejReason above, this tag is only ever produced synchronously by the gateway/account (a rejected cancel/replace that the exchange itself rejects asynchronously surfaces as an ExecutionReport with OrdRejReason instead — see the note in section 5).

Value Meaning Actually sent by this gateway?
0 Too late to cancel
1 Unknown order ✅ (both a session-scoped lookup miss, and a genuine backend "order not found")
2 Broker / Exchange Option ✅ (generic fallback for a backend rejection not covered by another value)
3 Order already in Pending Cancel or Pending Replace status
4 Unable to process Order Mass Cancel Request not currently produced
5 OrigOrdModTime did not match last TransactTime of order not currently produced
6 Duplicate ClOrdID received
99 Other ✅ (VIEWER-role rejects, and any backend reason not covered above — see Text(58))

BusinessRejectReason (380)

Value Meaning Actually sent by this gateway?
0 Other ✅ (fallback for an unrecognized MsgType, see 3 below)
1 Unknown ID ✅ (OrderStatusRequest for an unknown order)
2 Unknown Security ✅ (unknown Symbol on NewOrderSingle)
3 Unsupported Message Type ✅ (default for any unrecognized MsgType)
4 Application not available not currently produced
5 Conditionally Required Field Missing not currently produced
6 Not authorized ✅ (VIEWER role on OrderMassCancelRequest)
7 DeliverTo firm not available at this time not currently produced
(other values) Exchange-defined (throttle limits, kill switch, risk/credit controls, etc.) ✅ — an unsolicited exchange-side error tied to an order passes the exchange's own code through directly, unclamped; values above 7 are the exchange's own, not a standard FIX 4.4 code.

SessionRejectReason (373)

Only value 99 (Other) is currently produced by this gateway, for any application message received before Logon completes. The full standard enum exists for completeness if your engine validates against it, but no other value will be sent.

SecurityType (167)

Value Meaning
FUT Future
OPT Option
MLEG Multi-leg / spread

PosType (703) / PosAmtType (707)

PosType is always "TOT" (Total Transaction Qty). PosAmtType is always "FMTM" (Final Mark-to-Market).

MDEntryType (269)

Value Meaning
0 Bid
1 Offer

MDUpdateAction (279)

Value Meaning
0 New
1 Change
2 Delete