59. Walkthrough: Design a Stock Exchange Matching Engine
The prompt. "Design a stock exchange", or "Design the order matching engine for a trading platform / crypto exchange." It's asked at fintech companies, trading firms, and sometimes in general Staff+ loops, because it inverts the usual instincts: no horizontal scaling of the hot path, no eventual consistency, microsecond latency, and determinism above all.
Step 0 — Clarify the business goal
🎤 Candidate: For an exchange, the product is fairness and trust: strict price-time priority, the same market data to everyone at the same time, no lost or duplicated orders, and a full audit trail for regulators. Speed matters because market makers choose venues by latency and reliability, and their liquidity attracts everyone else. Are we a regulated equities exchange or a crypto exchange? And is clearing/settlement in scope?
🧑💼 Interviewer: A regulated equities exchange. Clearing is out of scope.
Step 1–2 — Requirements
| Functional | Non-functional |
|---|---|
| Order entry: limit, market, cancel, cancel/replace | Matching latency: p99 < 100 µs inside the engine; gateway-to-ack < 1 ms |
| Price-time priority matching; partial fills | Throughput: 1M orders/s peak across ~10k symbols |
| Pre-trade risk checks (credit limits, fat-finger, price bands) | Determinism: replaying the input log reproduces identical state |
| Market data: top of book (L1) and depth (L2), trades | Zero loss of acknowledged orders; failover in seconds with the book intact |
| Drop copies and audit trail to regulators | Fairness: equal treatment and equal information timing |
| Trading halts, opening/closing auctions | Availability during market hours ≈ 100% (scheduled maintenance outside hours) |
Step 3 — Estimates
1M orders/s peak; average message ≈ 100 B → ~100 MB/s of input to journal & replicate
Per symbol: highly skewed — top symbols take a large share of messages → a single hot symbol
can see 100k+ msgs/s → one core per hot symbol shard
Order book size: active orders per symbol: thousands to ~1M → fits in memory (MBs-GBs total)
Journal per day: 1M/s peaks, ~10B messages/day × 100 B ≈ 1 TB/day → sequential disk, cheap
Step 5 — High-level architecture
Members (brokers, market makers) ─FIX / binary protocol─▶ Order gateways (session auth, throttles,
message validation, client order IDs)
▼
Pre-trade risk (per member credit limits, price bands, max order size) ← in-memory, µs
▼
SEQUENCER ── assigns a global, gap-free sequence number + timestamp to every inbound event
│ journals to disk & replicates to standby BEFORE forwarding (durability point)
▼
Matching engine shards (partitioned by symbol; each shard SINGLE-THREADED, pinned core,
in-memory order books; consumes sequenced events in order; no locks, no wall-clock reads)
│ outputs: acks, executions, book changes (also sequenced)
├──▶ Order gateways → members (execution reports)
├──▶ Market data publisher → UDP multicast (L1/L2/trades) — same packets to everyone
├──▶ Drop copy / surveillance / regulatory audit trail
└──▶ Post-trade → clearing (out of scope)
Hot standby engines consume the SAME sequenced stream and compute identical state
(state-machine replication) → failover = promote standby, no book reconstruction
Step 6 — Deep dives
- Order book data structure:
bids: price levels sorted desc ┐ each level: FIFO doubly-linked list of orders (time priority) asks: price levels sorted asc ┘ orders_by_id: hashmap order_id → node (O(1) cancel) Price levels: array indexed by tick offset around the current price (dense, cache-friendly) or a balanced tree for wide/sparse ranges Match: incoming buy limit P → while best_ask ≤ P and qty > 0: fill against head of best level All prices and quantities are INTEGERS (ticks, lots) — never floats - Determinism and the sequencer: the engine is a pure function of the sequenced input. Time comes from the sequencer in the event, never from the engine's clock; there's no randomness and no multithreaded nondeterminism. That gives replay (rebuild state after a crash from a snapshot plus the journal), audit (regulators can reproduce any trade), and hot standbys that stay identical.
- Latency engineering: kernel-bypass networking (DPDK, Onload), cores pinned and isolated, busy-polling instead of interrupts, no allocation or GC on the hot path (pre-allocated pools, or GC-free Java), cache-friendly structures, lock-free single-producer/single-consumer ring buffers between stages, and members co-located in the same data center.
- Durability without losing microseconds: the sequencer journals to memory-mapped files and replicates to the standby before acknowledging. Acknowledged means present on two machines. Disk fsync batching is tuned for group commits.
- Fairness: price-time priority; multicast market data sent to everyone simultaneously; equal cable lengths in co-location. Some venues add deliberate speed bumps.
Step 7 — Failure modes
| Failure | Response |
|---|---|
| Matching engine host dies | Promote the hot standby (same state); members reconnect and resync via sequence numbers |
| Sequence gap on market data (UDP loss) | Clients detect the gap and request retransmission from a recovery service, or take a snapshot plus incremental updates |
| Member reconnects mid-order | Client order IDs make resubmits idempotent; the gateway replays missed execution reports from the sequence |
| Runaway algorithm floods orders | Per-session throttles, kill switches per member, price bands, market-wide circuit breakers |
| Bad deploy | Never mid-session: deploy outside market hours, replay the previous day's journal on the new version and compare outputs (deterministic diff testing) |
🌍 Real-world references
- LMAX Architecture (Martin Fowler's 2011 write-up): a single-threaded business logic processor handling millions of orders per second in memory, with the Disruptor ring buffer, event sourcing, and replay. It's the canonical reference for this design.
- IEX: introduced a 350-microsecond speed bump (a coil of fiber) to neutralize certain latency-arbitrage strategies. Fairness as a product feature, implemented physically.
- Regulatory timing: rules such as MiFID II require tightly synchronized, high-precision timestamps for high-frequency trading, which pushes exchanges to PTP time sync with microsecond-level accuracy.
- Robinhood, January 2021: as a broker (not an exchange), it had to restrict trading in some stocks because clearinghouse deposit requirements spiked. Business and regulatory constraints can dominate architecture.
⚠️ Corner cases
- Self-trade prevention: a firm's buy matches its own sell. Detect by member/account and cancel the newest, the oldest, or both, per policy.
- Market orders sweeping a thin book: they execute at absurd prices. Use price bands, limit up/limit down, and conversion to protected limit orders.
- Cancel/replace races: a cancel arrives after the order already filled. Respond deterministically ("too late to cancel") using sequence order.
- Iceberg orders: the hidden quantity replenishes, and each replenishment loses time priority. Implement it precisely, because members litigate fairness.
- Auctions: opening and closing call auctions use a different matching algorithm (maximize matched volume at one clearing price). A separate engine mode.
- Halts: must take effect at an exact sequence number so every party agrees on what traded before the halt.