Education8 min read2026-09-17

Look-Ahead Bias in Backtesting: 7 Real Examples

Seven concrete ways look-ahead bias leaks future information into a backtest, from same-bar fills and repainting to full-sample tuning, and how to find and fix each one.

Roboquant

Roboquant Team

Trading Automation Experts

backtestinglook-ahead biasrepaintingoverfittingdata quality
Look-Ahead Bias in Backtesting: 7 Real Examples

What Look-Ahead Bias Is

Look-ahead bias happens when a backtest uses information that wasn't available at the moment the decision was made. The strategy "knows" something from the future: a closing price, a daily high, a confirmed pivot, the best parameter set. Live trading can't reproduce that knowledge, so the live results can't match.

It's sometimes called a future leak, and it's one of the most common reasons a backtest looks far better than anything you can trade. The trouble is that it rarely looks like a bug. The code runs, the equity curve is smooth, and every trade has a plausible reason.

Here are seven concrete ways it gets into a backtest, how to spot each one, and how to fix it.

Example 1: Deciding on the Close, Filling at the Open

The leak. The strategy checks whether a bar closed above a moving average, then enters at that same bar's open. The open happened before the close existed, so the entry uses a price from before the signal could be known.

A softer version: the strategy decides on the close and fills exactly at that close. In live trading, by the time a bar has closed and your order reaches the market, price has moved on. Whether that's a small assumption or a large one depends on how fast the market moves and how small your average trade is.

How to spot it. Entries sit at or near the best price of the signal bar far more often than chance would allow.

Fix. A signal computed from a closed bar can only trade after that bar closes: at the next bar's open, or on the next price after the close.

Example 2: Reading a Higher-Timeframe Value Too Early

The leak. A 5-minute strategy filters trades with the daily trend: "only buy when today's close is above the 20-day average". At 10:00 in the morning, today's close doesn't exist yet. A backtest that reads the completed daily bar during the day uses the end of the day to decide trades at its start.

TradingView's Pine Script documentation describes this risk for request.security(): using barmerge.lookahead_on without offsetting the requested value can leak future data into historical bars. Its Other timeframes and data page shows the pattern and the offset that avoids it.

How to spot it. The filter looks almost perfect on history: it seems to know which days will trend.

Fix. Use the previous completed higher-timeframe bar until the current one closes.

Example 3: Knowing the Day's High or Low Early

The leak. A strategy buys when price breaks "the high of the day", or fades moves near "the low of the day", using the day's final high and low. Those values are only known after the session ends. Similar leaks come from session ranges measured one bar too long, such as an opening range that includes the first bar after the range should have ended.

How to spot it. Trades enter right at the day's extreme and rarely see adverse movement afterwards.

Fix. Track the running high and low as the session progresses, and use only bars that have already closed.

Example 4: Data That Didn't Exist Back Then

The leak. The data set itself contains knowledge from later dates. Two common forms:

  • Survivorship. Testing only on markets or instruments that still exist or are still popular today quietly removes the ones that were delisted, stopped trading or fell out of favour.
  • Adjusted or stitched history. Continuous futures series are built from several contracts. When the series is back-adjusted, past prices are shifted by roll gaps that happened later. Rules that use absolute price levels or percentage moves on adjusted prices are reading numbers no trader saw at the time.

How to spot it. Results change noticeably when you switch between adjusted and unadjusted data, or add instruments that no longer trade.

Fix. Know how your data was built. Use price levels only on data where they mean what traders saw, choose the test universe by rules you could have applied at the time, and treat roll dates explicitly.

Example 5: Indicators That Repaint

The leak. Some indicators change their past values after the fact. A swing high, for example, is only confirmed once several bars to its right have printed lower highs, yet many pivot tools mark it on the bar where it happened. A backtest that trades the pivot on that bar enters before the pivot could have been known.

Repainting also appears when a signal is computed on a bar that is still forming. TradingView's Repainting page explains the difference between historical and realtime calculations and lists common causes, including higher-timeframe requests and scripts that recalculate on every tick.

How to spot it. Signals shown on historical bars appear later, move, or vanish when you watch the same indicator in real time or in bar-by-bar replay.

Fix. Act on a pivot only after its confirmation bars have closed, and base signals on confirmed bar values.

Example 6: Tuning on the Whole Sample

The leak. This one isn't in the code. You try hundreds of parameter sets over the full history and report the best one. The chosen parameters were selected with knowledge of every trade in the test, including the last year. The same thing happens when a strategy normalises data with statistics from the whole data set, such as a volatility threshold set at a percentile of the entire history.

How to spot it. Results collapse on data that wasn't used for tuning, or the best parameters sit on a sharp, isolated peak.

Fix. Keep an out-of-sample period you never tune on, or use walk-forward validation. Compute thresholds and statistics only from data before each decision.

Example 7: Misaligned Timestamps and Timezones

The leak. Two data sources are joined with different conventions. A bar stamped with its open time is treated as if it were stamped with its close time, so an hourly bar's close appears available at the start of the hour. Daily data stamped at midnight gets joined to intraday bars from the same date, making the day's close available at its first minute. Session rules written in one timezone are compared with timestamps in another, shifting everything by hours.

How to spot it. Trades cluster just before large moves, or results change when you shift a data source by one bar.

Fix. Check what every timestamp means (bar open or bar close) and which timezone it uses before joining anything. Shift the slower data so a value is only available after the period it describes has ended.

A Quick Look-Ahead Audit

Run these checks on any backtest before you trust it:

  1. Look at the best trades on the chart. For each one, ask what the strategy knew at the moment of entry.
  2. Shift the signal by one bar. If a one-bar delay destroys the results, the edge may have come from information that arrived with that bar.
  3. Replay it bar by bar. Signals that change or appear late in replay are repainting.
  4. Check every higher-timeframe and external data join for timestamp meaning and timezone.
  5. Test on data you didn't tune on.
  6. Compare with live or demo trading over the same days. A large, one-sided gap points at look-ahead or fill assumptions; see Why Your Backtest Doesn't Match Live Trading.

How Roboquant's Engine Handles Causality

Several of these leaks come from a strategy being able to see data it shouldn't. In Roboquant, the engine controls market data and time, and it enforces causality by design:

  • Bar strategies see closed bars. In on_bar, the bar supplied to the strategy is the bar that just closed, so orders sent there are placed after that bar is complete.
  • The forming bar never contains the future. In tick, timer and trade hooks, the current bar is the forming bar as known at that instant, never its final close or extremes. Indicators read during those hooks stay anchored to the last completed bar.
  • Higher-timeframe indicators wait. They expose the previous completed higher-timeframe value until the next higher-timeframe bar closes.
  • Tick orders don't read future prints.
  • Timestamps are explicit. Engine timestamps are UTC, a bar's time is its open time, and Eastern-time helpers handle daylight saving time for New York session rules.

The engine can't protect you from everything. Tuning on the full sample (example 6) is a research choice, so use in-sample/out-of-sample or walk-forward validation in the Optimize tab, depending on your plan. And you still decide what the strategy should do: compiling proves the code is valid, not that its rules are the ones you meant. The engine overview and runtime reference document the causality rules.

Checklist

  • Signals from closed bars trade only after those bars close
  • Higher-timeframe values come from completed bars only
  • Daily and session highs and lows are running values, not final ones
  • You know how your data was built: adjustments, rolls, which instruments are included
  • Pivots and other confirmed patterns are used only after confirmation
  • Parameters and thresholds were chosen without the test period
  • Every timestamp's meaning and timezone is known

Related Reading

Try It

Describe a strategy in plain English, and test it on an engine that only shows your strategy what was known at the time.

Start building ยท See pricing

Trading involves risk of loss. Backtest results are hypothetical and do not guarantee future performance.

Share this article:

Build Your Next Strategy with Roboquant

Chat an idea into a strategy, then backtest, optimize and deploy it live. All in one place.