System Design Library

Stock Exchange (Matching Engine)

Match buy/sell orders fairly with microsecond latency and strict ordering.

Open the interactive version → diagrams, practice & more

Requirements

Functional

  • Order book
  • Match orders (price-time priority)
  • Cancel/modify
  • Market data feed

Non-functional

  • Deterministic & ordered
  • Ultra-low latency
  • Fair

Scale

Millions of orders/sec

The approach

A single-threaded in-memory matching engine per symbol (determinism + no lock contention) processes a strictly ordered input log; matches by price-time priority; an event-sourced log enables replay/recovery; market data fans out.

Key components

Gateway → sequenced input log → per-symbol matching engine → trade + market-data feeds

Numbers that matter

Senior deep-dive

The matching engine must be single-threaded — deterministic price-time priority requires a strict total order on events, which is impossible with concurrent mutation of the order book.

The input log is the database: the order book is reconstructed from an ordered event log; the matching engine is stateless given the log, enabling replay, debugging, and disaster recovery without a traditional DB.

Latency is measured in microseconds and dominated by kernel bypass: a TCP stack adds 50–100μs; DPDK/RDMA brings this to <5μs; the matching itself (a price-level sorted map lookup) takes <1μs with a cache-warm order book.

Single-threaded matching: why concurrency is wrong here

Price-time priority (best price wins; equal price → earlier order wins) requires a strict total order on all events for a symbol. Any concurrent access to the order book creates races: two simultaneous matching decisions for overlapping price levels produce non-deterministic fills. The solution is a single-threaded event loop per instrument — incoming orders are serialized onto an input queue, the engine processes them one at a time, emits execution reports, and never blocks. Throughput comes from CPU frequency, cache efficiency, and lock-free queues, not parallelism.

Order book data structure

The order book is a price-level map: a sorted map (by price) where each level holds a FIFO queue of orders. For a limit buy at $100.01, the engine looks up the ask side for any resting order at ≤$100.01 — this is a min-heap or sorted map lookup. Array-backed price levels (a fixed-size array indexed by price in ticks) outperform `std::map` for liquid instruments with a narrow spread — O(1) level access vs O(log n). The entire book for a liquid instrument fits in L3 cache, which is why cache-warm throughput is orders of magnitude higher than cold.

Event sourcing: the log is the database

The matching engine does not write to a database during matching — it appends order events (new, cancel, fill) to a durable log. The order book is the projection of that log. On restart, replay the log to rebuild state. This is not just a design philosophy — it is how exchanges satisfy regulatory audit requirements: the regulator can request the event log and independently reconstruct every fill. The log also enables backtesting and market simulation without a separate testing environment.

Market data: SBE and multicast

Exchange market data feeds use Simple Binary Encoding (SBE) — a fixed-layout binary format with no heap allocation, parseable in a few nanoseconds. Data is broadcast via UDP multicast to subscribers who co-locate servers in the exchange's data center. Retailers receive a slower consolidated tape via TCP. The matching engine publishes fill and order book update events atomically with the matching decision — sequence numbers on the feed let consumers detect gaps and request retransmit via a separate request/response channel.

Risk and pre-trade checks

Pre-trade risk (position limits, order size limits, price collars) must run in <1μs or it becomes the latency bottleneck. This is implemented as a set of in-memory check tables updated asynchronously — the risk engine reads stale position data (last-known, updated every few milliseconds) rather than computing exact real-time exposure. Post-trade risk (exact exposure) runs in a separate process after the fill. The 2010 Flash Crash was partly attributed to risk systems not keeping pace with order flow — circuit breakers (limit-up/limit-down bands) are now a regulatory requirement.

What breaks at scale

Clock synchronization failures produce misordered timestamps: if two co-located servers have 500ns of clock skew and both submit orders in the same microsecond, the time-priority ordering is wrong. PTP hardware timestamping solves this, but requires supported NICs and careful switch configuration — software NTP is not sufficient. The second failure mode is input queue saturation: at auction open, order flow spikes 10–100x; the engine's input queue must be deep enough to absorb the burst, and the queue itself must be lock-free (Disruptor pattern) or the queue-write becomes the bottleneck.

In production

LMAX Disruptor is the most widely studied open-source pattern for exchange-grade throughput: a lock-free ring buffer replaces queues between pipeline stages (ingest → match → risk → publish), achieving 6 million orders/second on a single core without garbage collection. CME Group's Globex and ICE both run separate matching engines per product with no cross-product coordination — instrument isolation is the scalability lever. The real challenge is deterministic replay: every exchange must be able to reconstruct the exact order book state from the event log for regulatory audit and post-trade reconciliation. This requires that the log is the ground truth — any in-memory state is derived. Clock synchronization is a regulatory requirement (MiFID II mandates <100μs timestamp accuracy) and is achieved with PTP/IEEE 1588 hardware timestamping on the NIC, not OS clocks.

Common mistakes

Related System Design Library

Part of System Design Library on SystemLore — system design interview prep with 148 deep topics, interactive diagrams, and a practice game. Practice this one →