↗
TENSORPARADIGMNEURAL MARKET INTELLIGENCE
TENSORPARADIGM RESEARCH / BACKTEST VALIDITY

How to Detect Lookahead Bias in a Trading Bot Backtest

A practical audit of future data leakage in trading bot backtests, from candle timing and higher timeframes to Python features, train/test splits and order fills.

THE DIRECT ANSWER

Lookahead bias occurs when a backtest uses information that was unavailable when its simulated trading decision was made. To find it, write down when every input became available, when the signal was calculated, and when the order could first fill. Then inspect candle indexing, higher timeframe requests, feature construction, model fitting and the execution simulator. A strategy can be coded correctly in appearance yet trade on tomorrow’s label, today’s unfinished candle or a price already gone.

WHAT TO CHECK FIRST

  • Separate the timestamp of a candle from the time its final value becomes known.
  • A signal using a completed bar cannot be filled earlier in that same bar under an ordinary bar close decision model.
  • Higher timeframe values, centered windows and future shifted features are common leakage paths.
  • Fit scalers and select features on training data within each chronological split.
  • A realistic walk forward result can still fail from costs or overfitting; removing leakage is necessary but does not establish profitability.

Start with the information timeline

For each trade, record four events: when the underlying data was published, when the strategy could read it, when the decision occurred and when an order could fill. A candle’s timestamp often marks its opening, not the time its final high, low and close were known. Exchange data feeds, market calendars and session boundaries can change the exact availability time.

Consider a five minute candle labeled 10:00 that covers 10:00 through just before 10:05. A rule based on its final close cannot know that close at 10:00. If the backtest marks a purchase at the 10:00 open because the same candle eventually closed above a threshold, it has crossed the information boundary. Even filling at the 10:05 close requires a simulator that actually supports that timing; a default bar close strategy often fills a market order on the next available tick, commonly the next bar’s open.

This is a hypothetical timing example, not an actual TensorParadigm trade or performance result.

Symptoms that justify an audit

A smooth equity curve, near perfect turning point entries or returns that collapse after moving a signal one bar later deserve closer inspection. None proves leakage by itself. Market making and some short horizon systems have unusual shapes for other reasons; conversely a leaking strategy can still lose money.

  • Trades appear at the same bar’s high or low while the rule needs that bar’s completed close.
  • One hour indicators change within an earlier five minute bar in a historical test.
  • A feature has a surprisingly strong correlation with a future return or label.
  • A train/test result changes dramatically after preprocessing is fit separately on training data.
  • The backtest records fills before the relevant quote or observation was available.

Check Pine Script higher timeframe requests

TradingView documents that using request.security() for a higher timeframe with barmerge.lookahead_on can expose final higher timeframe values too early on historical bars if the requested expression is not offset. For example, feeding the final one hour close to the first five minute bar in that hour lets the five minute rule see the hour’s ending before it happens.

For a strategy that only needs the previous completed higher timeframe bar, TradingView’s documented non repainting pattern offsets the requested expression by one higher timeframe bar while using lookahead_on. That keeps historical and realtime availability aligned for this use case. Do not copy that pattern indiscriminately into lower timeframe requests or rules that intentionally trade on developing values.

Inspect every request.security() expression, its timeframe, the lookahead setting and the bar index used for decisions. Also review whether calculations are confirmed on the chart timeframe. A corrected higher timeframe input does not repair a simulator that still grants an unrealistically early fill.

Audit Python feature creation before fitting

A centered rolling average borrows observations from both sides of the current timestamp. A negative shift in a feature column can put tomorrow’s value in today’s row. Either may be valid for a label used to score predictions later; neither belongs in the features available to a live decision today.

Make a small table with columns for decision_time, each feature’s source_time and available_time, target_window_end, signal_time and earliest_fill_time. Assert available_time is no later than signal_time for every feature. If you create labels such as next bar return, keep them out of model inputs, joined metadata and preprocessing that can indirectly reveal the answer.

Event data requires its actual release or ingestion time. A funding rate, revised macro series or news item assigned a market timestamp can still arrive later than that timestamp. Point in time snapshots and data revision history matter when those inputs drive trades.

Split chronologically and fit preprocessing inside each fold

A random split lets a model train on later market regimes and test on earlier observations. scikit-learn’s TimeSeriesSplit preserves chronological train/test order and offers a gap between them. Choose a gap based on the label horizon and any overlapping observation windows; it is not a universal fixed number.

Compute scaling, imputation, feature selection and hyperparameter selection using training data only in each split. A scikit-learn Pipeline helps keep transformations inside cross validation. Even an unsupervised scaler trained on the full sample learns properties of the future test period. A final untouched evaluation period helps assess choices made during development.

Repeatedly changing the strategy after seeing the test set is selection bias or overfitting, even when no individual feature contains future data. Keep a record of experiments and distinguish that problem from direct lookahead leakage.

Reconcile signals with simulated order fills

Signal time and fill time are separate. TradingView’s broker emulator normally creates an order on a calculation tick and can fill it no sooner than a subsequent available tick. Its process_orders_on_close option can attempt fills on a bar’s closing tick. Other intrabar settings and order types can also change the execution model, so read the actual strategy settings before declaring a one bar difference a bug.

For each suspicious trade export or log the indicator values used, the bar close confirmation, order creation timestamp, order type and reported fill timestamp and price. Ask whether the indicated price was reachable after the decision. Include spread, commission and slippage assumptions separately: these do not fix leakage, but they affect whether a corrected backtest has any economic value.

Run three falsification checks

First, delay every signal by one completed chart bar and measure how much the result changes. This is a diagnostic, not an automatic correction: a valid fast signal can also weaken when delayed. Second, recalculate indicators one bar at a time in a chronological replay and compare them with the full history computation at each decision timestamp. Third, rerun after rebuilding each training fold independently and freezing the strategy before checking a later period.

When results diverge, trace the first mismatching trade rather than only comparing headline profit. Identify the exact data field or order fill that could not have existed at its stated time. After fixing it, repeat the test with execution costs and reasonable parameter perturbations. A surviving result is better evidence, not a guarantee of live performance.

A concrete audit of one suspect trade

Suppose a bot buys at the 10:00 open because a five minute candle closes above its previous high. Write down the 10:00 open as the proposed fill, the 10:05 completion as the earliest moment its final close is known, and the strategy’s actual decision and order times. The condition depends on information from after the proposed fill, so this recorded trade is invalid under that rule.

If the code is corrected to decide at 10:05, inspect what the execution engine actually does next. A next tick market fill may occur near the following bar’s open; a closing tick option is a different assumption and may not match a live alert arriving after the market moves. Do not keep the old entry price when moving only the signal timestamp.

Now check any one hour filter on the same trade. If it reads the hour’s final close before that hour ends, the five minute entry remains contaminated even after the execution timestamp is repaired. Both input availability and fills have to pass.

What a clean backtest can and cannot tell you

Removing future data leakage restores a basic requirement for a credible test. It does not establish that a strategy will survive regime change, outages, spread widening, funding payments, incomplete liquidity or risk limits. A valid backtest can still be a bad trading system.

Preserve your data versions, strategy code, costs, parameter choices and fold boundaries so the test is reproducible. Report both the suspect result and the corrected result when you discover a defect, with the change explained. A transparent correction is more useful than a polished equity curve.

10 / DUE DILIGENCE

Ten checks before trusting a bot backtest

  1. Record the decision timestamp and availability timestamp for each feature.
  2. Verify whether candle timestamps denote open or close and when final values arrive.
  3. Ensure bar close signals do not receive an earlier bar open fill.
  4. Inspect every higher timeframe request for historical future values.
  5. Search feature code for centered rolling windows and future shifts.
  6. Keep future targets and their proxies out of live features.
  7. Use chronological splits and sufficient separation for overlapping targets.
  8. Fit every learned preprocessing step within each training fold.
  9. Reconcile order creation, type and fill time against available prices.
  10. Replay sequentially, include trading costs and preserve a later untouched test.
FAQ / DIRECT ANSWERS

Common questions about lookahead bias

What is lookahead bias in a trading bot?

It is the use of data in a simulated decision before that data would have been available in live trading. Examples include a candle’s final close used at its open, tomorrow’s return in a feature or a later higher timeframe close exposed early.

Is using the current candle’s close always lookahead bias?

No. A system that makes its decision after the candle closes can use the completed close. The problem arises when the test claims that decision or fill happened earlier, or assumes an unavailable closing price for execution.

Does request.security() always repaint?

No. The risk depends on timeframe, expression and lookahead settings. TradingView warns that lookahead_on with an unoffset higher timeframe expression can leak future historical values. Review the exact request and realtime behavior.

Does TimeSeriesSplit eliminate all leakage?

No. It preserves chronological training and testing, but features, publication times, overlapping labels, fitted transformations and execution still need separate checks. A suitable gap may be necessary for overlapping windows.

How is lookahead bias different from overfitting?

Lookahead gives a historical decision information it could not have known. Overfitting is selecting rules that happen to match past noise or repeated tests. Both can inflate results, and both can occur together.

Can a strategy remain profitable after removing leakage?

Possibly, but a valid timeline alone does not show an edge. Evaluate a later untouched period, plausible fills and costs, drawdown, trade count and robustness before making any live decision.

Sources and methodology

Platform documentation for historical higher timeframe behavior and simulated order timing, plus scikit-learn guidance for chronological validation and leakage prevention. The trade timeline and audit procedure are illustrative analysis by TensorParadigm.

  1. TradingView Pine Script: Other timeframes and data
  2. TradingView Pine Script: Strategies and order execution
  3. scikit-learn: TimeSeriesSplit
  4. scikit-learn: Common pitfalls and data leakage

Educational research only. This article does not provide investment advice or represent live results for TensorParadigm.