How It Trades
- 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.
- 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.
- 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.
- 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)