Introduction

Every time you click Buy on a stock, crypto, or futures exchange, a tiny algorithm decides whether your order fills immediately, waits in a queue, or goes unfilled. That algorithm is called a matching engine, and its data structure is the limit order book.

The book is a sorted list of resting orders split into two sides. The bid side collects buyers willing to pay up to some price; the ask side collects sellers demanding at least some price. The highest bid and the lowest ask sit face to face — and the gap between them is the spread, the cost of trading.

When a new order arrives, the matching engine scans the opposite side of the book in price-time priority: best price first, and among equal prices, whoever arrived earliest wins. It is a deterministic, sub-microsecond arbitrator that never argues and never sleeps — the closest thing finance has to a physical law.

Try the Matching Engine

Submit limit orders on both sides and watch the book fill up. When bids and asks overlap, the matching engine fires and fills the trade at the resting order's price. A market order sweeps the entire opposite side until it is filled or the book runs dry.

<!-- {{c_intro}} -->
<div class="controls">
  <div class="row">
    <label>{{lbl_side}}</label>
    <div class="toggle">
      <button id="btnBuy" class="active" type="button">{{lbl_buy}}</button>
      <button id="btnSell" type="button">{{lbl_sell}}</button>
    </div>
  </div>
  <div class="row">
    <label for="priceIn">{{lbl_price}}</label>
    <input id="priceIn" type="number" min="1" max="200" step="1" value="100">
  </div>
  <div class="row">
    <label for="qtyIn">{{lbl_qty}}</label>
    <input id="qtyIn" type="number" min="1" max="100" step="1" value="10">
  </div>
  <div class="btns">
    <button id="btnLimit" type="button">{{btn_limit}}</button>
    <button id="btnMarket" type="button" class="ghost">{{btn_market}}</button>
    <button id="btnReset" type="button" class="ghost">{{btn_reset}}</button>
  </div>
</div>
<div class="book-wrap">
  <div class="book-col" id="asks">
    <div class="book-hdr">{{hdr_asks}}</div>
    <div id="askRows"></div>
  </div>
  <div class="spread-box" id="spreadBox">{{spread_label}}: —</div>
  <div class="book-col" id="bids">
    <div class="book-hdr">{{hdr_bids}}</div>
    <div id="bidRows"></div>
  </div>
</div>
<div class="status" id="status">{{hint_start}}</div>
/* {{c_style}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: .9rem; }
.controls { display: flex; flex-direction: column; gap: .45rem; margin-bottom: .7rem; }
.row { display: flex; align-items: center; gap: .5rem; }
label { width: 60px; font-weight: 600; color: #444; font-size: .82rem; }
input[type=number] { width: 90px; padding: .3rem .5rem; border: 1px solid #cdd9e3; border-radius: 6px; font-size: .9rem; }
.toggle { display: flex; gap: 0; border-radius: 6px; overflow: hidden; border: 1px solid #1d3557; }
.toggle button { border: none; border-radius: 0; padding: .3rem .8rem; background: #fff; color: #1d3557; cursor: pointer; font: 600 .83rem system-ui, sans-serif; }
.toggle button.active { background: #1d3557; color: #fff; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 .83rem system-ui, sans-serif; padding: .38rem .8rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 6px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
/* {{c_book_style}} */
.book-wrap { display: flex; align-items: flex-start; gap: .4rem; margin-top: .4rem; }
.book-col { flex: 1; }
.book-hdr { font-weight: 700; font-size: .78rem; text-align: center; padding: .2rem 0;
            background: #e8eef3; border-radius: 5px 5px 0 0; }
#asks .book-hdr { color: #c92f3c; }
#bids .book-hdr { color: #0a7d33; }
.book-row { display: flex; justify-content: space-between; padding: .18rem .5rem;
            font-size: .82rem; border-bottom: 1px solid #eee; font-variant-numeric: tabular-nums; }
.ask-row { background: #fff5f5; color: #c92f3c; }
.bid-row { background: #f0fff4; color: #0a7d33; }
.spread-box { font-size: .8rem; font-weight: 600; color: #555; writing-mode: horizontal-tb;
              align-self: center; text-align: center; min-width: 54px; padding: .2rem .3rem;
              background: #f5f5f5; border-radius: 5px; border: 1px solid #ddd; white-space: nowrap; }
.status { margin-top: .5rem; font-size: .9rem; font-weight: 600; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.info { color: #1d3557; }
// Code not found

Notice how the spread — the gap between the best bid and best ask — tightens or widens as you add and remove orders. Narrow spreads mean abundant liquidity; wide spreads signal a thin market where even a small order moves the price.

The Real Complexity

The matching engine sounds simple — sort by price, break ties by time — but its performance demands are extreme. Modern exchanges process millions of order events per second with latencies measured in nanoseconds.

  • Naive sorting is disqualified. Re-sorting the book on every event costs O(nlogn)O(n \log n) per event and collapses under load.
  • The classical solution uses price-indexed queues. Each distinct price level holds a FIFO queue of orders. A hash map or array indexed by price locates any level in O(1)O(1); matching then pops the front of the best queue, also in O(1)O(1) amortized.
  • The full event loop — insert, cancel, match — runs in O(1)O(1) amortized per event on average, O(k)O(k) where kk is the number of levels a market order crosses.
  • Why not trees? A balanced BST gives O(logn)O(\log n) per level lookup but the constant factor is too large for co-located matching engines fighting for nanoseconds. Arrays of queues, lock-free ring buffers, and NUMA-aware memory layouts dominate in practice.
  • The hard problem is not matching but fairness. Exchanges publish detailed matching rules (pro-rata, time-priority, size-priority) and spend years litigating edge cases. The algorithm design challenge is deciding which order fills when several qualify equally.

The limit order book is a case where the algorithmic complexity is solved and the engineering complexity is the frontier. Getting to O(1)O(1) matching was the theory; shaving the last 50 nanoseconds off the hot path is the practice.

Where It Matters

The matching engine pattern appears wherever scarce capacity must be allocated fairly and fast:

  • Equities and futures: every major exchange — NYSE, NASDAQ, CME — runs a variant of the limit order book. The 2010 Flash Crash and the 2022 meme-stock squeezes were both limit-order-book dynamics playing out at scale.
  • Cryptocurrency exchanges: decentralized exchanges (DEXs) replicate the order book on-chain, where "price-time priority" becomes a smart-contract invariant — and gas fees make every match visible on a public ledger.
  • High-frequency trading: HFT firms co-locate servers inside exchange data centers and submit and cancel orders in microseconds, exploiting the O(1)O(1) matching guarantee. The arms race has driven exchange latency below 100 nanoseconds.
  • Network packet scheduling: routers prioritize packets using weighted fair queuing — mathematically identical to a multi-class order book with price replaced by priority class.
  • Task and job queues: operating-system schedulers, cloud job runners, and optimal-testing harnesses all apply some form of priority queue with time-ordering as a tiebreak.

The limit order book is a microcosm of a broader algorithmic idea: priority queues with fairness constraints. Once you understand how a matching engine works, you recognize its skeleton in schedulers, routers, and any system that must serve competing demands in order.

Conclusion

The limit order book is deceptively simple: two sorted lists, one rule — best price wins, ties broken by arrival time. Yet from that rule emerges price discovery, the mechanism by which millions of competing opinions about value collapse into a single number.

The matching engine solved its algorithmic problem decades ago. What keeps researchers and engineers busy is everything around it: fairness in rule design, latency in hardware, stability under stress, and the arms race between market makers and takers.

Next time you see a price on a screen, remember that behind it sits a queue, a priority rule, and a matching loop running faster than a heartbeat — the same idea that governs scheduling in operating systems and max-flow in networks, dressed in the language of markets.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/limit-order-book/Content licensed under CC BY-NC 4.0.