‹ Backtest Strategies Lesson 10 of 17
Contents Lesson 10 of 17

4 min read · professional

Slippage, or the price you did not get

Costs are what you are charged. Slippage is the difference between the price your backtest assumed and the price you would actually have received, and it is the part beginners model as zero without noticing they modelled it at all.

Execution is an assumption, and you already made one

Your backtester fills at some price. Whatever that is, it is a claim about how the world works:

  • Fill at the close of the signal bar — impossible, you did not know the close until it happened.
  • Fill at the next open — plausible, and the honest default. It is the first price at which a decision made after yesterday's close can act.
  • Fill at the next close — a different assumption, not a safer one. It is what a market-on-close order gets, and the close is about as often better than the open as worse, so it buys you no margin.
  • Fill at the best price in the next bar — this is not conservative or aggressive, it is fantasy. If you see Math.min(low, ...) in fill logic, that is a bug wearing an optimisation's clothes.

Pick one, write it in the report, and make it a parameter so it can be changed and re-run.

The overnight gap is not slippage — and it is bigger

A signal computed at Monday's close, filled at Tuesday's open, absorbs the whole overnight move. That is not a modelling error; it is what actually happens. But it means the price you get is systematically different from the price you saw, and on gappy instruments that difference dwarfs your ten basis points.

Measure it on your own data rather than guessing: for the bars where your strategy traded, compute the median absolute gap between the previous close and the next open, in basis points. If that number is larger than costBps, your cost model is dominated by the thing it does not include.

A slippage model you can defend

Keep it simple and stated:

const fill = nextOpen * (1 + direction * params.slippageBps / 10_000);

Always against you — worse when buying, worse when selling. A symmetric random slippage averages out over many trades and therefore models nothing; the point of the assumption is to be conservative, not realistic.

The test for this one

Same craft discipline as unit 2: turn the check into a test.

Assert that every recorded fill price lies within that bar's low-to-high range. A fill outside the day's range is impossible, so this catches off-by-one bars, adjusted prices mixed with raw ones, and fills taken from the wrong day — whenever the wrong price happens to land outside the range. A neighbouring day with an overlapping range slips through, so treat it as a cheap net rather than a proof.

Six lines. Run it on every backtest.

Try it now

Measure the median overnight gap on the bars where your strategy traded, and compare it to your costBps. Then re-run with fills at the next open instead of the signal close. The drop in return is the value of a fact you did not have yesterday.