Parsing FIX and ITCH in the Same Order Pipeline
Two protocols, one truth
Operating an exchange or order management system (OMS) requires interfacing with two fundamentally different protocol paradigms:
- FIX (Financial Information eXchange): A session-oriented, ASCII-encoded tag-value protocol used for client order entry and execution reporting (e.g.,
8=FIX.4.2|35=D|49=CLIENT|56=SERVER|11=ORD1001|...). It is self-describing, human-readable, and run over TCP, but carries high bandwidth overhead and parsing latency. - NASDAQ ITCH: A compact, fixed-width binary protocol broadcast over UDP multicast for real-time market data distribution. It uses big-endian byte-aligned structs designed for maximum data density and minimal network packet overhead.
An order engine that accepts incoming client orders via FIX while simultaneously consuming market data depth and trade events from an ITCH multicast feed must process both protocols in real time. Allowing protocol-specific structures, tag numbers, or raw binary offsets to leak into downstream business logic creates fragile, coupled code that is difficult to test and maintain.
Normalizing at the edge
To isolate our matching engine and order state machine from protocol mechanics, we implemented an edge normalization pattern. Incoming protocol streams are parsed at the network boundary by dedicated parsers — FixMessageParser for ASCII tag-value streams and ItchMessageParser for binary multicast packets.
graph TD
FIX["FIX Protocol Client<br/>(ASCII Tag-Value over TCP)"] --> FixParser["FixMessageParser"]
ITCH["ITCH Market Feed<br/>(Binary Struct over UDP Multicast)"] --> ItchParser["ItchMessageParser"]
FixParser -->|Normalize| Event["Normalized OrderEvent DTO"]
ItchParser -->|Normalize| Event
Event --> Core["Matching Engine Core & Risk Manager"]

Both parsers translate raw incoming data into a single, immutable domain DTO: the OrderEvent. Everything downstream — order book state updating, risk limits, audit logging, and WebSocket broadcasting — interacts exclusively with normalized OrderEvent instances.
// Simplified representation of the edge normalization interface
public interface ProtocolParser<T> {
OrderEvent parse(T rawInput);
}
// FIX ASCII tag-value parser implementation
public class FixMessageParser implements ProtocolParser<String> {
@Override
public OrderEvent parse(String rawFix) {
FixMap fields = FixTagValueParser.parse(rawFix);
return new OrderEvent(
fields.get(11), // ClOrdID (Tag 11)
Side.fromFix(fields.get(54)), // Side (Tag 54)
Price.fromDecimal(fields.get(44)),// Price (Tag 44)
fields.getLong(38), // OrderQty (Tag 38)
EventType.NEW_ORDER,
System.nanoTime()
);
}
}
// ITCH fixed-width binary parser implementation
public class ItchMessageParser implements ProtocolParser<ByteBuffer> {
@Override
public OrderEvent parse(ByteBuffer buffer) {
byte messageType = buffer.get(); // Byte 0: Message Type ('A' = Add Order)
short stockLocate = buffer.getShort();// Bytes 1-2: Stock Locate
int trackingNumber = buffer.getInt(); // Bytes 3-6: Tracking Number
long timestamp = buffer.getLong(); // Bytes 7-14: Timestamp (nanoseconds)
long orderReference = buffer.getLong();// Bytes 15-22: Order Reference Number
byte buySellIndicator = buffer.get(); // Byte 23: Buy/Sell Indicator ('B'/'S')
int shares = buffer.getInt(); // Bytes 24-27: Shares
int stock = buffer.getInt(); // Bytes 28-31: Stock Symbol ID
int price = buffer.getInt(); // Bytes 32-35: Price (4 decimal implied)
return new OrderEvent(
String.valueOf(orderReference),
Side.fromItch(buySellIndicator),
Price.fromFixedPoint(price, 4),
shares,
EventType.ADD_ORDER,
timestamp
);
}
}
Why binary parsing needed its own discipline
Text-based formats like JSON or FIX fail loudly when a tag is missing or malformed, throwing explicit syntax exceptions. Binary protocols like ITCH provide no such safety net.
Because ITCH messages rely on fixed byte alignments, a single off-by-one error in a buffer offset calculation does not trigger a runtime exception. Instead, the parser silently reads adjacent bytes, interpreting a 4-byte integer price offset by a single byte as a garbage numerical value (such as 4294967295), corrupting order book state without producing errors in application logs.
Furthermore, exchange specification documentation often drifts from real production behavior. During integration testing against live exchange test feeds, we discovered undocumented optional vendor extension bytes and sequence header padding that differed slightly from official PDF documentation.
To guarantee zero-drift parsing accuracy, we established strict testing guidelines:
- Fixture-Based Packet Capture (PCAP) Tests: We captured raw PCAP packet dumps from live exchange multicast feeds and built an automated test suite that replays raw byte buffers through
ItchMessageParser, asserting exact DTO output. - Off-Heap Direct Memory Buffers: We utilized direct Java
ByteBufferinstances allocated off-heap. This avoided unnecessary byte array copying and prevented Garbage Collection (GC) pauses during market data volume spikes. - Zero-Allocation Object Pooling: To achieve sub-microsecond processing latencies,
OrderEventDTO instances are reused via thread-local object pools rather than instantiated on every incoming network packet.
Results and architectural impact
Decoupling protocol parsing from our core order processing pipeline yielded tangible architectural benefits:
- Protocol Interoperability: Adding support for a new exchange feed (such as SBE or Ouch) requires writing a single isolated parser implementing
ProtocolParserwithout modifying a single line of order book logic. - Deterministic Latency: Off-heap binary parsing combined with object pooling reduced edge normalization p99 latency to under 450 nanoseconds per message.
- Simplified Testing: Downstream components can be thoroughly unit tested by passing mock
OrderEventDTOs directly, eliminating the need to spin up full FIX session engines or mock UDP multicast sockets during daily developer test runs.