Edgoro
Indicator · Signals and price structure

Pine Script barstate.isconfirmed: signals on closed bars

Use barstate.isconfirmed in Pine Script v6 to act only on closed bars, count the live signals that vanished before the close, and read every barstate value.

Short answer

barstate.isconfirmed is true on every historical bar and on the last update of a realtime bar, the moment it closes, so signal and barstate.isconfirmed only fires once the bar is final. Use it for markers, labels and alert() calls that must not change after they appear.

The script

A breakout drawn in two colors, final and still live, a counter of live signals that were gone at the close, and a table of the barstate values. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("Confirmed vs live signals", overlay = true)

len = input.int(20, "Breakout lookback (bars)", minval = 2)

level    = ta.highest(high, len)[1]
breakout = close > level

// Final: only on a closed bar. Live: may still change before the close
bool confirmed = breakout and barstate.isconfirmed
bool live      = breakout and not barstate.isconfirmed

// Live signals that were gone when their bar closed (counted on realtime bars only)
varip bool seenLive = false
varip int  vanished = 0
if barstate.isnew
    seenLive := false
if live
    seenLive := true
if barstate.isconfirmed and seenLive and not breakout
    vanished += 1

plot(level, "Breakout level", color.gray, style = plot.style_stepline)
plotshape(confirmed, "Confirmed breakout", shape.triangleup, location.belowbar, color.teal, size = size.small)
plotshape(live, "Live breakout (not final)", shape.triangleup, location.belowbar, color.new(color.orange, 40), size = size.small)

if confirmed
    alert("Breakout: close above " + str.tostring(level, format.mintick), alert.freq_once_per_bar_close)

// The bar states, as the last bar sees them
var table states = table.new(position.bottom_right, 2, 6, bgcolor = color.new(color.black, 20), border_width = 1, border_color = color.new(color.gray, 60))
stateRow(table t, int r, string name, bool value) =>
    t.cell(0, r, name, text_color = color.silver, text_halign = text.align_left, text_size = size.small)
    t.cell(1, r, value ? "true" : "false", text_color = value ? color.teal : color.gray, text_size = size.small)
if barstate.islast
    stateRow(states, 0, "barstate.isconfirmed", barstate.isconfirmed)
    stateRow(states, 1, "barstate.isrealtime", barstate.isrealtime)
    stateRow(states, 2, "barstate.isnew", barstate.isnew)
    stateRow(states, 3, "barstate.islast", barstate.islast)
    stateRow(states, 4, "barstate.ishistory", barstate.ishistory)
    states.cell(0, 5, "Vanished live signals", text_color = color.silver, text_halign = text.align_left, text_size = size.small)
    states.cell(1, 5, str.tostring(vanished), text_color = color.white, text_size = size.small)

How it works

  • History and live bars. On historical bars the script runs once per bar, on its final values, so barstate.isconfirmed is always true there. On the live bar it runs on every price update, and barstate.isconfirmed only becomes true on the update that closes the bar.
  • Two markers. confirmed is the signal you can rely on. live is shown in orange so you can watch the breakout form during the bar; if price falls back before the close, the orange marker disappears and no teal one is drawn.
  • The vanished counter uses varip, which keeps changes across the live bar's updates. seenLive remembers that the breakout appeared at some point in the bar; if it's false at the close, the counter goes up. It stays at 0 on history and counts from the moment the chart is open, which makes the difference concrete on a fast timeframe.
  • alert(..., alert.freq_once_per_bar_close) sends an alert built from data (the level) once the bar closes. Since confirmed is only true on the closing update anyway, the frequency and the condition agree.
  • The table shows the five barstate.* values on the last bar. With the market open, isrealtime is true; with the market closed, the last bar is historical: isconfirmed and ishistory are true.

Pitfalls

  • Signals arrive at the close. That's the point, but on a 4-hour chart it means waiting up to four hours. If you want intrabar alerts, accept that some will be withdrawn: the vanished counter shows how often.
  • Inside request.security(). TradingView's reference advises against using barstate.isconfirmed in the expression of request.security(): its value there is unreliable. Confirm on the chart's own bars instead. The multi-timeframe guide shows the non-repainting pattern.
  • Strategies already wait for the close. By default a strategy runs once per bar, at the close, on live bars too, so barstate.isconfirmed changes nothing. It matters with calc_on_every_tick = true.
  • Crossovers placed after and. barstate.isconfirmed and ta.crossover(a, b) skips the crossover on unconfirmed updates, since v6's and stops early (should be called on each calculation). Compute the cross first, then combine.
  • barstate.isrealtime as a filter. A condition with barstate.isrealtime is never true on history, so the chart shows no past signals, and when the market is closed even the last bar isn't realtime.

Variations

The previous bar's signal. Reading the signal one bar later gives the same final result, available from the first update of the next bar, and works the same way in code that has no notion of bar states:

// The breakout of the previous bar: final from the first update of this bar
bool prevBreakout = breakout[1]
plotshape(prevBreakout, "Breakout (previous bar)", shape.circle, location.abovebar, color.blue, size = size.tiny)

With alertcondition(). alertcondition() has no frequency argument: choose "Once Per Bar Close" in the alert dialog, or include barstate.isconfirmed in its condition. Both are compared in alertcondition() vs alert().

Questions

What is barstate.isconfirmed in Pine Script?

A built-in bool that is true when the script is running on a closed bar: always on historical bars, and on the last update of a realtime bar. Adding it to a condition makes signals wait for the bar's close.

What is the difference between barstate.isconfirmed and barstate.islast?

barstate.islast is true on the last bar of the chart, whether it's closed or still forming, on every update. barstate.isconfirmed is true on every closed bar. On a live chart, the last bar is islast on all its updates but isconfirmed only on the closing one.

Does barstate.isconfirmed stop repainting?

It stops signals from changing during the live bar, which is the most common kind. It doesn't fix other causes: higher-timeframe data read with lookahead, pivots drawn back in time, or future values used in calculations.

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“Alert me when price closes above the 20-bar high, only once the bar has closed, with the breakout level in the alert message.”