4 min read

Fills at the touch, never at the mid

A backtest is only as honest as the prices it assumes. Why we model fills against the historical bid and ask, and what filling at the midpoint quietly hides.

There is a decision every backtest has to make and most make silently: what price did the trade happen at?

The convenient answer is the midpoint between the bid and the ask. It is easy to compute, it is available in every dataset, and it is a price at which nobody has ever traded.

What the midpoint assumes

Filling at the mid assumes that every order, in every size, at every moment, met a willing counterparty halfway. It assumes no spread was paid, on entry or on exit. It assumes the quote you saw was the quote you got.

In a liquid instrument with a penny spread, that assumption is close enough to harmless. In options ( where spreads are wider, where they widen further exactly when something interesting is happening, and where a strategy may cross them several times in a single position) it is not an approximation. It is a subsidy, paid to the strategy by the simulation, on every leg of every trade.

The subsidy is invisible in the output. It does not appear as a line item. It appears as a slightly better result, everywhere, consistently, in a way that looks like the strategy rather than like the assumption.

What we do instead

Fills are modeled against the historical bid and ask at the moment the decision would have been made, on the side the order would actually have to cross. Buying lifts the offer. Selling hits the bid. The spread is paid because in the real world the spread is paid.

There is a second timing element that is easy to omit. A decision is made at one instant and an order arrives at the venue at a slightly later one. Modeling the fill at the decision instant assumes an infinitely fast path from thought to exchange. We apply an explicit allowance for that delay and fill against the market as it stood when the order would have arrived, not when it was conceived.

Neither of these makes the simulation correct. They make it wrong in the direction of pessimism, which is the only direction in which being wrong is safe.

Missing and crossed quotes

Real historical data is not tidy. Quotes go missing. Bids occasionally exceed offers, for a tick, usually around an event.

The tempting handling is to interpolate, reaching for the nearest available quote and carrying on. It produces a clean, complete, continuous series, which is exactly the problem: the periods where quotes are missing or crossed are disproportionately the periods where something was happening, and smoothing over them removes precisely the conditions a risk model most needs to see.

We treat those moments explicitly rather than papering over them. A gap in the data is recorded as a gap. A crossed quote is handled as a known condition, not silently averaged into something that looks normal.

Why this matters more than it sounds

An optimistic fill model does not merely inflate the result. It changes which strategies look attractive.

Filling at the mid systematically favors strategies that trade more, that cross more spreads, and that operate in wider-spread instruments, because those are exactly the strategies receiving the largest hidden subsidy. A firm that fills at the mid will not only overestimate its returns; it will select, from a set of candidates, the ones whose apparent edge is most made of the assumption.

That is the part that is hard to recover from. An overestimate can be discounted. A distorted selection process quietly points the whole research program at the wrong questions.

The general form

Every simulation contains assumptions that are convenient rather than true, and most of them are individually defensible. The ones worth hunting are those whose error runs consistently in the direction you would like.

An assumption that makes results worse gets noticed and interrogated immediately. An assumption that makes them better gets absorbed into the finding, because it agrees with what you were hoping for. Filling at the midpoint is the clearest example of the second kind, which is why it is worth naming explicitly and eliminating first.