barstate.isconfirmed in Pine Script: Close[0] < Close[1] Patterns and Common Fixes
Why gating your price-comparison conditions behind barstate.isconfirmed stops phantom signals and makes live behavior match your historical backtests.
What barstate.isconfirmed Actually Does
Pine Script recalculates every indicator on every tick of a live bar. That means close[0] is not the bar's final price — it is whatever price is trading right now. When your script evaluates close[0] < close[1] on a live bar, the result flips back and forth as price moves, producing signals that appear and vanish within the same candle.
barstate.isconfirmed is a built-in boolean that becomes true only on the final execution of a bar — the tick at which that bar closes and becomes permanent history. Gate any condition you want tied to a confirmed close behind it, and mid-bar flicker disappears.
On historical bars every bar is already closed, so barstate.isconfirmed is always true there. The guard changes behavior only on the current live bar, which is exactly where most bugs hide.
The Close[0] < Close[1] Pattern
A bearish-close condition looks like this: barstate.isconfirmed and close[0] < close[1]. close[0] is the current bar's confirmed close and close[1] is the previous bar's close. When both conditions are true, the current bar just finished lower than the one before it — and that fact is locked in.
A minimal working example: bearClose = barstate.isconfirmed and close[0] < close[1], then plotshape(bearClose, style=shape.triangledown, location=location.abovebar, color=color.red). The shape will plot exactly once per qualifying bar, after close — never mid-candle.
Without the guard, plotshape repaints: the shape appears when the current tick is below the prior close, vanishes when price rebounds, reappears again. This looks like a working signal. It is noise.
barstate.isclosed Does Not Exist — and What to Use Instead
barstate.isclosed is not a valid Pine Script variable. Many developers search for it after reading generic programming articles. The correct variable is barstate.isconfirmed. Using barstate.isclosed will produce a compiler reference error and the script will not load.
The bar-state booleans you actually have access to: barstate.isconfirmed (bar has closed), barstate.isrealtime (currently open live bar), barstate.islast (the most recent bar, whether open or closed), and barstate.islastconfirmedhistory (the last bar that has fully closed, never a partially formed one). For signal logic you almost always want barstate.isconfirmed.
A pattern that trips up beginners: in the strategy tester all bars are historical and already confirmed, so a script missing barstate.isconfirmed passes backtests cleanly but repaints visibly on a live chart. If your indicator looks perfect in the tester but fires phantom signals live, missing confirmation logic is the first place to look.
Divergence Indicators and Labeling the Current Price at Confirmation
Divergence detection multiplies the problem. A typical RSI or MACD divergence check compares a recent swing high on price against the corresponding oscillator peak. Pine Script's ta.pivothigh() and ta.pivotlow() return a value only after enough subsequent bars have formed. Running that logic without confirmation means your script evaluates on incomplete pivots, fires early, then shifts — exactly what readers searching for 'pine script barstate.isconfirmed divergence indicator wait bar close' are experiencing.
The correct pattern: compute your divergence condition normally, then gate the action — the alert, the plotshape, the label — behind barstate.isconfirmed. The condition can still evaluate every tick; you simply do not act on it until the bar closes and the result is locked.
To label the confirmed close price at signal time: if barstate.isconfirmed and mySignalCondition then label.new(bar_index, close, str.tostring(close, "#.##"), style=label.style_label_up). This places one label per qualifying bar, at the exact confirmed close price. Without the guard, label.new fires on every tick and leaves hundreds of overlapping labels on the bar.
These Are Hypothetical Backtests, Not Financial Advice
IndicatorEdge runs out-of-sample backtests across 903 assets and 660,005 total test runs on 1-Hour, 4-Hour, Daily, and Weekly timeframes. Every result is a hypothetical simulation under realistic cost assumptions — it describes past statistical behavior, not future returns. Only 26% of indicator-and-asset combinations beat a simple buy-and-hold over the test period.
Getting barstate.isconfirmed right matters because a repainting strategy produces a backtest that looks better than the live version. The code patterns on this page produce no-repaint signals, which is the minimum requirement for a test result you can trust. Correct code does not change the fundamental uncertainty of markets.
Nothing on this page is financial advice. Backtest results are not predictive of future performance. Use position sizing and risk management appropriate to your own situation.
Questions, answered
Why does barstate.isconfirmed fire on every bar in the strategy tester?
On historical data every bar is already closed, so barstate.isconfirmed is true on every bar — that is correct behavior. The guard only filters execution on the current live, unformed bar. To observe the difference, add the script to a real-time chart and watch the forming candle.
What is the difference between barstate.isconfirmed and barstate.islastconfirmedhistory?
barstate.isconfirmed is true whenever any bar has just closed — it fires once per bar, every bar, as each one completes. barstate.islastconfirmedhistory is true only on the single most-recent bar that has fully closed and never on a bar that is still forming. Use barstate.isconfirmed for signal logic across all bars; use barstate.islastconfirmedhistory when you specifically need to act only on the latest completed bar.
My divergence indicator still shifts signals on replay even with barstate.isconfirmed. Why?
Pivot functions like ta.pivothigh() look both backward and forward in time by the number of bars you specify. A pivot at bar N is not visible until bar N + your right-offset has formed. If your signal reads the pivot before that offset has elapsed the result is still undefined. barstate.isconfirmed stops mid-bar flicker but does not fix a pivot offset that is too short. Double-check that your look-back and right-offset parameters match.
How do I fire an alert exactly once per bar when close is lower than the prior close?
Use alert() inside an if block gated by barstate.isconfirmed: <strong>if barstate.isconfirmed and close[0] < close[1]</strong> then <strong>alert('Bearish close confirmed', alert.freq_once_per_bar_close)</strong>. The freq_once_per_bar_close frequency argument is redundant when you already gate on barstate.isconfirmed, but it adds a second layer of protection against accidental double-fires during broker feed anomalies.
Every figure here comes from our own out-of-sample backtests, costs included — not a course or a guess. Educational information only — not investment advice. Hypothetical backtested results; past performance does not guarantee future results. Trading involves risk of loss.
Keep reading
Get the weekly edge report
The best-performing indicator per asset, what changed this week, and the honest caveats — straight to your inbox.