DEV Community

Christian Pichichero
Christian Pichichero

Posted on

The Fill Model Is Where Backtests Quietly Cheat

Every backtest has to answer a boring question: when the strategy says "buy," what price does it actually get? Most backtesting frameworks answer this question badly by default, and the badness is almost always in the strategy's favor.

Here are the four assumptions that do the most damage, roughly in order of how often they show up.

Mid-price fills

If your backtest fills orders at the midpoint of the bid-ask spread, you are assuming you trade for free. You don't. A market order pays at least half the spread to cross it; a marketable limit order pays something close to that too, once you're honest about how often it actually gets hit versus sitting unfilled while the market moves away. Mid-price fills are the single most common way a backtest manufactures edge that doesn't exist, because the effect compounds with trade frequency — a strategy that trades often looks great on mid-price fills and mediocre-to-negative once it pays the spread on every round trip.

Zero slippage

Slippage is the gap between the price your signal fired at and the price your order actually executed at, and it's not just a queuing artifact — it's partly information. If your strategy is buying because something changed, other participants are reacting to the same thing, and the price you wanted is often gone by the time your order reaches the book. A backtest with zero slippage is quietly assuming the market waits for you.

Unlimited size at the touch

Backtests routinely assume you can execute your full position size at the best bid or ask, no matter how large the order is relative to the visible size there. In practice, a large order walks the book, and the average fill price is worse than the touch price by an amount that depends on how thin the book is. This one is invisible until you try to size up, which is exactly when a strategy that looked fine in testing starts bleeding.

Commissions omitted or averaged

Commissions and fees are usually small per trade and therefore easy to skip or fold into a rough average. But a strategy with thin per-trade edge and high turnover can have its entire expectancy eaten by costs that were treated as a rounding error.

A worked example

Take a mean-reversion strategy trading a $30 stock with a 2-cent spread: average win 18¢, average loss 14¢, win rate 55%.

expectancy (per share, before costs)
= 0.55 × 18¢ − 0.45 × 14¢
= 9.9¢ − 6.3¢
= 3.6¢
Enter fullscreen mode Exit fullscreen mode

That 3.6¢ looks like a real edge. But if the backtest filled at the mid, it never paid the spread it would pay in live trading. A more honest fill — buying near the ask, selling near the bid — costs roughly the full spread on the round trip, here about 2¢. Subtract that:

3.6¢ − 2¢ = 1.6¢
Enter fullscreen mode Exit fullscreen mode

The edge didn't disappear, but it lost more than half its value to an assumption that never showed up as a line item anywhere in the report. Add a per-share commission and a little realistic slippage on top, and it's easy to see how a strategy with a "good" backtest turns out to be trading the spread, not an actual signal.

What you can actually check from a trade list

If all you have is a CSV of closed trades — entry, exit, size, timestamps — there's a limit to how much of this you can diagnose. You can check sensitivity: rerun expectancy with a range of assumed slippage and spread costs and see how much of the edge survives. You can check whether wins are concentrated in trades with unusually favorable prices relative to the surrounding bars, which is a proxy for lookahead or mid-price fills. You can check whether performance depends on a handful of trades — if removing the best 5% of trades erases the edge, that's worth knowing regardless of the fill model.

What you generally can't check from a trade list alone is anything that requires order book state: actual queue position, actual available size at the touch at the moment of the signal, actual latency between signal and order arrival. Those require tick-level or order-book data and a simulator that models the exchange mechanics, not just entry and exit prices. If someone tells you they can fully validate execution realism from a CSV of closed trades, they're skipping something — the honest version of this check is partial, and it should say so.

The practical test

The cheap version of all this: take your reported average win and average loss, subtract a full spread crossing on both entry and exit, and see if the strategy still has positive expectancy. If it doesn't survive that adjustment, the edge was largely the spread it never paid, and no amount of additional testing further downstream is going to rescue it. If it does survive, you've at least confirmed the edge isn't purely a fill-model artifact — which is a different question from whether it will hold up in other ways, but it's the first one worth asking.

Disclosure: I build Tradevo Verify, which takes a closed-trade export and runs it through this kind of stress-testing, among other checks, and produces a versioned evidence record rather than a verdict on whether the strategy is good.

Top comments (0)