fivenines
6/39
Lesson 1.3

The order book and price-time priority

What you'll learn

The central data structure of the exchange, the two-rule algorithm that runs it, and why the engine is single-threaded on purpose.

Building on

In 1.2, Alice's order "rested on the book." This lesson is that book.

A central limit order book (CLOB) is two sorted lists per symbol: bids (buyers, best = highest price) and asks (sellers, best = lowest). Within one price level, orders queue in arrival order.

flowchart LR
    subgraph bids["BIDS — buyers, best price on top"]
        direction TB
        B1["10.01 · 300 shares · 2 orders"]
        B2["10.00 · 1,200 shares · 5 orders"]
        B3["9.99 · 500 shares · 3 orders"]
        B1 --- B2 --- B3
    end
    MID["spread = 0.02"]
    subgraph asks["ASKS — sellers, best price on top"]
        direction TB
        A1["10.03 · 200 shares · 1 order"]
        A2["10.04 · 800 shares · 4 orders"]
        A3["10.06 · 400 shares · 2 orders"]
        A1 --- A2 --- A3
    end
    bids ~~~ MID ~~~ asks
    
The ACME book. Best bid 10.01, best ask 10.03. No trade occurs because no buyer's price meets any seller's — the book is crossed only momentarily, at the instant a marketable order arrives.

Matching is governed by price-time priority, two rules applied in strict order:

  1. Price first: a better price always trades before a worse one.
  2. Time second: at the same price, whoever arrived first trades first.
Worked example

Into the book above arrives: buy 900 ACME, limit 10.04. The engine walks the ask side:

  1. Best ask is 10.03 (200 shares). 10.03 ≤ 10.04, so it matches: trade 200 @ 10.03. Note: at the seller's resting price — the resting order sets the price; the aggressor pays it.
  2. Next level 10.04 (800 shares across 4 orders). 10.04 ≤ 10.04, match in time order: trade 700 @ 10.04, filling the oldest orders first; the 4th order at that level fills only partially.
  3. Buyer's 900 shares are done. Book now shows best ask 10.04 (100 shares remaining), best bid still 10.01. One incoming order produced 5+ trades, 5+ private fill reports, and a burst of public market data.

Now the design consequence. Rule 2 — time priority — means matching is order-sensitive: process two orders in a different sequence and you get different owners of the same fill. So the matching engine for a given set of symbols is a single-threaded event loop: one input at a time, to completion, no locks, no races. This sounds like a bottleneck; it isn't. A tight in-memory loop handles hundreds of thousands to millions of events per second — and buys you determinism, which lesson 1.7 converts into the entire recovery story.

Key ideas
  • The book = two price-sorted sides, FIFO queues within each price.
  • Price-time priority: better price wins; ties broken by arrival.
  • Aggressors trade at resting prices; single-threading is a feature, not a compromise.
Next step

See what actually stuck.

Take the practice scenarios now.