On Friday 31 May 2024 the Nifty 50 closed at 22,530.70. The next session, Monday 3 June, it closed at 23,263.90, up 3.25% on the weekend’s exit polls.
Now run the same trade twice.
Version one is what a lot of retail backtests do. The rule reads Friday’s close, decides to buy, and fills at that same close of 22,530.70. By Monday’s close the position shows a gain of 3.25%.
Version two is what a live system does. The rule reads Friday’s close after the market has shut, the order sits overnight, and it fills at Monday’s open of 23,337.90. Monday closed at 23,263.90. The position finished the session down 0.32%.
Same signal, same index, same session. The two versions are 3.58 percentage points apart on a single entry, and the difference is not brokerage and not slippage. Version one bought at a price that had stopped existing by the time the decision could be acted on.
What execution timing actually decides
A backtest makes two separate choices on every trade, and it is easy to notice only the first.
The first is the decision moment: which bar’s data the rule is allowed to read. The second is the fill: which price the order is assumed to transact at. Execution timing is the rule that maps one to the other, and it is the setting that determines whether the equity curve describes a portfolio a human could have held.
There are two settings worth naming. same_bar_close evaluates the rule on a bar and fills at that bar’s closing price. next_bar_open evaluates the rule on a completed bar and fills at the opening price of the following one.
Only one of them is physically achievable.
Why a fill at the signal bar’s close is impossible
Walk the clock forward on a real NSE session.
Your rule reads close. The closing price is not knowable while the session is running; it is determined once trading has finished. So the earliest moment your rule can evaluate is after the market has closed. To fill at that close, your order needed to already be resting in the book and already matched, before the price your rule consumed had been established.
You are being asked to place an order using a number that will not exist until after the order has to have been placed. That is not a modelling approximation you can shrink with better assumptions. It is a sequence violation.
The same logic holds at every bar size. If a rule reads the close of a 5-minute bar, it cannot fill inside that same 5-minute bar. The decision has to precede the fill, and the first tradeable price after a completed bar is the next bar’s open.
This is look-ahead bias wearing a smaller costume
Most people learn look-ahead bias through obvious cases. Ranking stocks by full-year revenue growth on 1 January. Using an index constituent list that reflects today’s membership. Screening on a figure that was filed six weeks after the date the backtest reads it.
Filling at the signal bar’s close belongs to the same family and gets caught far less often, because the offending data is only a few hours early rather than a few months. The consequence is identical. The backtest consumes information at a moment when it was not available to act on, and the returns it reports are returns nobody could have captured.
There is one useful diagnostic. Run a strategy under both timings and look at the difference. If the same-bar-close version is dramatically better, the strategy’s edge is concentrated in the window between the signal and the next open, which is precisely the window a live system cannot trade in. That comparison is a diagnostic, not a result. The same_bar_close number is not a number you may quote.
The overnight gap is an exposure, not a rounding error
The next session after that Monday rally, 4 June 2024, the Nifty 50 opened at 23,179.50 and closed at 21,884.50, down 5.93% and its worst day in more than four years. Opening and closing levels for both sessions are from Nifty 50 historical price data. An investor who had entered at Monday’s open was down 6.2% by Tuesday’s close.
That is what next_bar_open correctly hands you. Between one session’s close and the next session’s open, the market is shut and your position is not. Earnings land. Global markets move. Election counts come in. Regulators publish orders. None of it can be traded around, and all of it is priced into the opening print.
A backtest that fills at the signal bar’s close never experiences this. It steps from decision to fill with no interval in between, so the overnight gap is silently deleted from its risk profile. Reported drawdowns come out smaller, and the smaller number is an artefact of the timing setting rather than a property of the strategy.
The gap is not a coin flip either, and this is the part that matters most. Your signal is computed from information the market has already seen. The overnight reaction to that same information is exactly what moves the open. So a fill at the signal bar’s close systematically captures the market’s reaction to the news your rule selected on. Averaged over hundreds of trades, that is a persistent bias in your favour, not random noise that cancels out.
Where the distortion is largest
The size of the error depends on what your rule selects on.
A quarterly-rebalanced quality screen that ranks the Nifty 500 on ROCE and debt-to-equity will barely notice. Four rebalances a year, and the names it picks are not picked because they moved yesterday. The difference between a close fill and a next-open fill is roughly a day of ordinary noise, four times a year.
Now take a strategy that selects on large daily moves. A breakout rule using close > @MAX(high, 20). A gap-continuation screen. An event trigger that acts on results day. A reversal rule that buys after a limit-down session. Every one of these selects a name because something happened to it, which means the overnight gap on the selected names is systematically larger and systematically directional. Conditional on selection, the gap is where the strategy’s apparent edge lives.
Here is illustrative arithmetic, not a backtest result. Suppose a daily breakout strategy takes 60 entries a year, and on the names it selects the overnight gap averages 0.4% in the signal’s direction. A same-bar-close fill hands that strategy roughly 24 percentage points of annual return that a live system could never have collected. Change the assumption to 0.2% and it is still 12 points. There is no cost model, no slippage setting, and no parameter tune that recovers a strategy which has lost that much.
The uncomfortable corollary: the strategies most likely to be built with a same-bar-close fill are the ones the error damages most, because “buy what closed strong” is an obvious thing to write and an obvious thing to fill at the close.
Intraday strategies and what a daily bar can carry
A daily bar carries four prices and one moment on the engine’s clock. That single moment is the constraint people run into first when they try to build an intraday strategy without intraday data.
You can enter at a daily bar’s open and square off at that bar’s close, because square-off is a session-level action rather than a second rule evaluation. The signal comes from the previous completed session, the entry fills at today’s open, and the whole book is flattened before the close because the session is ending. Every number in that sequence was knowable when it was used. The mechanics of that construction, and the cost arithmetic that decides whether it survives, are covered in backtesting an intraday strategy honestly.
What a daily bar cannot do is host two independent decisions. An intraday stop, a midday re-rank, an exit rule that fires when the position moves 1.5% against you: none of these are expressible on a bar that only gives the engine one moment per session. The bar has a high and a low, but it has no path, and inferring “the stop must have triggered because the low was below it” is a guess about ordering that the data does not support.
If the decision you want to make happens inside a session, you need bars that are shorter than the session. The engine runs on daily, hourly, 15-minute, 5-minute and 1-minute bars, and next_bar_open means the same thing at every one of them: decide on a completed bar, fill at the open of the next.
Variations you can defend
Next-bar-open is the baseline. There are a few honest departures from it, and each buys realism at the cost of some return.
| Setting | What it assumes | When it is defensible |
|---|---|---|
| Next bar’s open | Your order is in the queue before the session starts and participates in the opening | The default for liquid names |
| Open of the bar after next | You need a session to place the order rather than an overnight queue | Manual execution, or a workflow with an approval step |
| Open plus a fixed slippage haircut | The opening print is not the price you get | Always, and the haircut should be larger than your mid-session assumption |
| Volume-weighted price over the first N minutes | You worked the order rather than crossing the spread | Larger orders, and only if you model the participation constraint too |
What none of these change is the ordering. Every one of them fills strictly after the bar the decision was made on. A setting that fills before or at the decision bar is not a variation, it is the bug.
Risks and failure modes
The open is the least orderly price of the day. Spreads are widest, overnight information is still being priced, and every queued order competes at once. Whatever slippage figure you use for mid-session execution, the open deserves a larger one. A market order into the open is the most expensive way to express confidence.
In thin names, the opening print can be one small trade. Your backtest records a fill at that price and your live order gets done several percent away. A participation cap that limits a single day’s trade in one name to a small fraction of that bar’s volume is what keeps the modelled fill connected to reality.
Correct execution timing does not fix look-ahead in your data. You can fill at the next open and still be reading a March-quarter earnings figure on 1 April, six weeks before it was filed. The two problems are independent, and the data one is covered in point-in-time data and look-ahead bias.
A smaller max drawdown under same-bar-close is not a safer strategy. It is the same strategy with the overnight gaps removed. If you have been comparing candidate strategies on drawdown while the timing setting varied between them, the comparison was measuring the setting.
Gap risk is now genuinely yours, and it should change your position sizing. A concentrated book of ten names entered at the open carries real overnight exposure to single-stock news. That is a reason to think about position limits, not a reason to go back to a fill you cannot achieve.
High-turnover versions still die on costs. Fixing execution timing makes a strategy honest, not profitable. A daily-rebalanced book paying a full round trip on every name is usually a cost problem before it is a signal problem.
How saral.money handles it
The Saral Engine fills orders at the next bar’s open. That is the default rather than an option you have to find, and the reason is structural: the same code path runs a backtest and a live deployment, so anything the backtest does has to be something a live system can physically do. A fill at a close you are still observing is not one of those things. Strategies run on daily, hourly, 15-minute, 5-minute and 1-minute bars, with slippage and transaction costs modelled per trade. Live and paper deployment is in preview behind a per-user flag rather than generally available, but the execution semantics you backtest against are the ones it inherits, which is the whole point of sharing the path. You can see the rest of the engine’s contracts on the features page.
What to try next
Take a strategy you already believe in and check one thing: does its edge survive the move from the signal bar’s close to the next bar’s open? Run it both ways, look at the difference, and treat the gap between the two as a measurement of how much of your result was living in a window you cannot trade. If the strategy selects on large daily moves, expect the gap to be wide. Then start from a strategy template that already fills correctly and build from there.
Further reading
- Backtesting trading strategies: a step-by-step guide covers the other biases that sit alongside this one.
- Point-in-time data and look-ahead bias is the data-side companion to this post.
- Cognitive biases that destroy algo trading strategies on why an inflated backtest is so hard to disbelieve once you have seen it.
- Glossary: look-ahead bias, slippage, square-off, market order, backtesting.