Paste the complete indicator into Pine Editor, save it and add it to a chart. It stores confirmed pivots, classifies each break from the prior state and clears the broken level regardless of display settings. The first break receives an INIT label rather than an invented CHOCH. Labels identify event time; no drawing is moved back to the original pivot candle.
Latest confirmed pivot model; chart timeframe; both modes wait for bar closepinescript
//@version=6
indicator("BOS and CHOCH example [Pineify]", overlay = true, max_labels_count = 200)
leftBars = input.int(5, "Pivot left bars", minval = 1, maxval = 100)
rightBars = input.int(5, "Pivot right bars", minval = 1, maxval = 100)
breakMode = input.string("Close", "Break test", options = ["Close", "Wick"])
showBos = input.bool(true, "Show BOS labels")
showChoch = input.bool(true, "Show CHOCH labels")
ph = ta.pivothigh(high, leftBars, rightBars)
pl = ta.pivotlow(low, leftBars, rightBars)
var float activeHigh = na
var float activeLow = na
var int direction = 0
bool bullBos = false
bool bearBos = false
bool bullChoch = false
bool bearChoch = false
if barstate.isconfirmed
if not na(ph)
activeHigh := ph
if not na(pl)
activeLow := pl
float upPrice = breakMode == "Close" ? close : high
float downPrice = breakMode == "Close" ? close : low
bool upBreak = not na(activeHigh) and upPrice > activeHigh
bool downBreak = not na(activeLow) and downPrice < activeLow
if upBreak and downBreak
// Intrabar ordering is unknown. Wait for fresh pivots on both sides.
label.new(bar_index, high, "AMBIGUOUS", style = label.style_label_down, color = color.gray, textcolor = color.white)
activeHigh := na
activeLow := na
else if upBreak
bullBos := direction == 1
bullChoch := direction == -1
if direction == 0 or (bullBos and showBos) or (bullChoch and showChoch)
string tag = direction == 0 ? "INIT UP" : bullBos ? "BOS UP" : "CHOCH UP"
label.new(bar_index, high, tag, style = label.style_label_down, color = color.teal, textcolor = color.white)
direction := 1
activeHigh := na
else if downBreak
bearBos := direction == -1
bearChoch := direction == 1
if direction == 0 or (bearBos and showBos) or (bearChoch and showChoch)
string tag = direction == 0 ? "INIT DOWN" : bearBos ? "BOS DOWN" : "CHOCH DOWN"
label.new(bar_index, low, tag, style = label.style_label_up, color = color.red, textcolor = color.white)
direction := -1
activeLow := na
// Levels begin when confirmed; there is no backdated offset.
plot(activeHigh, "Active confirmed high", color = color.teal, style = plot.style_linebr)
plot(activeLow, "Active confirmed low", color = color.red, style = plot.style_linebr)
alertcondition(bullBos, "Bullish BOS", "Bullish BOS on {{ticker}} {{interval}}")
alertcondition(bearBos, "Bearish BOS", "Bearish BOS on {{ticker}} {{interval}}")
alertcondition(bullChoch, "Bullish CHOCH", "Bullish CHOCH on {{ticker}} {{interval}}")
alertcondition(bearChoch, "Bearish CHOCH", "Bearish CHOCH on {{ticker}} {{interval}}")