Edgoro
Trend indicator

Market structure, BOS and CHoCH: rules and Pine Script v6 code

How swing highs and lows define market structure, what break of structure (BOS) and change of character (CHoCH) mean as rules, and a free script.

What it measures

Market structure describes a trend as a sequence of swings. An uptrend makes higher highs (HH) and higher lows (HL); a downtrend makes lower highs (LH) and lower lows (LL). Two events mark changes in that sequence:

  • A break of structure (BOS) is a close beyond the last swing in the direction of the trend, such as a close above the last high in an uptrend. It's read as continuation.
  • A change of character (CHoCH) is the first break against the trend, such as a close above the last high while structure was bearish. It's read as an early sign that the trend may be turning.

The terms come from smart money concepts, but the idea of trends as swings is much older; see smart money concepts explained without the hype.

The formula in words

  1. Swings. A swing high is a high with N lower highs on each side (a pivot); a swing low is the mirror image. With N = 5, a swing is confirmed five bars after it happens.
  2. Labels. Each new swing high is an HH if it's above the previous swing high and an LH if it's below; each swing low is an HL or an LL in the same way.
  3. Breaks. A bullish break is the first close above the latest confirmed swing high; a bearish break is the first close below the latest confirmed swing low. Each swing can be broken once.
  4. BOS or CHoCH. A bullish break while structure is bullish is a BOS. A bullish break while structure is bearish is a CHoCH, and structure turns bullish. Bearish breaks mirror this.

Two details change the result: whether a break needs a close or just a wick beyond the swing (this script uses the close), and the swing strength N. Educators differ on both, which is why two "BOS" indicators rarely agree.

How traders read it

  • Trend state. The last break tells you which way structure points. Many traders take longs only after a bullish BOS or CHoCH, and shorts only after a bearish one.
  • CHoCH as a warning. A CHoCH after a long trend is read as a possible reversal, but the old trend often resumes. Some traders wait for a BOS in the new direction to confirm it.
  • Pullbacks. After a BOS, traders look for price to pull back towards the broken level, or to an order block or fair value gap left by the move.
  • Multiple timeframes. Structure on a higher timeframe sets the direction; a CHoCH on a lower timeframe in that direction is used for timing.

Common settings

SettingTypical valueWhat changing it does
Swing strength3 to 10 barsLow values track minor swings and break often; high values track major swings and confirm late.
Break ruleClose beyond the swingA wick rule gives earlier, more frequent breaks.
TimeframeAnyThe same rules on a higher timeframe describe the larger trend.

Pine Script v6 code

The complete, self-contained script is below. Copy it into a new indicator in TradingView's Pine Editor, or download the .pine file.

Pine Script v6
//@version=6
indicator("Market structure: BOS and CHoCH", shorttitle = "Structure", overlay = true, max_lines_count = 500, max_labels_count = 500)

swingLen   = input.int(5, "Swing strength (bars on each side)", minval = 1, maxval = 50, group = "Swings")
showSwings = input.bool(true, "Label swings (HH, HL, LH, LL)", group = "Display")
bullCol    = input.color(color.teal, "Bullish breaks", group = "Display")
bearCol    = input.color(color.red, "Bearish breaks", group = "Display")

// Swings are pivots: a high with swingLen lower highs on each side, confirmed swingLen bars later.
ph = ta.pivothigh(high, swingLen, swingLen)
pl = ta.pivotlow(low, swingLen, swingLen)

var float lastHigh    = na
var int   lastHighBar = na
var bool  highBroken  = true
var float lastLow     = na
var int   lastLowBar  = na
var bool  lowBroken   = true
var int   trend       = 0 // 1 = bullish structure, -1 = bearish, 0 = not known yet

bosUp     = false
chochUp   = false
bosDown   = false
chochDown = false

if barstate.isconfirmed
    if not na(ph)
        if showSwings
            label.new(bar_index - swingLen, ph, na(lastHigh) ? "H" : ph > lastHigh ? "HH" : "LH", yloc = yloc.abovebar, style = label.style_none, textcolor = color.gray, size = size.small)
        lastHigh    := ph
        lastHighBar := bar_index - swingLen
        highBroken  := false
    if not na(pl)
        if showSwings
            label.new(bar_index - swingLen, pl, na(lastLow) ? "L" : pl < lastLow ? "LL" : "HL", yloc = yloc.belowbar, style = label.style_none, textcolor = color.gray, size = size.small)
        lastLow    := pl
        lastLowBar := bar_index - swingLen
        lowBroken  := false

    // A close above the last swing high: CHoCH if structure was bearish, BOS otherwise.
    if not highBroken and close > lastHigh
        highBroken := true
        chochUp    := trend == -1
        bosUp      := not chochUp
        trend      := 1
        line.new(lastHighBar, lastHigh, bar_index, lastHigh, color = bullCol, style = chochUp ? line.style_dashed : line.style_solid)
        label.new(int(math.avg(lastHighBar, bar_index)), lastHigh, chochUp ? "CHoCH" : "BOS", style = label.style_label_down, color = color.new(bullCol, 100), textcolor = bullCol, size = size.small)

    // A close below the last swing low: CHoCH if structure was bullish, BOS otherwise.
    if not lowBroken and close < lastLow
        lowBroken := true
        chochDown := trend == 1
        bosDown   := not chochDown
        trend     := -1
        line.new(lastLowBar, lastLow, bar_index, lastLow, color = bearCol, style = chochDown ? line.style_dashed : line.style_solid)
        label.new(int(math.avg(lastLowBar, bar_index)), lastLow, chochDown ? "CHoCH" : "BOS", style = label.style_label_up, color = color.new(bearCol, 100), textcolor = bearCol, size = size.small)

alertcondition(bosUp, "Bullish BOS", "Bullish break of structure on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(bosDown, "Bearish BOS", "Bearish break of structure on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(chochUp, "Bullish CHoCH", "Bullish change of character on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(chochDown, "Bearish CHoCH", "Bearish change of character on {{ticker}} ({{interval}}) at {{close}}")

Swings come from ta.pivothigh() and ta.pivotlow(). Each confirmed swing gets an HH, HL, LH or LL label drawn back on the bar where it happened, and each break draws a line from the swing to the breaking bar, solid for a BOS and dashed for a CHoCH, with a matching label.

The first break on the chart is labelled BOS because there's no earlier structure for it to change. All updates run on closed bars only, and four alert conditions cover bullish and bearish BOS and CHoCH; use Once per bar close.

Add it to your chart

  1. Open any chart on TradingView and open the Pine Editor.
  2. Create a new indicator, delete the template code and paste the script.
  3. Click Save, then Add to chart. Change lengths and levels from the indicator's settings.

Stuck on an error? See how to add a Pine Script indicator to TradingView.

Pitfalls

  • Swing labels appear late. A swing is known only N bars after it happens. The label is drawn back on the swing bar, but it wasn't visible there in real time.
  • Wick versus close. Compared with an indicator that breaks on wicks, signals will differ. Neither is "right"; pick one.
  • Short swings, constant breaks. With N = 2 or 3 almost every small wiggle becomes a swing, and BOS and CHoCH labels pile up.
  • CHoCH is not a reversal. It marks the first break against the trend. Many are followed by the old trend resuming.
  • Repainting versions. Scripts that draw structure the moment a high forms are using unconfirmed swings and will change their past labels; see repainting explained.

The script is an educational example: test it on your own markets before relying on it. Indicators describe the past; they don't predict the future. 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.

Make it yours with Edgoro

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, ready to paste. For example:

“Label swing highs and lows with 5-bar pivots, mark BOS and CHoCH on closes beyond the last swing, and alert on a bullish CHoCH on the 1-hour chart.”

Build your BOS and CHoCH indicator