DayTraderScripts
← Back to blog
non repainting indicatorsTradingViewPine Scriptrepaintingbarstate.isconfirmed

Non-Repainting Indicators on TradingView: What Repainting Is and How to Catch It

What repainting actually means in Pine Script — security() lookahead, intrabar signals, forming-bar levels — plus a concrete test protocol and why barstate.isconfirmed matters.

DayTraderScripts Desk·Jul 17, 2026·5 min read

"Non-repainting" is the most abused phrase on the TradingView marketplace. Half the scripts claiming it repaint anyway; half the accusations of repainting describe behavior that's actually fine. Before you trust any indicator with real money — ours included — you should know precisely what repainting is, the three ways it happens, and how to test for it yourself in one trading session.

What Repainting Actually Is

An indicator repaints when its historical output differs from what it displayed in real time. The chart rewrites its own past: signals that fired live disappear, signals that never fired appear, levels shift to where they "should" have been. The result is a backtest and visual history that describe a strategy nobody could have traded.

Be precise about what isn't repainting:

  • A value updating on the currently forming bar is normal. Every indicator does this — the bar isn't finished. Repainting is about history changing after the bar closes.
  • HTF values updating while the HTF bar is open is normal — with a catch we'll get to. A daily level shown on a 5-minute chart legitimately updates until the daily bar completes.

The sin is when the finished record doesn't match what you saw live. Three mechanisms account for nearly all of it.

Mechanism 1: request.security() Lookahead

Pine Script's request.security() pulls data from another timeframe or symbol. Called carelessly, it commits the classic sin:

// REPAINTS on historical bars
dailyHigh = request.security(syminfo.tickerid, "D", high,
     lookahead = barmerge.lookahead_on)

With lookahead_on and a non-offset series, historical bars read the completed daily bar — including hours of price action that hadn't happened yet at that point in the session. Historically, every bar knows the day's high in advance. Live, it can't. Backtests built on this are fantasy.

The honest pattern offsets the series so you only ever reference the previous, completed HTF bar:

// Does not repaint: yesterday's completed high
dailyHigh = request.security(syminfo.tickerid, "D", high[1],
     lookahead = barmerge.lookahead_on)

The [1] offset paired with lookahead is the standard non-repainting HTF idiom. Any script requesting a same-period HTF value without an offset is either repainting or accepting that its historical bars show information live bars never had. When you audit a script, search the source for lookahead and check every request.security call for that offset.

Mechanism 2: Intrabar Signals and Alerts

A signal computed on the forming bar can be true at 10:31:40 and false at 10:32:00 when the bar closes. If the script plots the arrow or fires the alert intrabar, you acted on a signal that then ceased to exist. On reload, TradingView recalculates using only closed bars — the arrow vanishes, and the chart claims innocence.

This is why barstate.isconfirmed matters. It's true only on a bar's final update:

longSignal = crossover(fastMA, slowMA) and barstate.isconfirmed

Gated this way, the signal only ever exists in its final, permanent form — what fires live is exactly what history shows. The cost is honesty's price tag: you get the signal at bar close, not mid-bar. Scripts that skip the gate look faster and alert sooner; they're also unauditable, because their live behavior and their history are two different indicators.

Alert settings matter too: "Once per bar close" respects confirmation; "Once per bar" on unconfirmed logic will happily fire on conditions that evaporate.

Mechanism 3: Forming-Bar and Forming-Session Levels

The subtlest variant: an indicator draws levels from a period that is still in progress, and signals off them. An opening-range script that tracks the range high while the range is still forming, a session-VWAP band computed mid-session and then used retroactively, pivot logic that centers on a bar and needs future bars to confirm it — pivothigh(10, 10), for instance, is only known 10 bars after the fact, and plotting it back at the pivot without accounting for that delay makes historical signals clairvoyant.

The rule: a level may only generate signals after the period that defines it has closed. Levels that keep moving are context, not triggers.

How to Test Any Indicator for Repainting

You don't need source code access — behavior is testable in one session:

  1. The screenshot test. Load the indicator on a live intraday chart at the open. Screenshot at fixed times — 10:00, 12:00, close. After the close, reload the chart (or reopen the layout) and compare against the screenshots. Any signal that moved, appeared, or vanished is repainting. One session of this beats any marketplace comment thread.
  2. The bar-replay cross-check. Run TradingView's bar replay across a past session and note where signals print. Then view the same session on the normal historical chart. Mismatches mean the script behaves differently with and without future data. (Replay itself has quirks with request.security, so treat this as a cross-check, not the verdict — the screenshot test is the verdict.)
  3. The source audit (open-source scripts). Search for: lookahead_on without a [1]-offset series; signal logic with no barstate.isconfirmed (or equivalent close-confirmation) gate; calc_on_every_tick = true in strategies; and pivot functions plotted without their confirmation delay. Fifteen minutes of grep answers what fifty pages of comments won't.
  4. The alert-vs-chart ledger. Let the indicator's alerts run for a week. Log every alert. At week's end, compare the log to the chart's historical signals. Perfect agreement is the definition of non-repainting; any orphaned alert is the smoking gun.

Why We're Strict About This

Every DayTraderScripts indicator ships under the same discipline: HTF data referenced with completed-bar offsets, all signals and alerts gated on barstate.isconfirmed, and no level used as a trigger before its defining period closes. That's not a marketing bullet — it's a constraint that makes our tools slower than repainting competitors, because confirmed signals arrive at bar close by definition. We think that trade is not optional. VWAP Bands Pro, for example, computes its session bands strictly from completed data, so a band touch on your chart tonight is the same band touch that was there at 10:14 this morning. Run the screenshot test on it; that's what the test is for.

The uncomfortable truth about repainting scripts is that they look better — historical signals cherry-picked by hindsight always do. If an indicator's chart history seems too clean, that cleanliness is the tell, not the selling point.

Bottom Line

Repainting has three faces: security() lookahead without offsets, intrabar signals unguarded by barstate.isconfirmed, and levels that trigger before they're final. All three produce histories no live trader could have traded. Test with screenshots against a reload, audit sources for the three patterns, and reconcile a week of alerts against the chart. An indicator that survives all that is worth evaluating on its merits. One that doesn't has none.

Educational content, not financial advice. Trading involves substantial risk of loss.

Keep reading