Trading Desk NotebookTrading

Market Structure for Builders: How Orders, Books, and Feeds Actually Work

An educational primer on order books, matching, and market data feeds for engineers who want to understand the machinery — not get advice.

JM
Jordan Morris
Founder & CEO · May 8, 2026 · 9 min read
Sharer/Y𝕏inf

A market is a piece of software. Before it is a chart, before it is a headline, before it is anyone's opinion about where price is going, it is a program that accepts orders, matches them by rules, and broadcasts the results. If you build systems for a living, that should be reassuring — a market is not magic, it is a queue with strict priority rules and a very fast event loop. This is a tour of that machinery: order types, the limit order book, the matching engine, and the data feeds that turn private state into the public price you see. No advice, no calls, no predictions — just the parts and how they fit.

We say this up front because the rest of the piece is going to make markets feel legible, and legible is dangerously close to predictable in the wrong frame of mind. Understanding the plumbing of an exchange tells you exactly how an order is matched. It tells you nothing about whether you should send one. Hold those two ideas apart and the rest of this is pure systems engineering.

An order is just a typed message

Everything starts with the order — and an order is a small, strongly-typed message, the kind you have validated a thousand times in an API handler. It says: which instrument, which side (buy or sell), how much, and under what condition. The condition is the part worth slowing down on, because the order type is where most of a trader's intent actually lives.

  • Market order — 'fill me now at whatever price is available.' It prioritizes certainty of execution over price. It will walk the book taking whatever it touches, which is great for getting done and bad when the book is thin.
  • Limit order — 'fill me only at this price or better.' It prioritizes price over certainty. If nobody meets your price, it rests in the book and waits. This is the order type that builds the book.
  • Stop order — a dormant trigger: 'when price reaches X, then send a market (or limit) order.' It is conditional logic living on the exchange instead of in your client.
  • Immediate-or-cancel / fill-or-kill — time-in-force flags that say 'take what you can right now and cancel the rest' or 'all of it this instant or none of it.' These are the flags that keep a fast strategy from leaking resting orders it never meant to leave behind.

Notice the shape of this. An order is a record with a side, a size, a price (or no price, for market orders), and a set of flags. It has an identity, it has a lifecycle — new, partially filled, filled, cancelled — and it emits events at each transition. If you have ever modeled an entity with a status enum and a state machine, you have already modeled an order. The exchange is just the system that holds millions of them at once and resolves them against each other deterministically.

typescript
// An order is a typed message with a lifecycle — nothing exotic.
type Side = "buy" | "sell";
type TimeInForce = "GTC" | "IOC" | "FOK"; // good-til-cancel, immediate-or-cancel, fill-or-kill

interface Order {
  id: string;
  instrument: string;     // e.g. "BTC-USD"
  side: Side;
  size: number;           // remaining quantity
  price: number | null;   // null => market order, fill at best available
  tif: TimeInForce;
  status: "new" | "partial" | "filled" | "cancelled";
  ts: number;             // arrival timestamp — matters for priority
}

// A limit buy that rests in the book until matched or cancelled.
const bid: Order = {
  id: "o_8f21", instrument: "BTC-USD", side: "buy",
  size: 0.5, price: 61_500, tif: "GTC", status: "new", ts: Date.now(),
};

The limit order book: a sorted ledger of intent

When a limit order doesn't immediately match, it rests. The collection of all resting orders for an instrument is the limit order book — two sorted lists of standing intent. The bids are everyone willing to buy, sorted highest price first. The asks (or offers) are everyone willing to sell, sorted lowest price first. The highest bid and the lowest ask are the top of book — the best price available on each side right now.

The gap between the best bid and the best ask is the spread. A tight spread means buyers and sellers nearly agree on price; a wide spread means they don't, and crossing it costs you. The total size resting at and near the top is the depth — how much you can trade before you start eating into worse prices. Spread and depth together are what people loosely call liquidity, and as a builder you can read both directly off the book's data structure.

Fig 1 — the book as two sorted ledgers facing each other. Bids climb toward the spread from below, asks descend toward it from above; the thin gap between best bid and best ask is where the next trade happens.

Mechanically, the book is two priority queues. Within a price level, orders are usually served price-time priority: better price wins first, and among orders at the same price, the one that arrived earliest wins. That second rule is why the ts field on an order matters — being early in the queue at your price level is a real, mechanical advantage, not a vibe. A common implementation keeps a sorted map of price levels, each level holding a FIFO queue of orders, so the top of book is O(1) to read and inserts land in the right level fast.

typescript
// A minimal order book: price levels, each a FIFO queue (price-time priority).
class OrderBook {
  // price -> orders in arrival order. Bids and asks kept separately.
  private bids = new Map<number, Order[]>(); // want: iterate high -> low
  private asks = new Map<number, Order[]>(); // want: iterate low -> high

  bestBid() {
    const prices = [...this.bids.keys()].sort((a, b) => b - a);
    return prices[0] ?? null; // highest price someone will buy at
  }
  bestAsk() {
    const prices = [...this.asks.keys()].sort((a, b) => a - b);
    return prices[0] ?? null; // lowest price someone will sell at
  }
  spread() {
    const b = this.bestBid(), a = this.bestAsk();
    return b !== null && a !== null ? a - b : null;
  }
}
// Production books use specialized structures (e.g. a sorted tree or array
// of price levels) so the top of book is O(1) and inserts stay cheap.

The matching engine: where intent becomes a trade

The matching engine is the heart of the exchange — the single authority that decides what trades and at what price. Its job is narrow and absolute: take incoming orders, check them against the resting book, and execute a trade whenever a buy and a sell can agree. A trade happens the instant an incoming order's price crosses the book — a buy at or above the best ask, or a sell at or below the best bid. The engine fills against the best-priced resting orders first, walking down the queue until the incoming order is satisfied or runs out of counterparties.

  1. An order arrives and is validated — instrument exists, size and price are sane, the account is permitted.
  2. The engine checks whether it crosses: does a market buy meet any ask, or does a limit buy's price reach the best ask?
  3. If it crosses, it matches against the best-priced resting orders in price-time order, generating one or more fills, each emitting a trade event.
  4. Whatever quantity is left over, if the order type allows resting, is inserted into the book at its limit price — and now it is liquidity for the next incoming order.
  5. Every state change — new resting order, partial fill, full fill, cancellation — is published to the outgoing data feed.

The hard requirement is determinism. Given the same sequence of orders, the matching engine must produce the exact same trades every time, because fairness and auditability depend on it. That is why serious engines run the match on a single logical thread per instrument — a tight, sequential event loop — rather than fanning out concurrently and risking a race over who got to a resting order first. The parallelism lives in front of the engine (parsing, validation, networking), not inside the match itself. If you have ever forced an ordered, single-writer path through a system to kill nondeterminism, this is the same instinct at exchange scale.

A matching engine is a state machine that must give the same answer twice. Everything fast about it is in service of that one boring promise.

Studio principle

Market data feeds: turning private state into public price

The matching engine holds the real book in its own memory. You, on the outside, never see that memory directly — you see a market data feed, the broadcast of what the engine is doing. Understanding feeds is where a lot of builders' intuition breaks, so it is worth being precise: there is no single thing called 'the price.' There are events, and there are different resolutions of those events sold or streamed as feeds.

  • Trade prints (Level 1, top of book) — the lightest feed: last trade price and size, plus the current best bid and best ask. Enough to draw a price line and a spread. This is what most retail dashboards consume.
  • Full depth (Level 2 / market-by-price) — every price level and the total size resting at each. Now you can see the shape of the book, not just its top.
  • Market-by-order (Level 3) — every individual resting order, not just aggregated levels. The richest and heaviest feed; mostly the domain of professional and institutional systems.
  • OHLCV bars — not a raw feed at all but an aggregation: open, high, low, close, and volume bucketed into time windows. A 1-minute candle is a GROUP BY over trade events, computed after the fact. The candle never existed inside the engine.

Feeds are typically delivered as a snapshot plus a stream of deltas: you receive the full book state once, then a sequence of incremental updates you apply in order to keep your local copy in sync. This is the same pattern as a database replication log or a CRDT sync — a base state and an ordered diff stream. The sequence numbers on those deltas are not decoration. Drop one, apply them out of order, or silently reconnect without re-snapshotting, and your local book quietly diverges from the real one. The bug won't announce itself; your prices will just be wrong.

typescript
// Consuming a feed: snapshot once, then apply ordered deltas. Gaps = resync.
let seq = 0;
let book: BookState;

function onSnapshot(s: Snapshot) {
  book = s.book;
  seq = s.seq; // anchor our sequence to the snapshot
}

function onDelta(d: Delta) {
  if (d.seq !== seq + 1) {
    // We missed a message. Do NOT guess — our book is now untrustworthy.
    return requestSnapshot(); // re-anchor instead of drifting silently
  }
  applyDelta(book, d); // mutate local book: add, change, or remove a level
  seq = d.seq;
}
// The whole game is: never let your local view drift from the exchange's.

Why this matters when you build market software

You don't need to build an exchange to need this model. The moment you build anything that touches markets — a watchlist, a portfolio tracker, a markets dashboard, an alerting tool — you are consuming feeds, aggregating events, and rendering derived state. Knowing the machinery upstream tells you exactly where your numbers come from and how they can lie to you.

It changes the questions you ask. Is this price a trade print or a mid-quote? Are these candles built on the exchange's clock or my server's? Did my socket reconnect and silently skip a resync? Is the spread I'm showing real depth or one stale resting order? These are not trading questions — they are data integrity questions, and they are the same discipline we bring to every data pipeline we build: know your source, preserve ordering, never trust a number you can't trace back to an event.

2
sides to every book
1
deterministic match path
30+
platforms shipped by the studio

That last instinct — never trust a number you can't trace back to an event — is exactly the standard we hold across the 30+ platforms we have shipped, market-facing or not. A delivery status, an order total, a price tick: each is a derived view of an underlying event log, and each is only as trustworthy as the pipeline that produced it. Markets just make the stakes of getting that wrong unusually visible.

Building a markets dashboard, a data tool, or anything that has to render numbers it can trust? That's the kind of careful, source-aware software we build — educational tooling, not advice.

Start a project
#market-structure#order-book#matching-engine#market-data#trading-systems

Questions, answered

What is the difference between a market order and a limit order?
A market order says 'fill me now at whatever price is available' — it prioritizes getting executed over the price you get, and will take whatever the book offers. A limit order says 'fill me only at this price or better' — it prioritizes price, and if nobody meets it, the order rests in the book and waits. Market orders take liquidity; resting limit orders provide it. This is structure, not advice — which one suits a given situation is your own decision.
What is a limit order book and what is 'the spread'?
The limit order book is the collection of all resting (unmatched) limit orders for an instrument: bids sorted highest-price-first and asks sorted lowest-price-first. The best bid and best ask form the 'top of book.' The spread is the gap between them — tight when buyers and sellers nearly agree, wide when they don't. The total size resting near the top is the depth. Spread and depth together are what people mean by liquidity, and both are readable straight off the book's data structure.
Why do two sources show different prices for the same instrument?
Because there is no single 'the price' — only events and different resolutions of those events. Two systems can consume different feeds (last trade vs. mid-quote), aggregate candles on different time boundaries, or sit at different network points with different latency, and all be correct. Any single price is a sample observed at a particular place and moment, not absolute truth. When you build market software, always know which feed a number came from.
Recommended · affiliate

The stack we actually use

The tools below run our own systems and the ones we build for clients. A few are affiliate links — they support The Signal at no cost to you.

Some links on this site are affiliate links. If you click through and sign up or buy, J Supreme Tech may earn a commission at no extra cost to you. We only recommend tools we actually use to build and run our own systems. Full disclosure.

Found the signal useful? Pass it on.

Sharer/Y𝕏inf
The Communications Debrief

Get the signal in your inbox.

Field notes on building software, marketing systems and the markets — what we shipped, the problems and the fixes. One clean dispatch, no noise.

Double opt-in. No spam, unsubscribe anytime.