Bollinger Band Mean Reversion
Buy the dip below the lower band, sell the spike above the upper band.
The Intuition
Bollinger Bands exploit a fundamental property of financial time series: prices tend to revert to their mean over short horizons. The strategy surrounds a rolling average with a volatility envelope — two standard-deviation bands — and assumes that price excursions beyond those bands are temporary dislocations likely to snap back.
The economic rationale is anchored in statistical mean reversion: absent a persistent trend, price oscillates around its recent average. When prices push significantly below the lower band, the market has overshot to the downside — sellers have dominated, possibly driven by panic or thin liquidity rather than new fundamental information. The reverse holds above the upper band.
John Bollinger developed these bands in the 1980s. They became popular because they self-adjust to volatility: in calm regimes the bands tighten, in turbulent regimes they widen. This means the strategy implicitly scales entry thresholds to current market conditions, unlike a fixed-threshold approach.
Key assumptions: (1) The price series is stationary or weakly so over the lookback window. (2) Deviations from the mean are transient, not driven by structural breaks. (3) Returns within the window are approximately normally distributed — the standard deviation is only a coherent bandwidth measure if this holds.
The strategy fails in strong trending markets. A stock in a sustained uptrend will repeatedly trigger short signals as it closes above the upper band. Bollinger himself cautioned that band touches are not signals in isolation — volume and momentum context matters. In practice, it works best in range-bound instruments: FX pairs, commodity spreads, and fixed-income futures.
The Math
Read this as a compact model summary: what the signal sees, what it ignores, and where fragility can creep in.
SMA(t) = mean(Close[t-n : t])
sigma(t) = std(Close[t-n : t])
Upper Band(t) = SMA(t) + k × sigma(t)
Lower Band(t) = SMA(t) - k × sigma(t)
Signal(t) = +1 if Close(t) < Lower Band(t) [go long]
= -1 if Close(t) > Upper Band(t) [go short]
= 0 otherwise [stay flat]
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| window | int | 20 | Rolling window for SMA and std dev |
| num_std | float | 2.0 | Number of std devs for band width |
Source Code
def run(ticker: str, start: str, end: str, **params) -> dict:
window = int(params.get("window", 20))
num_std = float(params.get("num_std", 2.0))
df = fetch_ohlcv(ticker, start, end)
positions = _signal(df, window, num_std)
return run_backtest(df, positions, ticker=ticker, start=start, end=end,
strategy=METADATA["slug"], params={"window": window, "num_std": num_std})
Further Reading
- Bollinger, J. (2002). Bollinger on Bollinger Bands. McGraw-Hill.
- Chan, E. (2013). Algorithmic Trading, Ch. 2. Wiley.
- Kaufman, P. (2013). Trading Systems and Methods, 5th ed. Wiley.
When It Works / When It Fails
- Range-bound markets with stable, predictable oscillation
- Calm volatility environments with tight band compression
- Instruments near known support and resistance levels
- Trending markets — price walks along the upper/lower band
- Vol-expansion phases: bands widen, reversal signals fire rarely
- Gap-prone equities with binary event-driven risk