Frontend Labs
⋯

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

  1. 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
    
  2. 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.
  3. 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.
  4. 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.
  5. 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.

Generated from real-life-system-design-guide.md · 8 Oct 2026 16:36