TradingView Repainting: Why Pine Script Strategies Repaint and How to Fix It
A strategy that looks perfect in backtests and falls apart live is usually repainting. What repainting is, three tests to catch it, and five Pine Script v6 fixes, with code.
If you have ever built a strategy in TradingView and thought "this backtest is incredible", only to watch it trade nothing like that live, the most likely culprit is repainting.
Repainting makes a script look better on history than it can ever be in real time. Signals appear exactly at the turn, stops sit just out of reach of every spike, and the equity curve climbs in a straight line. Then the script runs on live bars and the magic is gone, because on live bars it cannot see what it saw on history.
This guide explains what repainting is, how to test any script for it in a few minutes, and five fixes in Pine Script v6. It closes with what repainting does to an automated strategy, which is where it gets expensive.
What is repainting?
TradingView's Pine Script manual defines repainting as script behavior that makes historical and real-time calculations or plots behave differently.
The root cause is simple. On a historical bar, every value is final: the bar has closed, and its open, high, low and close will never change. On the real-time bar, the one forming now, close is just the latest price. It changes on every tick until the bar closes. So is every value calculated from it: an RSI, a moving average, an ATR, a crossover.
A script that makes decisions from values that are still changing will behave differently live than it did on history. That difference is repainting.
Not all repainting is bad
Almost every script repaints in the narrow sense, because almost every script reads the live bar. That is fine for a chart you are watching. It becomes a problem when it changes signals after the fact or quietly uses information that was not available at the time.
TradingView's manual estimates that more than 95% of indicators repaint in some form, and sorts repainting into four kinds:
| Kind | What it looks like | Acceptable? |
|---|---|---|
| Widespread | Values on the live bar update until it closes | Usually, if you understand it |
| Potentially misleading | Signals plotted into the past, pivots, strategies calculating on every tick | Not for automation |
| Unacceptable | Future data leaking onto historical bars, strategies run on non-standard charts, orders from live intrabar values | Never |
| Unavoidable | Data feed revisions, and a chart's history starting on a different bar | Out of your control |
The goal is not zero repainting. It is no signals that change after you could have acted on them, and no future data on history. Note the non-standard charts entry: a strategy backtested on Renko, Kagi or Heikin Ashi bars is in the unacceptable group, because those bars are built from prices that were not tradeable at the times shown.
How to tell if a script repaints: three tests
You do not need to read the code to find out. Three tests catch almost every case.
1. The bar replay test. Open the chart, start TradingView's Bar Replay a few days back, and step forward bar by bar. Watch where signals appear. Then stop the replay and look at the same stretch on the normal chart. If signals sit on different bars, or appear on history that never appeared during replay, the script repaints.
2. The reload test. Leave the script on a live chart for a session and screenshot its signals. Reload the page, or remove and re-add the script, and compare. Signals that moved or vanished are repainting.
3. The alert log test. Let the script fire real alerts for a day, even to nowhere. Compare the alert log's timestamps with the signals drawn on the chart afterwards. Every signal on the chart should have a matching alert at the time the bar closed. Extra signals on the chart that never alerted, or alerts with no signal left on the chart, are the tell.
If a script fails any of these, its backtest is not telling you what it would have done live.
Five fixes in Pine Script v6
1. Lock stops and targets at entry
This is the most common way a strategy flatters itself. It recalculates its exits on every bar:
//@version=6
strategy("Moving exits", overlay = true)
atr = ta.atr(14)
if ta.crossover(ta.sma(close, 10), ta.sma(close, 30))
strategy.entry("Long", strategy.long)
strategy.exit("TP/SL", from_entry = "Long", stop = close - atr * 1.5, limit = close + atr * 2.0)
Because strategy.exit() runs on every bar with the current close, the stop and target move with price for the life of the trade. Store them once, at entry, instead:
//@version=6
strategy("Locked exits", overlay = true)
atr = ta.atr(14)
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
var float sl = na
var float tp = na
if ta.crossover(fast, slow) and strategy.position_size == 0
sl := close - atr * 1.5
tp := close + atr * 2.0
strategy.entry("Long", strategy.long)
strategy.exit("Exit", from_entry = "Long", stop = sl, limit = tp)
var keeps the values from bar to bar, so the exits stay where the trade put them.
2. Act only on confirmed bars
A crossover on the live bar can appear and disappear several times before the bar closes. barstate.isconfirmed is true only on a bar's final update, so gating on it means a signal only counts once it can no longer change:
if barstate.isconfirmed and ta.crossover(fast, slow)
strategy.entry("Long", strategy.long)
Strategies already calculate once per bar close by default. This matters most in indicators, and in strategies where you have turned on calc_on_every_tick.
3. Request higher timeframes without future data
request.security() is the classic source of the unacceptable kind of repainting. Asking for a daily value on an intraday chart can hand historical bars the day's final value before the day was over. The pattern TradingView's manual recommends for a non-repainting request is to ask for the previous completed value with lookahead turned on:
//@version=6
indicator("Previous day high, non-repainting", overlay = true)
prevDayHigh = request.security(syminfo.tickerid, "D", high[1], lookahead = barmerge.lookahead_on)
plot(prevDayHigh, "Previous day high", color.red)
The [1] asks for the last finished day, and lookahead_on places it correctly on every bar, so history and real time show the same line.
4. Build decisions on finished values
When a signal does not need the live bar, use the previous one. close[1], high[1] and an indicator's [1] value are final and cannot change:
longSignal = ta.crossover(fast[1], slow[1])
You trade one bar later, and what you backtest is what you get.
5. Fire alerts once per bar close
An indicator alert that fires on every tick can send several orders for one flickering signal. Fire it once, when the bar closes:
if ta.crossover(fast, slow)
alert("Buy signal", alert.freq_once_per_bar_close)
When creating the alert in TradingView, choose the matching Once per bar close option for condition-based alerts too.
What repainting does to an automated strategy
On a chart, repainting costs you a misleading backtest. In automation it costs money, because the live alerts are the only version of the strategy that trades.
A repainting script sends signals the backtest never showed, and misses ones it did. A strategy whose signals flicker inside a bar can send an entry and then an exit for a trade that no longer exists on the chart. And your broker position stops matching what TradingView thinks it holds.
Two habits contain the damage. Fix the script first, using the tests above. Then, when automating it through CrossTrade, send the strategy's position with every alert and turn on Strategy Sync, which checks the live account against what TradingView expects before each entry and corrects or skips the order when they disagree. It cannot make a repainting strategy profitable, but it stops one from building positions nobody intended.
Frequently asked questions
What does repainting mean in TradingView?
A script behaves differently on historical bars than on the live bar, so its signals or values change after the fact. Backtests of repainting scripts look better than live trading can.
How do I know if an indicator repaints?
Run Bar Replay and compare its signals with the normal chart, reload the chart and look for moved signals, or compare its alert log with the signals left on the chart.
Does barstate.isconfirmed stop repainting?
It stops signals that flicker on the live bar, because it only lets them count once the bar has closed. It does not fix future data from request.security, which needs its own fix.
Does request.security repaint?
It can. Requesting the previous completed value, such as high[1], with lookahead set to barmerge.lookahead_on is the non-repainting pattern TradingView's manual recommends.
Can I automate a strategy that repaints?
You can, but the live orders will not match the backtest. Fix the repainting first, then use Strategy Sync so the account never drifts from what the strategy expects.
Sources
Automate a strategy you can trust
Once your script passes all three tests, start a free 7-day trial to automate it, and bring your Pine questions to the CrossTrade Discord.
CrossTrade is execution and automation software, not a broker, financial adviser, or trading strategy. Futures trading involves substantial risk. Test every automated workflow in simulation and verify orders at your broker.