NewTurn any lesson into a live trading bot with one prompt, using AgenKitagenkit.xyz

Part 5 · Market makingLesson 2114 minQuant Bible §6 + beyond

Inside high-frequency trading

The limit order book, queue priority, the economics of a market maker, and why microseconds and the speed of light matter.

Everything in this course meets in one place: high-frequency trading. A modern market-making firm runs Bayes updates on every fill, prices with expected values, sizes with variance, trusts the law of large numbers to turn tiny edges into steady profits, and does all of it in microseconds.

What high-frequency trading is

High-frequency trading (HFT) means automated strategies that make decisions and send orders in microseconds, hold positions for seconds or less, and trade enormous numbers of times a day. Most of it falls into three families:

  • Market making: continuously quoting bids and asks and earning the spread (lessons 17–19, at machine speed).
  • Arbitrage: keeping related prices in line: the same stock on different exchanges, an ETF against its basket, a future against the underlying.
  • Short-term signals: predicting the next few ticks from the order book itself.

The limit order book

Every exchange keeps a limit order book: all resting buy orders (bids) and sell orders (asks), sorted by price. The highest bid and lowest ask are the best bid and offer; the gap between them is the spread; the quantity at each price is the depth.

  • A limit order that doesn’t cross the spread rests in the book and adds liquidity (a maker).
  • A market order (or a crossing limit) removes liquidity (a taker): it fills against the best level, and if that runs out, the next one. That is called walking the book.
  • Most exchanges use price–time priority: at the same price, the order that arrived first fills first. Your queue position is worth money, and being fast is how you get a good one.
Figure 21.1A live order book, in 3D
A simulated book for a stock trading around $100. Green columns are resting bids, red are asks; the front row is now and each row behind is a moment earlier, so the landscape is the book’s recent history. Orange beads are trades. Send a 300-lot market buy and watch it eat several levels and widen the spread; market makers refill it within a few ticks.

Reading the book: imbalance and the microprice

The plain mid is (b+a)/2(b + a)/2. But if 900 lots are bid and only 100 offered, the next trade is more likely to lift the ask. The microprice weights each side by the size on the other side:

microprice=b⋅Qa+a⋅QbQa+Qb,imbalance=Qb−QaQb+Qa.\text{microprice} = \frac{b \cdot Q_a + a \cdot Q_b}{Q_a + Q_b}, \qquad \text{imbalance} = \frac{Q_b - Q_a}{Q_b + Q_a} .

With b=99.99b = 99.99, a=100.01a = 100.01, Qb=900Q_b = 900 and Qa=100Q_a = 100, the microprice is 100.008100.008: very close to the ask. Order-book imbalance is one of the best-known short-horizon signals in HFT research.

The economics of a market maker

Each time a market maker buys at the bid and later sells at the ask, it earns the spread. Per fill, the expected profit is roughly

E[PnL per fill]≈s2⏟half-spread  −  E[price move against you after the fill]⏟adverse selection  −  fees+rebates.E[\text{PnL per fill}] \approx \underbrace{\tfrac{s}{2}}_{\text{half-spread}} \;-\; \underbrace{E[\text{price move against you after the fill}]}_{\text{adverse selection}} \;-\; \text{fees} + \text{rebates}.

The adverse-selection term is Bayes’ theorem from lesson 1: being filled is evidence that the trader on the other side knew something. Edges are tiny, often a fraction of a cent per share, and noise per trade is large. They become a business only because of the law of large numbers (lesson 6): with millions of fills, total PnL’s mean grows like nn while its noise grows like n\sqrt n.

Why speed matters

Speed isn’t about trading more often for its own sake. It buys three things:

  1. Queue position. At a given price the first order in line gets filled first, and fills at the front of the queue carry less adverse selection.
  2. Not getting picked off. When the market moves, a quote that is now stale is free money for whoever hits it first. A market maker must cancel faster than others can trade against it.
  3. Arbitrage. When prices diverge across venues, only the first trader to see both and act captures the difference.

The path from market data arriving to an order leaving is called tick-to-trade: receive the packet, decode it, update the book, decide, run risk checks, and send. Firms shrink it with colocation (servers in the exchange’s data centre), kernel-bypass networking, carefully written C++, and FPGAs that do the whole loop in hardware.

Figure 21.2The latency ladder
From one clock cycle to a blink, on a log scale where each grid line is ×1,000. HFT lives in the left half of this chart.

The physics limit

Nothing is faster than light. In glass fibre light slows to about c/1.47c/1.47, roughly 204,000204{,}000 km/s, so every kilometre of fibre costs about 4.9 μs4.9\,\mu\text{s}:

t=d⋅nc,1 km×1.47299,792 km/s≈4.9 μs.t = \frac{d \cdot n}{c}, \qquad \frac{1\ \text{km} \times 1.47}{299{,}792\ \text{km/s}} \approx 4.9\ \mu\text{s}.

Air has n≈1n \approx 1, which is why firms built chains of microwave towers between Chicago (futures) and the New Jersey data centres (stocks): a straighter path through air shaves milliseconds off fibre, and milliseconds are thousands of FPGA decisions.

In code

import numpy as np

def microprice(bid, ask, bid_size, ask_size):
    return (bid * ask_size + ask * bid_size) / (bid_size + ask_size)

print(microprice(99.99, 100.01, 900, 100))     # 100.008: leans towards the ask

# Light in fibre and in air between Chicago and New Jersey (about 1,150 km straight line)
c = 299_792.458                                  # km/s
d = 1_150
print(f"air:   {d / c * 1e3:.2f} ms")             # ≈ 3.84 ms
print(f"fibre: {d * 1.47 / c * 1e3:.2f} ms")      # ≈ 5.64 ms on a straight line; real routes are longer

# Tiny edge, many trades: the law of large numbers at work
rng = np.random.default_rng(21)
edge, noise = 0.0005, 0.05                       # dollars per share per fill
for n in (1_000, 10_000, 100_000):               # fills per day
    daily = rng.normal(n * edge, np.sqrt(n) * noise, size=100_000)
    print(n, f"share of losing days: {np.mean(daily < 0):.3f}")   # ≈ 0.38, 0.16, 0.001

Check yourself

A market maker quotes 99.99 @ 100.01 and earns no rebates or fees. On average the price moves 0.6 cents against it after each fill. What is its expected profit per fill?

Progress is saved in this browser only.