Bollinger Squeeze Breakout

How this strategy trades, what it scored, and the measurements the score was read from: every symbol and timeframe, on real and resampled history.

How It Trades

  1. Compute 20-bar Bollinger Bands and watch their relative width: upper band minus lower band, divided by the middle. Narrow bands mean the market has gone unusually quiet.
  2. A squeeze is on when the width hits its lowest value of the last 125 bars: volatility is as compressed as it has been in roughly six months of bars, and quiet markets tend to resolve into violent moves.
  3. Buy the first close above the upper band within 5 bars of a squeeze: the compression resolving upward with force. A band break without a fresh squeeze is ignored.
  4. Sell when the close falls back below the middle band: the expansion has run its course.

Example Strategy

Pine Script

Paste it into TradingView’s Pine Editor to run these rules on any chart.

//@version=6
// Bollinger Squeeze Breakout — sort.finance. Fills at the signal bar's close, 0.02% fee a side,
// whole account per trade, as the backtest. History and indicator warm-up
// differ from the site's, so expect close to the same trades, not identical.
strategy("Bollinger Squeeze Breakout", overlay=false,
     initial_capital=100, default_qty_type=strategy.percent_of_equity,
     default_qty_value=100, commission_type=strategy.commission.percent,
     commission_value=0.02, process_orders_on_close=true, pyramiding=0)

// The bar the current position opened on, and the bar the last one closed on.
var int entryBar = na
var int exitBar = -1

// ── Signals ──────────────────────────────────────────────────────────────
[middle, upper, lower] = ta.bb(close, 20, 2)
width = (upper - lower) / middle
// A squeeze is band width at its 125-bar low; it counts while it
// was seen in the 5 bars before the breakout.
squeezed = width <= ta.lowest(width, 125)
recentlySqueezed = ta.highest(squeezed ? 1 : 0, 5)[1] == 1
enterNow = recentlySqueezed and close[1] <= upper[1] and close > upper
exitNow = close[1] >= middle[1] and close < middle

// ── Orders ───────────────────────────────────────────────────────────────
// Open on the first bar the entry holds while flat; close on the first
// later bar the exit holds; never reopen on the bar a position closed.
inTrade = strategy.position_size != 0
if inTrade and bar_index > entryBar and exitNow
    strategy.close("Long")
    exitBar := bar_index
else if not inTrade and bar_index > exitBar and enterNow
    strategy.entry("Long", strategy.long)
    entryBar := bar_index

// ── Drawing ──────────────────────────────────────────────────────────────
plot(upper, "Upper Band", color=#8a92a0, force_overlay=true)
plot(middle, "Middle Band", color=#4d8dff, force_overlay=true)
plot(lower, "Lower Band", color=#8a92a0, force_overlay=true)
plot(width * 100, "Band Width %", color=#e0a03a)