Costs turn a good backtest into a bad one
August 17, 2026
The first backtest almost everyone writes assumes trades are free. No commission, no spread, no slippage, and fills at the exact closing price the signal was computed from. On a strategy that turns over once a month, ignoring that is a small error. On one that trades every day, it is the difference between a business and a hobby.
The three costs
Commission is the easy one. Your broker publishes it. It is either per share, per trade, or zero with the cost buried elsewhere.
Spread is the gap between the bid and the ask. You buy at the ask and sell at the bid, so a round trip costs you the spread even if the price never moves. On a liquid large cap this is small. On a thin small cap it can be larger than the edge you are trying to capture.
Slippage is everything else: the price moving between your decision and your fill, and the market moving against you because of your own order. It is the hardest to model and the most likely to be underestimated.
Putting them in
Costs apply when the position changes, not on every bar. So the first thing you need is turnover:
df["position"] = signal.shift(1)
df["turnover"] = df["position"].diff().abs().fillna(0)
turnover is 1 on a bar where you go from flat to long, 2 where you flip from long to short, and 0 where you hold. Now charge for it:
COST_PER_TURN = 0.0010 # 10 basis points round trip, adjust to your broker and universe
df["gross"] = df["Close"].pct_change() * df["position"]
df["net"] = df["gross"] - df["turnover"] * COST_PER_TURN
Then compare the two equity curves:
(1 + df[["gross", "net"]]).cumprod().plot()
The gap between those lines is the cost of trading, and its size tells you something the Sharpe ratio will not.
The number that matters
Divide your average gross return per trade by your cost per trade. If a strategy makes 15 basis points per trade and costs 10 to execute, you are working for the broker. Strategies fail this test all the time, and they fail it silently, because a gross backtest never mentions it.
This is also why a daily strategy needs a much larger edge than a monthly one. Trading twenty times more often means the per-trade edge has to clear the cost hurdle twenty times more often.
Where people underestimate
The usual mistake is using a single cost number pulled from a large cap and applying it to a universe that includes illiquid names. Spread scales with liquidity. If your screen selects the smallest, most volatile stocks, which many mean reversion screens do, your real cost is not the cost you tested with.
The other one is assuming you get the close. If your signal uses the closing price, you cannot also trade at it. Either trade the next open, or accept that your fill is somewhere worse than the close and model it.
A useful habit: run the backtest at your best estimate, then run it again at double the cost. If the strategy dies at double, it is too fragile to trade, because your estimate is a guess and guesses about costs are usually optimistic.