Market microstructure
A backtest fills you at the close, instantly, in any size. The real market is a queue of resting orders you have to cross, and every crossing costs money you did not model. Microstructure is the study of how that queue works — how prices actually form, why there is a spread between what you can buy and sell at, and where the frictions in your P&L come from. This chapter builds the limit order book from the bottom up and decomposes the spread into its economic parts, then ties each back to the cost you charge in a backtest with ek.costs.CostModel.
The limit order book#
Modern electronic markets are continuous double auctions. Every resting order lives in the limit order book (LOB): a price-sorted ladder of bids (buyers, below) and asks/offers (sellers, above). Each level shows a price and the total quantity waiting there — the depth. The two prices that matter most sit at the inside of the book:
The best bid is the highest price a buyer will pay; the best ask the lowest a seller will accept. The midis the usual reference “price” — but you can never trade at the mid with a market order: you buy at the ask and sell at the bid. That gap is the first cost of doing business.

A market order that is larger than the depth at the inside walks the book: it fills the first level, then the next, then the next, at progressively worse prices. The average fill is therefore worse than the inside quote — the origin of market impact, covered in the next chapter. A backtest that assumes you always transact at the mid silently ignores both the spread and this walk.
ask 50.04 × 3000 | 50.03 × 2000 | 50.02 × 1500 | 50.01 × 800bid 50.00 × 1000 | 49.99 × 1800 | 49.98 × 2500The best bid is $50.00, the best ask $50.01, so the quoted spread is exactly one tick ($0.01) and the mid is $50.005. Now send a market buy for 5,000 shares. There are only 800 at the inside, so you walk up: 800 @ 50.01, 1,500 @ 50.02, 2,000 @ 50.03, and the last 700 @ 50.04. Total cost , so your average fill is $50.0252 — about 2 cents (≈4 bps) above the mid, even though the screen showed a 1-cent spread. A backtest filling all 5,000 at $50.005 just handed you $101 of free money that the book never offered.
Market orders vs limit orders#
Every trade is one of two roles:
Market order (liquidity taker): execute immediately against the resting book. You are guaranteed a fill but you pay the spread and any impact — you cross from mid to ask (buying) or mid to bid (selling). Certainty of execution, uncertain price.
Limit order (liquidity maker): post a resting order at your chosen price and wait. If it fills, you earn the spread instead of paying it (and often a maker rebate). But there is no guarantee it fills at all — and worse, it tends to fill precisely when you did not want it to (adverse selection, below). Certain price, uncertain execution.
Tick size and price formation#
Prices are not continuous — they move on a discrete grid, the tick size, the minimum increment between quotable prices. Tick size sets a floor on the spread: it can never be tighter than one tick. In a liquid, large-tick instrument the spread is often pinned at exactly one tick and the real competition is for queue priority at that price, not for price itself. In a small-tick instrument the spread floats and depth thins out.
Price formation is the process by which this book turns order flow into a consensus price. Each arriving market order and each cancellation nudges the inside quotes; the mid drifts as buyers and sellers reveal their willingness to pay. Price is not handed down — it is discovered, one order at a time, and the spread is the friction in that discovery.
The three components of the spread#
Why is there a spread at all, and what sets its width? The market-making literature decomposes it into three economic parts a liquidity provider must be paid for:
Order-processing cost#
The fixed operational cost of making a market — exchange fees, clearing, technology, and a minimum profit for the service of standing ready to trade. This is the floor and the smallest, most stable component.
Inventory cost#
A market maker who buys from a seller now holds inventory they did not want and bears the risk that the price moves against it before they can offload it. They widen the quote to be compensated for this warehousing risk, and they skew the quote to nudge their inventory back toward flat. The more volatile the asset, the larger this component — which is why spreads widen precisely when volatility spikes.
Adverse selection#
The deepest component, and the reason posting limit orders is dangerous. Some of the flow hitting the market maker is informed— it trades because it knows something about where the price is going. When an informed buyer lifts the maker's offer, the price is about to rise, so the maker just sold too cheap. The maker cannot tell informed flow from noise ex ante, so they widen the spread on everyone to recover, on average, what they lose to the informed. Adverse selection is the market maker's payment for trading against people who know more than they do.
Quoted vs effective spread#
The quoted spread is what you see: . The effective spread is what you actually paid, measured against the mid at the moment of your trade — and for anything larger than the inside depth it is wider, because you walked the book:
The factor of 2 makes it comparable to a full round-trip quoted spread (one side of the crossing, doubled). The gap between effective and quoted spread is your impact: the effective spread is the honest, trade-weighted cost, and it is the number a realistic backtest must charge — not the flattering inside quote.
Tying it back to ek.costs#
You will not simulate the full order book in a strategy backtest — you compress all of the above into a single cost charged per round trip. That is exactly what ek.costs.CostModel does. Its spread_rt parameter is the round-trip spread as a fraction of price: it stands in for the quoted spread plus the typical impact of your size — i.e. an estimate of the effective spread you will pay crossing the book twice. Its swap_day is the financing/carry per day held.
import edgekit as ek
# 12 bps round-trip spread, 2 bps/day financing (a crypto/CFD convention)
cm = ek.costs.CostModel(spread_rt=0.0012, swap_day=0.0002)
# cost of one round trip, expressed in R (dollar cost / the stop-distance risk unit)
r_cost = cm.r_cost(entry_price=100.0, risk_per_unit=2.0, days=3)
print(round(r_cost, 4)) # subtract this from every trade's gross Rspread_rt for that instrument and size is on the order of (16 bps), not the 12-bps placeholder. If your stop distance is $2 on a $50 entry, the round-trip dollar cost is , which is gone before the trade even works. A signal with 0.15R of gross expectancy just lost a quarter of its edge to the book — and that is before the cost stress in the next chapter doubles it.The microstructure lesson is why this subtraction is not optional. The spread is not noise you can average away — it is a structural transfer from taker to maker on every trade, set by inventory risk and adverse selection, and it scales with how often you trade and how hard you cross. A high-frequency signal pays the effective spread hundreds of times; a slow one pays it rarely. Which cost dominates, and whether a gross edge survives it at all, is the subject of the next chapter.
0.0012 default is a placeholder. Pull the actual quoted spread and typical depth for your instrument and size, widen it toward the effective spread for the size you trade, and — as always — re-run at 2x and 3x that estimate to see whether the edge is real or a rounding artefact.Next: Execution & transaction costs — slippage, the square-root impact law, execution algos, and the transaction-cost analysis that decides whether a signal is tradeable.
