Breakout

A price move beyond a defined level — resistance broken, range escaped. Straightforward to code, hard to trade well. False breakouts are common in choppy markets; context and confirmation separate the real moves from the noise.

A breakout is price moving decisively through a level that previously acted as a ceiling (resistance) or floor (support). The premise is that once price clears that boundary with conviction, the forces that were holding it in check have been overwhelmed and a new directional move is beginning.

Breakout systems are among the oldest systematic trading approaches for good reason: they are simple to define, easy to code, and when they work, the trades tend to run. Donchian Channels — the highest high and lowest low over a set lookback — are the most common mechanical way to define a breakout level. Opening Range Breakout (ORB) strategies use the first 30 or 60 minutes of a session to define the range, then trade the break of its high or low.

Why it matters — and the false breakout problem

The failure rate for raw breakouts is high. Price pokes above a resistance level, triggers a buy, and immediately reverses — the classic "fakeout." This happens most in choppy markets where there is no underlying directional pressure, just noise near a visible level.

The most reliable breakouts share a common characteristic: they follow a period of compression. When ATR has been declining and the range has been narrowing, the market is coiling. The longer and tighter the compression, the more energy behind the eventual expansion. Adding an ADX confirmation — waiting for ADX to start rising at the moment of the break — is another filter that improves signal quality by confirming that directional strength is actually building.

One Pine Script-specific trap: using `ta.highest(high, 20)` without the `[1]` offset means you are comparing the current bar's high to a channel that already includes that bar. The break is always "true." Always offset channel calculations by one bar in breakout strategies.

//@version=6
strategy("Breakout Demo", overlay=true)

length = input.int(20, "Channel Length", minval=5)

// [1] offset is critical — excludes the current bar from the channel calculation
// Without [1], ta.highest(high, 20) includes today's high, so a breakout always appears true
channelHigh = ta.highest(high, length)[1]
channelLow  = ta.lowest(low,  length)[1]

bullBreak = ta.crossover(close, channelHigh)
bearBreak = ta.crossunder(close, channelLow)

if bullBreak
    strategy.entry("Long Break", strategy.long)
if bearBreak
    strategy.entry("Short Break", strategy.short)

plot(channelHigh, "Channel High", color=color.blue,   linewidth=1, style=plot.style_stepline)
plot(channelLow,  "Channel Low",  color=color.orange, linewidth=1, style=plot.style_stepline)

plotshape(bullBreak, style=shape.triangleup,   location=location.belowbar, color=color.blue,   size=size.small)
plotshape(bearBreak, style=shape.triangledown, location=location.abovebar, color=color.orange, size=size.small)

Key takeaway: The `[1]` offset on channel calculations (`ta.highest(high, length)[1]`) is essential to avoid look-ahead bias. Using the current bar's high in the channel would make the breakout condition true on the same bar it is measured — that does not exist in live trading and produces inflated backtest results. Always check your breakout logic for this before trusting any performance figures.