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.
//@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.isconfirmedis always true there. On the live bar it runs on every price update, andbarstate.isconfirmedonly becomes true on the update that closes the bar. - Two markers.
confirmedis the signal you can rely on.liveis 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.seenLiveremembers 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. Sinceconfirmedis 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,isrealtimeis true; with the market closed, the last bar is historical:isconfirmedandishistoryare 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.isconfirmedin the expression ofrequest.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.isconfirmedchanges nothing. It matters withcalc_on_every_tick = true. - Crossovers placed after and.
barstate.isconfirmed and ta.crossover(a, b)skips the crossover on unconfirmed updates, since v6'sandstops early (should be called on each calculation). Compute the cross first, then combine. - barstate.isrealtime as a filter. A condition with
barstate.isrealtimeis 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.
Related pages
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.