The whole design, in writing
Learn system design by building a stock exchange / matching engine step by step. An interactive guide covering the order book and price-time priority, the matching engine, pre-trade risk checks, deterministic sequencing for low latency, market data feeds, journaling and replay, and clearing.
Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.
The big idea
What is a stock exchange?
Buyers and sellers submit orders; the exchange matches them into trades — fairly, deterministically, and in microseconds. Money is on the line, so it must never lose an order, never mismatch, and treat every participant equally. Correctness and latency, both absolute.
At the center is a matching engine running a single, ordered stream of events against an in-memory order book. Wrap it in admission, risk checks, deterministic sequencing, market-data broadcast, and a durable journal — keeping the core path tiny and fast.
What the new pieces do
- Traderclient
- Sends buy/sell orders and expects acknowledgement and fills in microseconds. Latency and fairness are the product — a slow exchange loses everyone.
Step 1 · The core
Match orders into trades
A trader sends “buy 100 at $50”. The exchange must instantly find a matching sell and execute, or rest the order until one appears. What structure makes that match fast and correct?
"Buy 100 at $50" must instantly find a match or wait for one. What structure makes that fast and correct?
Scanning every open order per incoming order is O(N) on the hottest path in the system — far too slow for microsecond matching. You need a structure sorted for instant best-price lookup.
A round-trip to disk-backed storage per order is orders of magnitude too slow, and SQL can’t express price-time matching cheaply. The book must live in memory, purpose-built.
Best bid and best ask sit at the top; matching crosses the spread — highest bid against lowest ask — until they no longer overlap. A market order takes best price; a limit order rests if it can’t match.
An Order Gateway admits the order to a Matching Engine that runs against an Order Book — resting bids and asks per symbol. A market order takes the best price; a limit order matches if possible, otherwise rests in the book.
What the new pieces do
- Order Gatewaybackend
- Authenticates the trader, validates the order, and admits it into the system. The boundary between the messy outside world and the pristine matching core.
- Matching Engineservice
- The heart: matches incoming orders against resting ones by price, then time. Kept single-threaded and in-memory so it’s deterministic and blazingly fast.
- Order Bookstore
- The live in-memory book of resting buy (bid) and sell (ask) orders per symbol, sorted for instant best-price lookup and matching.
Step 2 · The rules of matching
Price-time priority
When many orders want the same price, who trades first? Get this wrong and the market is unfair — and traders will (rightly) leave an exchange that doesn’t treat equal orders equally.
Many orders want the same price. Who trades first, fairly?
Favoring size lets big players jump the queue and punishes small orders — manifestly unfair, and traders will leave. Fairness can’t depend on order size.
Best price wins; among equal prices the earliest order fills first. Organizing the book as price levels, each a FIFO queue, builds the rule into the data structure — deterministic and auditable.
Randomness makes fills unpredictable and unauditable — a trader can’t reason about where they stand in line. Matching must be deterministic, so equal-priced orders need a stable order: time.
Match by price-time priority: best price wins, and among equal prices the earliest order fills first. The book is organized into price levels, each a FIFO queue, so the rule is built into the data structure.
- pricethen time
- FIFOper level
- deterministicfills
Back of the envelope
- sort by price, then time (FIFO)
- the matching rule is the data structure
- price levels = FIFO queues
- best bid / ask sit at the top
- deterministic, auditable fills
- every trader knows their place in line
Step 3 · Guard the book
Pre-trade risk checks
A trader could submit an order they can’t afford, or one that blows past position limits — a “fat finger” or a rogue algo. Letting that reach the book risks trades that can’t settle.
A trader could submit an order they can’t afford or that blows past limits (fat-finger, rogue algo). Where do you stop it?
Unwinding executed trades is disruptive, may be impossible once others traded against them, and corrupts the market. Bad orders must be stopped before they execute, not after.
Putting credit/position checks in the single-threaded hot loop adds latency and non-determinism to the core. Keep the matcher tiny and fast; validate before it.
Verify buying power, position/credit limits and price/size sanity at the gateway, rejecting violations immediately — so only valid orders ever touch the engine, and the hot loop stays fast and deterministic.
Insert a Risk Check before matching: verify buying power, position and credit limits, and sanity-check price/size. Reject violations immediately, so only valid orders ever touch the matching engine.
What the new pieces do
- Risk Checkguard
- Pre-trade gate: does the trader have the buying power and stay within position/credit limits? Rejects bad orders before they reach the book.
Step 4 · Make it deterministic
A single sequencer
Orders arrive concurrently from everywhere. For fairness, auditability, and the ability to run hot replicas, every component must agree on the exact order events were processed — and racing threads can’t guarantee that.
Orders arrive concurrently from everywhere. How do you make matching fair, auditable, and replica-safe?
Racing threads can’t guarantee the order events were processed, breaking fairness and making the engine non-reproducible — so replicas diverge. The core must process one agreed order.
A sequencer stamps every admitted order with a global sequence number, then a single thread processes that one stream — same input order, same output, on primary and every replica. Astonishingly fast in-memory, and perfectly reproducible.
Wall clocks differ across machines and tie at microsecond scale, so timestamps don’t give a single agreed order. You need one authoritative sequencer assigning a total order, not independent clocks.
Funnel all admitted orders through one Sequencer that assigns a global sequence number, then feed that single stream to a single-threaded matching engine. Same input order, same output, every time — on the primary and every replica.
- 1sequencer
- single-threadmatcher
- µsper match
What the new pieces do
- Sequencerservice
- Stamps every order with a single, global sequence number so all replicas process events in the exact same order — the basis of determinism and fairness.
Back of the envelope
- 1 sequencer ⇒ total order
- every replica sees the same input stream
- single-threaded, in-memory
- no shared-state races, µs per match
- same input ⇒ same output
- perfectly reproducible for failover
Step 5 · Tell the world
Market data feed
Every participant needs to see book updates and trades to make decisions — and they must all see them at the same time. Any latency advantage is an unfair edge worth millions.
Every participant needs book updates and trades — and a latency edge is worth millions. How do you distribute fairly?
Polling gives whoever polls fastest (or is closest) a systematic edge, and hammers the engine. Market data must be pushed equally, not pulled at each participant’s own rate.
Selling a latency advantage is exactly the unfairness a fair exchange must prevent — it lets some systematically see the market first. Delivery should be equal-latency for all.
The engine emits each change to a market feed (often multicast) so the same picture reaches everyone simultaneously. Distributing data fairly matters as much as matching fairly.
The engine emits every change to Market Data, broadcast over a Market Feed to all participants with equal-latency delivery (often multicast). The same picture reaches everyone simultaneously.
What the new pieces do
- Market Datastore
- The stream of book changes and executed trades. Every participant needs the same view of the market at the same time.
- Market Feedbus
- Publishes market data to all participants simultaneously, with equal-latency delivery so no one gets an unfair early peek.
Step 6 · Never lose a trade
Journal & replay
The order book lives in memory for speed — but memory is volatile. A crash without a record would lose orders and trades, which for an exchange is catastrophic and possibly illegal.
The order book lives in volatile memory for speed. How do you survive a crash without losing trades?
A snapshot every few seconds loses every order and trade since the last one — catastrophic and possibly illegal for an exchange. Every event must be durable before it takes effect.
Log first, apply second: on failure a replica replays the journal to reconstruct the exact book, and because the engine is deterministic the replay reproduces an identical result. The sequential write is cheap.
Synchronously copying full book state on every change is slow and still loses the in-flight event on a crash. Journaling the ordered event stream is cheaper and, with determinism, rebuilds state perfectly anywhere.
Journal every event to an append-only log before applying it. On failure, a replica replays the journal to reconstruct the exact book state and takes over. Because the engine is deterministic, replay reproduces the identical result.
What the new pieces do
- Journalstore
- A durable, ordered log of every event before it’s applied. Replaying it rebuilds the exact book state — the safety net behind an in-memory engine.
Back of the envelope
- append before apply
- the sequential journal write is cheap
- deterministic ⇒ replay = rebuild
- the log is a perfect recipe
- replica replays ⇒ takes over
- in-memory speed without losing data
Step 7 · After the match
Clearing, settlement & scale
A match is a promise, not finished money. Ownership and cash must actually change hands, and one engine can’t hold the order books for every symbol on earth.
Hand executed trades to Clearing & Settlement (T+1/T+2) off the hot path, so post-trade work never slows matching. Scale by partitioning symbols across matching engines — each symbol’s book is independent, so AAPL and TSLA run on different engines in parallel.
What the new pieces do
- Clearing & Settlementservice
- After a match, transfers ownership and money between parties (T+1/T+2). Decoupled from matching so settlement never slows the hot path.
You did it
You just designed a stock exchange.
Everything you assembled, in order
- A matching engine crossing an in-memory order book turns orders into trades.
- Price-time priority — best price, then FIFO — makes matching fair by construction.
- Pre-trade risk checks at the edge keep invalid orders out of the core.
- A single sequencer + single-threaded engine give deterministic, fair, fast matching.
- A market-data feed broadcasts the same view to all participants with equal latency.
- An append-only journal plus deterministic replay make the in-memory engine durable.
- Clearing runs off the hot path; partitioning by symbol scales the exchange out.