Pine Repaint, Lookahead & request.security (Basic Has No MTF)

Why signals vanish, lookahead traps, and why TradingView Basic does not add multi-TF security stacks in this academy.

📖 6 min read

📝 1,144 words

🏷️ Pine Script and TradingView

Share this article:

What repainting actually means

Three timeframes stacked: the higher one sets context, the lower one times the entry.H4trend and contextH1confirmationM15entry timing
How a multi-timeframe rule reads: context above, timing below.

Repainting is when a script's output on a bar differs depending on when it was calculated. The same bar, the same script, two different answers — one while the bar was forming, another after it closed and the script recalculated from history.

It follows from something structural rather than from a mistake. A historical bar holds four numbers: open, high, low, close. A realtime bar holds a close that is still moving, and a high and low that can still extend [1]. A script reading high on a forming bar is reading a value that is not final. When the script later reruns over that bar as history, it reads the final one.

Not all repainting is a defect

This is the part that gets lost, and getting it wrong in either direction is expensive.

Repainting that is working as intended: an indicator that updates as the current bar develops. You want that. A signal that only appears after the close is a signal you cannot act on at the close.

Repainting that invalidates a backtest: a script whose historical values could not have been known at the time they are plotted. This is the one that makes an equity curve look extraordinary and mean nothing, because the historical run had information the live run will never have.

The test is not "does it change" — it is "could this value have been known on that bar, at that moment, in real time?" If no, the backtest is measuring a strategy that cannot exist.

TradingView is direct about the worst version: scripts that leak future data into history are "extremely misleading", and publishing them is not allowed [2].


The ten documented causes

TradingView documents ten distinct causes [1]. They are not equally serious, so they are grouped here by what they do to a backtest rather than by language feature.

Fatal — the backtest is measuring something impossible

CauseWhat happens
Future leak via request.security()Using lookahead without offsetting reveals prices from beyond the current bar, on historical bars only
Plotting in the pastDrawing onto earlier bars retroactively, so the chart shows a detection that did not happen at that time

Both produce a historical record that could not have existed live. Nothing else on this list is in the same category.

Structural — expect a difference, and account for it

CauseWhat happens
Fluid data valuesHistorical bars hold only OHLC; a realtime bar's high, low and close are still moving
Repainting request.security() callsReturns an unconfirmed higher-timeframe value on realtime bars, which differs once the script restarts
Lower-timeframe request.security()Intrabars are not sorted in realtime, so realtime behaviour cannot be reproduced from history
calc_on_every_tick strategiesExecute on every tick in realtime but only at bar close historically

These four are the ordinary asymmetry between a bar that is finished and a bar that is not. They do not make a strategy fake; they make a backtest an approximation, and the size of the gap is worth knowing before trusting a result.

Design-dependent — correct until you assume otherwise

CauseWhat happens
varip declarationsKeep state across realtime updates in a way history cannot reproduce
Bar-state built-insbarstate.isnew and its siblings behave differently in realtime
timenowNecessarily differs between a historical run and a live one
Dataset variationsWhere the chart's history begins, and later data revisions, change calculations over time

The last one catches people who are sure their script changed when nothing in it did. A different chart start point, or a revision to the feed, is enough to move a result [1].

The question to ask of each

For every one of these, the useful test is the same: would a live run at that moment have had this value? The two in the fatal group answer no. The rest answer yes, with a timing difference you can measure.


Lookahead and the future leak

request.security() takes a lookahead parameter with two possible values [2]:

On historical barsOn realtime bars
barmerge.lookahead_off (default)the last confirmed higher-timeframe valuethe symbol's current unconfirmed value
barmerge.lookahead_onvalues from times beyond the bar it executes onthe same unconfirmed value as lookahead_off

Read the second row twice. On history, lookahead_on hands the script data from the future. In realtime it cannot, because that future does not exist yet — so it returns exactly what lookahead_off would.

That asymmetry is the whole problem. The historical half of the run is answering with information the live half will never have, and both halves are the same script. The backtest is not optimistic; it is describing a different strategy.

TradingView's own wording: programmers should exercise "extreme caution" with lookahead, especially on higher timeframes [2].

Using it correctly

Lookahead is not forbidden, and there is a documented pattern that makes it safe. Two things together [2]: offset the requested expression with the history-referencing operator, and enable lookahead.

pine
float htfPrice = request.security(syminfo.tickerid, timeframe,
    high[1], lookahead = barmerge.lookahead_on)

The [1] is what makes this sound. Asking for the previous higher-timeframe bar's high, with lookahead on, gives a value that was genuinely available at that point — so the series behaves identically on historical and realtime bars, and does not repaint [2].

Without the offset, the same call with lookahead_on is the future leak.

If you did not write the script

Two checks, in order, before trusting any strategy you did not write:

  1. Search it for lookahead. If barmerge.lookahead_on appears, find the expression being requested and confirm it is offset — high[1], not high.
  2. Check whether it uses request.security() at all. If it does and the backtest looks remarkable, this is the first thing to rule out.

Publishing a script that leaks future data into history is not permitted on TradingView [2], which tells you how common the mistake is.



Tactix AI on this workflow

AlfaTactix includes Tactix AI: use Tactix Studio to describe your idea in plain language and draft timeframes, signals, filters, and risk into the same six-step Strategy Builder form — or open Tactix Guide on any step when you only need a field explained. Review every value, then export MQL5 or Pine Script from Code Generator (form-first — not untested prompt-to-code).

Frequently Asked Questions

Often repaint: the condition was true intrabar and false at close. Prefer confirmed-bar logic. See the repainting documentation [1] · Tester vs live.

Using future information (common with careless request.security). AlfaTactix Basic exports avoid MTF security stacks — filters.

Not in this product track. Do not hand-add multi-TF for “better signals.” Map: same strategy MT5 vs TV.

A common pattern to act on closed bars — understand it before pasting from forums. v6 [2].

No dedicated twin — TV-specific. Closest ops caution: Tester vs live MT5.

Prefer builder conditions — production-ready · errors.