← Back to blog

Stop Crossover Repainting in Pine Script: 3 Fixes for Scalpers

October 16, 2026
Stop Crossover Repainting in Pine Script: 3 Fixes for Scalpers

Detect crosses on confirmed values: index the previous bar with [1] or gate the condition with barstate.isconfirmed, pull higher-timeframe data with a safe request.security pattern, and keep calc_on_every_tick off for strategies. The tradeoff is timing. Non-repainting signals confirm on bar close, so they arrive later, but they match what your alerts and backtests actually show.


TL;DR:

  • For alerts and entries, gate crossovers with barstate.isconfirmed to trigger at bar close; indexing with [1] adds a full bar of delay.
  • For higher timeframe values, shift the requested expression with [1] and enable lookahead_on; an unshifted expression can expose future data on history.
  • Keep calc_on_every_tick = false for representative strategy backtests; if intrabar execution is essential, model it deliberately and treat historical results cautiously.
  • TradingView says over 95% of indicators show some form of repainting; refresh charts and compare raw markers with confirmed versions.

Scalping-algo
Use Signals Built Not to Repaint
Scalping-Algo offers TradingView indicators with real-time, non-repainting signals for lower timeframes, alongside alerts and backtesting tools.
Explore the indicators

Table of Contents

Why crossover signals flip after you refresh

Repainting means a script calculates differently on historical bars than on the live bar, so plotted cross markers appear, move or vanish after a refresh. TradingView's documentation states that over 95% of indicators show some form of repainting. Check yours.

Here is why crossovers flip:

  • The live bar updates its price on every tick, so a cross built on close can form and disappear before the bar ends.
  • Higher-timeframe data can change until that higher bar closes.
  • On history, each bar is computed once with its final close, so the chart looks cleaner than live trading.
  • After a reload, realtime bars are recalculated as history, which erases the flips you saw live.

Pine Script settings that cause crossover repainting

Audit these first. Each one makes realtime behavior diverge from history:

  • barstate.isnew: true only on the first tick of a bar, so a cross flagged there reflects an unfinished price.
  • barstate.isrealtime: logic gated on it runs only live, never in the backtest, so the two results disagree.
  • calc_on_every_tick = true: the strategy executes on every tick live but only on close in history.
  • request.security() without care: higher-timeframe values can update mid-bar, and lookahead handling changes what history shows.
  • Raw close in realtime: without confirmation, close is just the latest price.

To test: refresh the chart, compare the markers, and temporarily add a plotshape for each suspect condition. Whatever differs after the reload is your repainting source.

Pro Tip: Plot the same cross twice, once raw and once with barstate.isconfirmed. Any marker that appears only on the raw version is repainting.

Raw signal disappears while confirmed signal remains

Three code patterns that stop crossover repainting

Each pattern trades speed for reliability. Pick based on how much delay you can accept. TradingView's repainting concepts page covers all three.

  1. Pattern A, previous-bar index: x = ta.crossover(fast, slow)[1]. The simplest change, delayed by one bar. Prefer it for a quick fix on an existing script.
  2. Pattern B, confirmation gate: ta.crossover(fast, slow) and barstate.isconfirmed. The signal fires only on the closing iteration, so it shows at bar close with no extra bar of delay. It skips intrabar early triggers. Prefer it for alerts and strategy entries.
  3. Pattern C, confirmed inputs: ma = ta.ema(close[1], n). The source never floats during realtime, but the signal lags by one bar. Prefer it when several indicators share one input.

PineCoders' repainting example demonstrates three non-repainting methods and notes that non-repainting markers and alerts are always later than repainting ones.

That is the cost of reliability, and for live alerts it is usually worth paying.

Using request.security safely for higher-timeframe crossovers

request.security can return unconfirmed realtime values, and with the wrong lookahead setting it can paint history with data that was not yet known. The safe approach pulls the last closed higher-timeframe bar.

  • Use request.security(syminfo.tickerid, "60", ta.ema(close, 50)[1], lookahead = barmerge.lookahead_on). The [1] shifts to the last closed bar, and lookahead_on makes history match live.
  • Wrap it in a small helper, such as a noRepaintSecurity() function, so every call uses the same pattern.
  • Never combine lookahead_on with an unshifted expression. That mix shows future data on history.

Here is a higher-timeframe EMA in a crossover:

htfEma = request.security(syminfo.tickerid, "higher timeframe", ta.ema(close, length)[1], lookahead = barmerge.lookahead_on)
signal = ta.crossover(close, htfEma) and barstate.isconfirmed

The community script Repainting? Yes/No documents how closed higher-timeframe bars behave on lower-timeframe charts.

Strategies, calc_on_every_tick and backtest validity

Strategies add another layer. TradingView notes that calc_on_every_tick = true causes major repainting because the strategy executes differently live than on historical closes. Your backtest then describes a system you are not actually running.

  • Set calc_on_every_tick = false for representative backtests, so history and live both act on bar close.
  • If you truly need intrabar execution, design for it deliberately and treat backtest results with caution.
  • Set alerts to "Once per bar close" when you need non-repainting alert behavior.
  • Rerun the backtest after every change, since a different execution model gives different trades.

For alert message setup, our guide to TradingView alert syntax walks through the fields.

Pro Tip: If a strategy looks too good on a lower-timeframe chart, check calc_on_every_tick first. Inflated backtests often trace back to that one setting.

Checklist: convert a repainting crossover to non-repainting

  1. Switch crossover checks to the previous bar with [1], or add and barstate.isconfirmed to the condition.
  2. Audit every request.security call and replace it with expression[1] plus lookahead = barmerge.lookahead_on.
  3. Turn off calc_on_every_tick for strategy backtests, or account for intrabar ticks explicitly.
  4. Refresh the chart, rerun the backtest, and set alerts to "Once per bar close".
  5. Accept the latency tradeoff: signals confirm on close, so they come later but hold up in live alerts.

Want to route those alerts somewhere useful? See our webhook examples for JSON and latency notes.

Speed versus reliability for scalpers

We prefer confirmed signals for live alerts and backtests, even when they arrive a bar late. A fast signal that disappears is worse than a slower one you can trust, because you cannot size a trade on a marker that may vanish. Our workflow is simple: backtest, run a short forward test, then check the script against community feedback. Open-source, no-repaint scripts make that last step possible, since anyone can read the logic. It also pairs well with volatility gating to cut false crosses on lower timeframes. For how signals get built and filtered, this guide to trade signals is useful background.

— Tran

Skip the rebuild: our non-repainting indicator suite

If you would rather start from confirmed-close signals than rewrite your own, we publish open-source Pine Script indicators built for non-repainting signals, with Discord webhook alerts and a backtesting dashboard. Everything is readable, so you can verify the logic yourself.

Scalping-algo

  • Review the Smart Scalping Signals page for features and chart examples.
  • Check the signals on your own symbols and timeframes through the dashboard.
  • See all plans on our main page.

FAQ

Does barstate.isconfirmed fully stop crossover repainting?

It stops the signal from firing on unfinished bars, which removes most crossover flips. Higher-timeframe data and strategy settings still need their own handling, as covered above.

Why do my crossovers look perfect on history but fail live?

History computes each bar once with its final close, while the live bar updates on every tick. A cross can form mid-bar and disappear by close, which history never shows.

Is it safe to use request.security for crossovers?

Yes, when you shift the expression with [1] and set lookahead_on. That pattern returns the last closed higher-timeframe bar, so history matches live.

Do non-repainting crossover signals arrive later?

Yes. PineCoders' examples note that non-repainting markers and alerts are always later than repainting ones. You trade a bar of delay for signals you can rely on.

Sources

Start with these references behind the patterns above: