How a Backtesting Engine Works: Event Loop, Fills and Costs
Inside a backtest: the event loop, how fills are simulated, why lookahead bias appears, and what separates a useful engine from a plausible one.
What does a backtesting engine actually do?
It replays historical market data in order, presenting each observation to the strategy exactly as if it had just arrived, collecting the resulting intents and simulating their execution, then reports the trades and the equity curve that would have followed.
The word "exactly" carries the design. Every property that makes a backtest trustworthy comes from the engine's discipline about what the strategy can see at each step.
The event loop, reduced to its essentials
for candle in ordered(history):
# 1. advance the clock. the strategy cannot read beyond it
clock = candle.timestamp
# 2. resolve orders placed on PREVIOUS iterations.
# this ordering is the point: an order decided at t
# cannot fill at t's close, only at t+1's open or later.
fills = simulate_fills(open_orders, candle)
portfolio.apply(fills, costs=cost_model(fills))
# 3. NOW evaluate. the strategy sees candles up to and
# including this one, and nothing after it.
intent = strategy.evaluate(portfolio.state, view_until(clock))
# 4. gate and queue. not filled in this iteration.
if intent and risk_gate.approve(intent, portfolio):
open_orders.append(to_order(intent, clock))Why is the order of steps 2 and 3 so important?
Because resolving fills before evaluating the strategy is what prevents an order from being filled at a price the strategy used to decide on it. Reverse the two and you get an engine that reports edge no live system can reproduce.
This is lookahead bias in its most common form, and it is easy to introduce accidentally when the loop is written for convenience rather than correctness. Making it structural, the strategy is handed a view of data that physically ends at the current timestamp, removes an entire class of bug.
How should fills be simulated?
Conservatively, and transparently. A market order decided on candle N should fill at candle N+1's open, adjusted for slippage. Filling at candle N's close assumes you traded at a price you only knew after the fact.
Limit orders need more care: a fill is only plausible if the candle's range actually traded through your price, and even then a touch is not a guarantee in a thin market. Engines that fill any limit whose price falls inside the candle range are generous in a way that shows up as a backtest edge and a live loss.
Fill assumptions, ranked by how much they overstate results
| Assumption | Realism | Effect on results |
|---|---|---|
| Fill at signal candle close | None, uses unknowable data | Large phantom edge |
| Fill at next open, no slippage | Partial | Optimistic, scales with trade frequency |
| Fill at next open + fixed slippage | Reasonable baseline | Usable |
| Fill at next open + volatility-scaled slippage | Better | Penalises trading in fast markets, correctly |
| Limit filled if price touched | Weak in thin books | Overstates limit-order strategies |
What is the difference between vectorised and event-driven backtests?
A vectorised backtest computes signals across the whole series at once, fast, but unable to represent state that depends on the path taken. An event-driven backtest steps candle by candle and can model trailing stops, partial fills and position-dependent sizing.
Vectorised runs are excellent for screening many parameter sets quickly. They cannot correctly model a trailing stop, because the stop's level depends on the highest price reached since entry, a path-dependent quantity. If a strategy uses one and the backtest is vectorised, the result is not describing that strategy.
What should an engine disclose?
Its fill model, its cost model, the data resolution, and the exact window tested. Without those four, a return figure is not interpretable and cannot be compared with anything.
This is a reasonable test to apply to any platform, including this one: if you cannot find out how fills were simulated, you cannot know what the number means.
References
- Advances in Financial Machine Learning, on backtest overfittingMarcos López de Prado, Wiley
- The Probability of Backtest OverfittingBailey, Borwein, López de Prado, Zhu, SSRN
- NSE, historical data and trading holidaysNational Stock Exchange of India
- backtesting
- architecture
- engineering
- validation
Topics
Written by
The Stretus team writing on algorithmic trading, market structure and the systems that sit between a strategy and an exchange. Every claim about the platform links to the documentation that specifies it.
See these ideas running
The platform implements the workflow described here: strategies expressed as rules, backtested against historical data, paper traded, then executed through authorized broker APIs under platform-level risk controls.
Related reading
The Architecture of an Automated Trading System
The six subsystems between a strategy definition and a filled order, what each is responsible for, and where the hard failure modes live.
How Backtesting Works, And What It Can't Tell You
Backtesting replays a strategy against historical data. What the process does, why results look better than they were, and how to read a report honestly.
What a Five-Year Order Audit Trail Has to Contain
The retention obligation, the fields an inspection actually asks for, and why "comprehensive logging" is not an answer to the question.