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.
Table of Contents
- Why crossover signals flip after you refresh
- Pine Script settings that cause crossover repainting
- Three code patterns that stop crossover repainting
- Using request.security safely for higher-timeframe crossovers
- Strategies, calconeverytick and backtest validity
- Checklist: convert a repainting crossover to non-repainting
- Speed versus reliability for scalpers
- Skip the rebuild: our non-repainting indicator suite
- FAQ
- Sources
- Key docs and example scripts to read next
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.

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.
- 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. - 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. - 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
- Switch crossover checks to the previous bar with [1], or add
and barstate.isconfirmedto the condition. - Audit every request.security call and replace it with
expression[1]pluslookahead = barmerge.lookahead_on. - Turn off calc_on_every_tick for strategy backtests, or account for intrabar ticks explicitly.
- Refresh the chart, rerun the backtest, and set alerts to "Once per bar close".
- 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.

- 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
- Tradingview
- How to avoid repainting when NOT using security() — Indicator by PineCoders — TradingView
Key docs and example scripts to read next
Start with these references behind the patterns above:
- TradingView repainting docs
- PineCoders non-repainting methods
- Pine Script repainting concepts
- EMA crossover strategy example
