Design a Stock Exchange Matching Engine
1. Problem Statement & Scope Clarification
System Mission
Design an ultra-low latency, deterministic electronic stock exchange matching engine (equivalent to NASDAQ, CME, or LMAX Exchange). The system must process hundreds of thousands of limit and market orders per second with strict Price-Time Priority (FIFO), sub-50 microsecond () matching latencies, zero lock contention via a single-writer architecture, zero memory allocations on the critical hot path, and sub-millisecond hot-standby failover with determinism.
Matching Engine Performance Specifications
| Measure | Value |
|---|---|
| Peak Throughput | 200,000 Orders/s |
| Matching Latency | P99 < 50 μs |
| Hot Path Allocations | Exactly 0 |
| Priority Rule | Price-Time (FIFO) |
| In-Memory Book Size | ~3.2 GB RAM |
| Concurrency | Single-Writer |
| Ring Buffer | LMAX Disruptor Pattern |
| Kernel Bypass | DPDK / ENA Express |
| Wire Protocol | ITCH / OUCH / SBE |
| Journaling | NVMe SPDK Append-Only |
| Market Data | UDP Binary Multicast |
| Failover RPO | 0 (Deterministic) |
Functional Requirements
- Order Ingestion (
EnterOrder): Parse, validate, and sequence incoming Limit, Market, and Cancel orders using compact binary wire protocols (Simple Binary Encoding - SBE / OUCH). - Deterministic Price-Time Priority (FIFO) Matching Engine:
- Price Priority: Orders at better prices (higher buy bids, lower sell asks) take strict precedence.
- Time Priority: Orders at the exact same price level are executed in strict arrival sequence order (First-In, First-Out).
- Constant-Time Cancellation (
CancelOrder): Cancel or modify existing resting limit orders in time complexity using direct memory pointers. - Real-Time Market Data Multicast (ITCH Protocol): Disseminate real-time binary market data feeds:
- Level 2 (Aggregated Depth): Top 10–50 bids and asks with aggregated share quantities.
- Level 3 (Order-by-Order Tick Data): Full visibility into every resting order state change.
- Decoupled Post-Trade Clearing & Settlement: Asynchronously emit trade execution drops to downstream clearing houses, regulatory reporting systems, and member firm risk engines without stalling the core matching loop.
Non-Functional Requirements (SLAs & SLOs)
- Latency (P99):
- Wire-to-Wire Matching Latency: , , .
- Order Cancellation Latency: .
- Determinism: mathematical reproducibility. Replaying identical sequenced input events through a fresh matching engine instance must generate the exact same trade output tape bit-for-bit.
- Throughput & Capacity: Sustain per symbol partition on a single pinned CPU core, handling up to .
- Zero Lock Contention: Completely eliminate OS-level thread synchronization primitives (mutexes, semaphores, conditional variables) from the matching hot path using lock-free ring buffers and core isolation.
- Availability & Durability: uptime SLA; Zero lost transactions (, Recovery Point Objective) with sub-millisecond hot-standby takeover (, Recovery Time Objective).
2. Capacity & Scale Estimation (Back-of-the-Envelope Math)
Ingestion Scale & Order Volume
- Listed Tradable Instruments: (Equities, ETFs, Indices).
- Daily Ingestion Volume: (New, Cancel, Replace).
- Daily Executed Trades: ( execution ratio; of orders are cancellations or unexecuted limit quotes).
- Average Trading Day: 6.5 hours ().
- Average Order Ingestion Rate:
- Peak Order Ingestion Rate ( market open/close burst):
Microsecond Latency Budget Breakdown (Target)
Synthesizing vector architecture diagram...
This diagram shows the six stages that every order passes through, in order, with the time budget of each stage. The order is read from the network card ring (10 μs), stamped with a sequence number by the sequencer (5 μs), placed in the Disruptor ring buffer (2 μs), matched by the single matching core (8 μs), written to the NVMe journal through SPDK (15 μs), and finally published as UDP multicast market data (10 μs). The stages run one after another for the same order, so their budgets add up: 10 + 5 + 2 + 8 + 15 + 10 = 50 μs, which is the end-to-end target. The largest single cost is the journal write (15 μs), and it happens before the result is published, so a published trade is always already durable.
| Subsystem Processing Stage | Latency Budget | Engineering Implementation Technique |
|---|---|---|
| 1. NIC Ingress & Kernel Bypass | AWS ENA Express + DPDK (Data Plane Development Kit) polling | |
| 2. Deterministic Monotonic Sequencer | Pinned CPU core assigning atomic 64-bit integer sequence ID | |
| 3. LMAX Lock-Free Ring Buffer | Cache-line aligned circular buffer (alignas(64)); zero false sharing | |
| 4. In-Memory Order Book Match | Direct-indexed price levels + intrusive doubly-linked list in L3 cache | |
| 5. NVMe Journaling (Fast Journal) | SPDK (Storage Performance Dev Kit) zero-copy direct NVMe write | |
| 6. Market Data UDP Multicast | Binary ITCH Level 2 packet serialization and kernel-bypass NIC broadcast | |
| Total End-to-End P99 Latency | Achieved via zero-allocation C++/Rust with CPU core pinning |
In-Memory Order Book RAM Sizing
- Average active resting orders across all 10,000 symbols: .
- Total live resting orders across the exchange:
- Each order struct is padded to exactly 64 bytes (1 CPU cache line):
- Price level nodes ().
- Total In-Memory Working Set: . The entire global order book fits comfortably inside the CPU L3 cache and physical DDR5 RAM of an Amazon EC2
c6i.32xlargeorc6in.metalbare-metal instance.
3. High-Level Architecture & AWS Component Mapping
The exchange architecture follows the LMAX Disruptor Single-Writer Pattern: all thread concurrency, locking, and socket contention are eliminated by funnelling incoming network packets through an atomic sequencer into a cache-line aligned lock-free ring buffer, serviced by a single dedicated CPU core running the matching algorithm.
Synthesizing vector architecture diagram...
Follow an order from the top. It arrives over Direct Connect and is read straight from the network card by a polling thread that bypasses the kernel, saving microseconds. In the "Deterministic Sequencing Domain" panel, one pinned CPU core gives every order the next sequence number and writes it into a lock-free ring buffer. In the "Single-Writer Matching Core Domain" panel, a single core owns the entire order book in memory and matches orders by price, then time; because only one thread ever writes, there are no locks. Every sequenced event is journaled to local NVMe and mirrored to a hot standby on another host, which replays the same events in the same order and can take over with no data loss. In the "Low-Latency Market Data & Post-Trade Settlement" panel, results go out as UDP multicast to all participants at once, and trades are sent asynchronously through SQS to settlement in Aurora, well away from the latency-critical path. Determinism is the key: the same input sequence always produces the same book, which makes the standby and replay possible.
Data Flow Walkthrough
- Network Ingress via Kernel Bypass: Inbound order packets bypass the Linux operating system kernel network stack entirely using DPDK and AWS ENA Express. Ingress packets move directly from the physical network interface controller (NIC) into user-space memory buffers without context switches or interrupts.
- Deterministic Sequencing: The single-threaded Sequencer pinned to CPU Core 2 receives the binary packet, validates account credentials, assigns an unalterable, gapless 64-bit sequence identifier (
seq_id = 1004812), and inserts the order into the circular LMAX ring buffer. - Single-Writer Matching Engine: CPU Core 3, isolated via Linux
isolcpusandcgroups, polls the ring buffer continuously in a busy-spin loop ( thread wake-up latency). It matches the order against the in-memory double-sided order book using the Price-Time Priority algorithm. - Synchronous SPDK Fast Journaling: Before publishing executions, the trade event is persisted to local NVMe SSDs via SPDK (Storage Performance Development Kit), achieving microsecond-level durability. Concurrently, the exact sequenced input event (not the output) is mirrored to the Hot-Standby engine on a second bare-metal host, which executes it in lockstep; a standby on the same host would die with the same kernel panic it is meant to survive.
- UDP Multicast Dissemination: CPU Core 5 packages match events into compact binary ITCH packets and streams them over UDP multicast directly to market participants.
- Asynchronous Settlement Decoupling: Post-trade clearing and regulatory archival are offloaded asynchronously via Amazon SQS FIFO queues into Amazon Aurora PostgreSQL, completely insulated from the ultra-fast matching path.
Core Request Tracing Execution Walkthrough
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| Step 1 | Binary OUCH Enter Order packet arrives via AWS Direct Connect | ENA Express / DPDK User-Space Poller | Zero-copy DMA transfer into pre-allocated circular ring buffer | 35-byte binary packet ingested (, 0 heap allocs) |
| Step 2 | Monotonic sequencer validates client session & stamps sequence ID | Sequencer Core (CPU Core 2, pinned) | Assigns monotonic gapless counter (e.g. seq_id = 104812) | Order event pushed to LMAX Disruptor slot |
| Step 3 | Single matching thread dequeues next order from lock-free ring | LMAX Disruptor Ring Buffer | Atomic consumer barrier check; cache-line padded alignas(64) | Dequeued in with zero lock contention and 0ns wake-up |
| Step 4 | In-memory Price-Time Priority order book matching execution | Dedicated Matching Core (CPU Core 3, isolated) | Sweeps best ask price level ($185.51); unlinks matched maker order node | TradeMatch generated (100 shares filled against Order 201) |
| Step 5 | Synchronous fast journaling to local NVMe storage | SPDK NVMe Driver Core | Direct DMA append to local NVMe SSD raw block log with CRC32C trailer | Execution durable in append-only journal () |
| Step 6 | Lockstep replication to hot-standby matching engine | Standby Matching Engine (second host) | Sequenced order event mirrored over EFA / ENA Express before the primary publishes | Standby state synchronized in memory (, ) |
| Step 7 | Binary ITCH trade execution packet emitted via dual multicast | Market Data Broadcaster (CPU Core 5) | Serializes binary ITCH 'E' Executed packet to UDP Feed A & Feed B | Wire-to-wire trade notification disseminated () |
4. In-Memory Order Book Data Structures
To guarantee order insertions, order cancellations, and sub-microsecond price depth sweeps:
- Price Levels: Indexed via a Direct-Indexed Flat Array (for tick ranges close to current mid-market price) or a cache-conscious B-Tree sorted by price.
- Time Priority Queue: An Intrusive Doubly-Linked List of resting
Orderstructs at each price level. Insertion is at the tail; matching is from the head. - Cancellation Hash Map: A pre-allocated, flat Cache-Conscious Hash Table mapping
order_id -> Order*. This enables instantaneous cancellations by unlinking the order directly from its doubly-linked list without searching the book.
Synthesizing vector architecture diagram...
This diagram shows the AAPL order book at one moment. The best bid is $185.50 and the best ask is $185.51, so the bid-ask spread is $185.50 to $185.51. In the "AAPL bids: highest price first" panel, each price level holds its resting orders as a doubly linked list in arrival order: at $185.50, Order 101 (100 sh) arrived first and sits at the head, then Order 104 (500 sh), then Order 109 (50 sh) at the tail, for 650 shares in total; at $185.49, Order 102 (200 sh) is ahead of Order 105 (100 sh). In the "AAPL asks: lowest price first" panel, $185.51 holds Order 201 (150 sh) ahead of Order 203 (250 sh), and $185.52 holds Order 202 (1,000 sh). An incoming order is matched against the head of the best opposite level first, which is the price-time (FIFO) rule. The "Fast cancellation lookup map (pre-allocated flat array)" panel shows why a cancel is O(1): the map returns a pointer straight to the Order 104 node, and because the list is doubly linked, the node can be unlinked from its neighbours Order 101 and Order 109 without walking the list.
5. API Interface Design & Binary Wire Protocols
Stock exchanges do NOT use JSON or HTTP for order ingestion. They deploy compact binary protocols (e.g., NASDAQ OUCH for order entry and ITCH for market data feeds) to minimize serialization overhead and network packet sizes.
1. Order Entry Protocol: Enter Order Message (OUCH Binary Format)
| Field Name | Offset | Length (Bytes) | Data Type | Description |
|---|---|---|---|---|
MessageType | 0 | 1 | char | 'O' = Enter Order Message |
OrderReferenceNumber | 1 | 8 | uint64 | Client-assigned unique order identifier |
Side | 9 | 1 | char | 'B' = Buy, 'S' = Sell |
Quantity | 10 | 4 | uint32 | Number of shares requested |
Symbol | 14 | 8 | char[8] | Ticker symbol padded with spaces (e.g., "AAPL ") |
Price | 22 | 8 | uint64 | Fixed-point price ( precision; ) |
TimeInForce | 30 | 1 | char | 'D' = Day Order, 'I' = Immediate-Or-Cancel (IOC) |
FirmIdentifier | 31 | 4 | char[4] | MPID / Trading firm identifier (e.g., "GSCO") |
| Total Message Size | 35 Bytes | Padded to 64-byte alignment |
2. Market Data Protocol: Order Executed Message (ITCH Binary Format)
| Field Name | Offset | Length (Bytes) | Data Type | Description |
|---|---|---|---|---|
MessageType | 0 | 1 | char | 'E' = Order Executed Message |
SequenceNumber | 1 | 8 | uint64 | Monotonic exchange sequence counter |
OrderReferenceNumber | 9 | 8 | uint64 | Matched resting order ID |
ExecutedShares | 17 | 4 | uint32 | Number of shares filled |
MatchExecutionNumber | 21 | 8 | uint64 | Unique trade execution ID |
TimestampNanoseconds | 29 | 8 | uint64 | Hardware nanoseconds since midnight UTC |
| Total Message Size | 37 Bytes | Emitted over UDP Multicast |
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~37%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.