The Financial Information eXchange (FIX) protocol remains the lingua franca of electronic trading. Despite the rise of native binary feeds and exchange-specific APIs, FIX still powers order entry, trade reporting, allocations, and many post-trade workflows. This article explains the practical parts of FIX you need to operate and debug production trading systems.
FIX is widely supported by brokers, ECNs, and most institutional counterparties. Its advantages are:
At the same time, FIX is text-based and verbose, which means parsing and resiliency patterns are important for production systems.
FIX is typically conceptualized in two layers:
Transport is commonly over TCP and often wrapped in TLS for encryption. Many firms place a FIX gateway or OEMS in front of their trading application to handle session-level concerns and provide journaling/replay.
A FIX message is a sequence of tag=value fields delimited by ASCII SOH (0x01). For readability we often show fields separated by | (pipe) — remember the real delimiter is SOH.
Example: a minimal NewOrderSingle (FIX 4.4 style)
8=FIX.4.4|9=0123|35=D|49=CLIENT12|56=BROKER1|34=2|52=20251112-12:34:56.789|11=ORDER123|55=ABC|54=1|38=100|40=2|44=10.50|59=0|10=128
Key fields in the example:
8 BeginString (protocol version)9 BodyLength (number of bytes in body)35 MsgType (D = NewOrderSingle)49 SenderCompID (client)56 TargetCompID (broker)34 MsgSeqNum (sequence number)52 SendingTime (timestamp)11 ClOrdID (client order id)55 Symbol54 Side (1 = buy, 2 = sell)38 OrderQty40 OrdType (2 = Limit)44 Price59 TimeInForce10 CheckSum (three-digit checksum)The real production message must calculate 9 (BodyLength) and 10 (CheckSum) correctly — many gateways and libraries do this automatically.
Fix sessions have explicit semantics to handle disconnection and out-of-order messages.
A typical session-management loop:
1# Pseudocode: minimal reliable sender loop (conceptual)
2out_seq = load_out_seq_from_persistent_store()
3pending_journal = load_unacked_messages(out_seq)
4
5# attempt to (re)establish session
6send(LogonMessage)
7
8# resend any un-acked messages if necessary
9for msg in pending_journal:
10 send(msg)
11
12while session_active():
13 event = poll_socket()
14 if event.type == MESSAGE:
15 if event.msg.SeqNum > in_seq + 1:
16 # gap detected
17 send(ResendRequest(start=in_seq+1, end=0))
18 elif event.msg.SeqNum == in_seq + 1:
19 process_application_message(event.msg)
20 in_seq += 1
21 elif event.type == SEND_ORDER:
22 msg = build_new_order(...)
23 msg.SeqNum = out_seq + 1
24 journal.append(msg) # persist to disk before send
25 send(msg)
26 out_seq += 1
27 persist_out_seq(out_seq)
28Key practical rule: persist outgoing messages and sequence numbers before acknowledging to your internal systems. That way, if you crash and restart, you can replay the journal and avoid duplicate/missing messages.
ClOrdID where possible and deduplicate by OrderID/ExecID on the server side.An ExecutionReport notifying a partial fill might look like:
8=FIX.4.4|9=0120|35=8|49=BROKER1|56=CLIENT12|34=10|52=20251112-12:35:03.123|37=EX123|11=ORDER123|17=FILL1|150=1|39=1|55=ABC|54=1|38=100|14=50|6=10.50|151=50|10=064
Important fields:
37 OrderID (broker-assigned)17 ExecID (execution id)150 ExecType (trade type)39 OrdStatus14 CumQty151 LeavesQtyProcessing an ExecutionReport must update order state and reconcile ClOrdID with local order objects.
FIX is foundational to electronic trading. The protocol's explicit session semantics (sequencing, resend, heartbeats) make it well-suited for robust order entry and post-trade workflows — provided you implement persistence, replay, and observability. For production systems, focus on correctness first (durable journaling, replay), then optimize for latency and throughput.
Technical Writer
NordVarg Team is a software engineer at NordVarg specializing in high-performance financial systems and type-safe programming.
Get weekly insights on building high-performance financial systems, latest industry trends, and expert tips delivered straight to your inbox.