Slippage and Latency Modeling in Backtesting
Backtests are usually too optimistic for one simple reason: they assume the market waited for you. This paper decomposes latency, models fill prices with market impact, and shows why PnL arises from signal after implementation.
Abstract
Backtest engines routinely overstate strategy performance by ignoring the mechanics of order execution. This paper presents a latency decomposition framework and fill-price model incorporating spread crossing, market impact via square-root law, and stochastic drift during the decision-to-fill interval. Realistic slippage modeling is shown to be inseparable from strategy definition itself.
Key Takeaways
- Total latency decomposes into decision, queue, network, and venue components, each contributing to adverse price movement.
- Fill prices should be modeled as the future midprice plus half-spread, market impact, and noise, not the current mid.
- The square-root impact model calibrates execution cost as a function of volatility and participation rate.
- A strategy with a 1.5 Sharpe in a frictionless backtest may collapse below 0.5 after realistic fill modeling.
- PnL arises from signal after implementation. Slippage is part of the strategy definition, not a cost assumption.
Introduction
Backtests are usually too optimistic for one simple reason: they assume the market waited for you. Between the instant a signal is computed and the instant an order is filled, several things happen. Threads wake up, messages are serialized, risk checks run, gateways forward packets, the venue processes the order, and other participants move the book. By the time the fill occurs, the price you thought you traded may no longer exist.
Latency Decomposition
A practical fill model for a buy order (where \(M_t\) is the reference midprice at decision time):
Market Impact
A commonly used specification for market impact uses a square-root law:
where \(\sigma\) is volatility, \(V\) is available volume, and \(\eta\) is a calibrated coefficient.
Python Fill Simulator
import numpy as np def simulate_fill(mid, spread, sigma, q, V, latency_ms, side, eta=0.1): # side: +1 for buy, -1 for sell impact = eta * sigma * np.sqrt(max(q, 1) / max(V, 1)) noise = np.random.normal(0, spread * 0.05) # latency drift: price can move during delay drift = np.random.normal(0, sigma * np.sqrt(latency_ms / 1000.0)) future_mid = mid + drift fill = future_mid + side * (0.5 * spread + impact) + noise return fill # Example for side in [1, -1]: f = simulate_fill( mid=100.0, spread=0.02, sigma=0.01, q=10_000, V=1_000_000, latency_ms=8, side=side ) print(ff"Fill ({'buy' if side > 0 else 'sell'}): {f:.4f}")
Implications
A strategy with a 1.5 Sharpe ratio in a frictionless backtest may collapse below 0.5 after realistic fill modeling. Mean reversion strategies are especially vulnerable because edge decays quickly and costs are frequent. The broader lesson: PnL does not arise from signal alone — it arises from signal after implementation. Slippage and latency are not "cost assumptions." They are part of the strategy definition.