FIX protocol and market connectivity
How orders and market data actually move between you and a venue. FIX is the incumbent in institutional equities, futures and forex; crypto and retail brokers mostly expose REST plus WebSocket instead.
What FIX is
Financial Information eXchange: a session-based, tag-value message protocol over TCP, standard since the 1990s for order routing and execution reporting.
8=FIX.4.4|9=145|35=D|49=CLIENT|56=BROKER|34=215|52=20260810-09:15:00.123|
11=ORD-1001|55=AAPL|54=1|38=100|40=2|44=185.50|59=0|10=094|
Each field is tag=value separated by SOH (0x01, rendered as | above). The tags that matter:
| Tag | Meaning |
|---|---|
| 8 / 9 / 10 | BeginString, BodyLength, CheckSum — the framing |
| 35 | MsgType — D new order, F cancel, G replace, 8 execution report, 0 heartbeat |
| 49 / 56 | SenderCompID / TargetCompID — who is talking to whom |
| 34 | MsgSeqNum — the sequence number the session is built on |
| 11 / 41 | ClOrdID / OrigClOrdID — your order id, and the previous one on a replace |
| 55 / 54 / 38 / 40 / 44 | Symbol, Side, OrderQty, OrdType, Price |
| 39 / 150 | OrdStatus / ExecType — where the order is, and what just happened |
The session layer is the hard part
FIX is two layers, and interviews focus on the session one because that is where production incidents live.
Sequence numbers are the state. Every message carries an incrementing MsgSeqNum. Receiving one higher than expected means messages were lost, and you send a Resend Request; receiving one lower is a protocol error that usually means someone reset a sequence file. Both sides persist their sequence numbers to disk, because after a crash you must resume where you left off, not from 1.
Logon, heartbeat, logout. The session opens with a Logon that negotiates a heartbeat interval. Silence past that interval triggers a Test Request, and no response drops the session. Sessions typically reset daily at a scheduled time.
PossDup and PossResend. Replayed messages are flagged. Your application must be idempotent on them — acting twice on a resent execution report is how you end up with a phantom position.
Reconciliation. After any disconnect you re-request order status rather than assuming your in-memory view is correct. An order can fill while you are disconnected.
In Python
quickfix (SWIG bindings over QuickFIX/C++) is the reference implementation; simplefix is a lightweight parser when you only need to build and read messages. There is also asyncfix and broker-specific SDKs.
import quickfix as fix
class App(fix.Application):
def onLogon(self, sessionID): ...
def onMessage(self, message, sessionID): ... # application messages
def fromApp(self, message, sessionID):
msgType = fix.MsgType(); message.getHeader().getField(msgType)
if msgType.getValue() == fix.MsgType_ExecutionReport:
self.handle_execution(message)
The honest engineering note: latency-sensitive FIX engines are not written in Python. Python is entirely reasonable for order management, reconciliation, position keeping and strategy logic at human timescales; sub-millisecond path work is C++ or Rust. Saying that distinguishes you from a candidate who claims Python for everything.
The alternatives you will meet
| Transport | Where |
|---|---|
| FIX | institutional equities, futures, forex; broker order routing |
| REST | crypto exchanges, retail brokers, reference data, historical bars |
| WebSocket | live market data and order updates on crypto and modern retail APIs |
| Binary/proprietary (ITCH, OUCH, SBE) | exchange-direct feeds and order entry where latency matters |
| gRPC | increasingly for internal service-to-service in trading infrastructure |
For crypto, the practical concerns are per-exchange rate limits, HMAC request signing, clock skew (many exchanges reject a request whose timestamp drifts), and reconnect logic that resubscribes and re-syncs the order book snapshot rather than assuming the delta stream is still coherent. See ../12_protocols/websockets/.
Idempotency and order state
Whatever the transport, the invariants are the same:
- A client-generated order id (
ClOrdID) on every request, so a retry cannot create a second order. This is the idempotency-key pattern applied to trading. See ../../system_design/02_resilience/01_timeouts_retries_backoff.md. - Never retry a new-order request blindly on timeout — you do not know whether it arrived. Query status first.
- Treat the venue as the source of truth for positions and reconcile against it, rather than trusting your own accumulated view.
Interview angle
- “What is FIX and what are its two layers?” - a tag-value message protocol for order routing and execution reporting. The session layer handles sequence numbers, heartbeats, logon and resend; the application layer carries orders and executions. Production problems are almost always session-layer.
- “What happens if you miss a message?” - the sequence number gap tells you, and you send a Resend Request. This is why both sides persist sequence numbers - after a crash you resume, you do not reset.
- “How do you make order submission safe under retries?” - a client-generated order id on every request, and never blind-retry a new order on timeout; query status first, because the order may already be live.
- “Would you write a FIX engine in Python?” - the session engine for a latency-sensitive path, no - that is C++ or Rust. Python is right for the order management, reconciliation and strategy layers above it, and
quickfixbindings are fine there. - “How is connecting to a crypto exchange different?” - REST plus WebSocket rather than FIX, HMAC-signed requests, strict rate limits, and clock-skew rejection. Reconnect must resubscribe and take a fresh order-book snapshot, not resume the delta stream.