Overview
A script repaints when its historical values differ from what it showed in real time on the same bars. The Strategy Tester accepts repainted data and produces profitable-looking results that do not survive live trading. Repainting comes from two places: data that was unknown when the bar formed, and realtime bars that update tick by tick while historical bars execute once.
Request higher-timeframe data with [1] and lookahead_on together
Use close[1] with barmerge.lookahead_on to get the last confirmed higher-timeframe value. This is the pattern TradingView documents as behaving the same on historical and realtime bars, and neither half works alone.
//@version=6
indicator("HTF close", overlay = true)
// Repaints: on realtime bars this is the developing HTF bar
repaints = request.security(syminfo.tickerid, "240", close)
// Stable: the last confirmed HTF close, identical on history and realtime
stable = request.security(syminfo.tickerid, "240", close[1], lookahead = barmerge.lookahead_on)
plot(repaints, "Repaints", color.red)
plot(stable, "Stable", color.green)lookahead_onwithout the[1]offset leaks the final value of a higher-timeframe bar into every earlier lower-timeframe bar of that period. Backtests then look prescient and live trading does not.lookahead_off(the default) without the offset shows the developing bar in real time, and the confirmed value in history.- Use
lookahead_onwithout an offset only in labeled research scripts that study future bars on purpose.
Gate signals on confirmed bars
In indicators, combine signals with barstate.isconfirmed so they update only when the bar closes, and send alerts with alert.freq_once_per_bar_close. In strategies, keep calc_on_every_tick = false; with true, realtime bars execute on every tick while history executes at the close, so the strategy repaints.
confirmedCross = ta.crossover(close, ta.sma(close, 50)) and barstate.isconfirmedReading the current bar’s high or low to make a decision on that same bar also repaints, because the extreme is not final until the close.
Choose the barmerge mode for the series you want
barmerge.gaps_off(default) fills lower-timeframe bars with the last higher-timeframe value, so the series has a value on every bar.barmerge.gaps_onreturnsnaexcept on the bar where new higher-timeframe data arrives. Use it to tell “new data” from “data unchanged”.
Test for repainting before trusting a script
Let the script run through several realtime bars, reload the chart, and compare the plotted values on those bars with what you saw live. Any difference is repainting. Never judge a signal from historical bars alone.
Pass multiple series in one request.security call
Each unique request.*() call counts against a per-script limit of 40 (64 on the Ultimate plan), and identical calls reuse data. Fetch several expressions from the same symbol and timeframe with a tuple.
[htfOpen, htfClose] = request.security(syminfo.tickerid, "D", [open[1], close[1]], lookahead = barmerge.lookahead_on)v6 runs request.*() calls dynamically by default, so the symbol and timeframe arguments can be series strings and the calls can sit inside loops and conditionals.