Bollinger Squeeze — Code Walkthrough
Understand how Bollinger Bands are constructed from standard deviation, what "bandwidth" means, and how the squeeze breakout signal is generated.
Bollinger Squeeze — Code Walkthrough
Bollinger Bands, developed by John Bollinger in the 1980s, are dynamic support/resistance levels that adapt to market volatility. This strategy exploits the "squeeze" — when bands contract, a big move is coming. The code detects when the expansion begins and price breaks out.
Step 1 — Configuration
export interface BollingerConfig {
period: number; // SMA/StdDev lookback window (default 20)
stdDev: number; // Band width multiplier (default 2.0)
stopLossPct: number; // 0.06 = stop at -6%
takeProfitPct: number;// 0.18 = target +18%
}
export const defaultConfig: BollingerConfig = {
period: 20,
stdDev: 2.0,
stopLossPct: 0.06,
takeProfitPct: 0.18,
};The 2.0 standard deviation multiplier means the bands contain ~95% of price action under normal market conditions (by the 68-95-99.7 rule). When price hits a band, it's statistically extreme.
Step 2 — calculateBollingerBands: Math Under the Hood
export function calculateBollingerBands(closes, config) {
const { period, stdDev } = config;
return closes.reduce((acc, _, i) => {
if (i < period - 1) {
// Push NaN placeholders during warmup
acc.upper.push(NaN); acc.middle.push(NaN);
acc.lower.push(NaN); acc.bandwidth.push(NaN);
return acc;
}
const slice = closes.slice(i - period + 1, i + 1); // rolling window
// Mean of the window (= middle band = 20-period SMA)
const mean = slice.reduce((a, b) => a + b, 0) / period;
// Population variance: average squared deviation from mean
const variance = slice.reduce((sum, v) => sum + (v - mean) ** 2, 0) / period;
const std = Math.sqrt(variance);
const upper = mean + stdDev * std; // upper band
const lower = mean - stdDev * std; // lower band
// Bandwidth = (upper - lower) / middle
// High bandwidth = bands wide = high volatility
// Low bandwidth = bands narrow = low volatility (squeeze!)
acc.bandwidth.push((upper - lower) / mean);
return acc;
}, { upper: [], middle: [], lower: [], bandwidth: [] });
}Population vs Sample Standard Deviation
The code divides by N (population), not N-1 (sample). Technically sample std (N-1) is the unbiased estimator, but for indicator purposes the difference is negligible and population std is the convention in most charting software including TradingView.
Step 3 — generateSignals: The Squeeze Breakout Logic
export function generateSignals(closes, config) {
const bands = calculateBollingerBands(closes, config);
return closes.map((price, i) => {
const { upper, middle, lower } = { upper: bands.upper[i], middle: bands.middle[i], lower: bands.lower[i] };
if (isNaN(upper)) return { index: i, signal: 0, upper, middle, lower };
const prevBw = bands.bandwidth[i - 1] ?? NaN;
// "Squeeze" = bandwidth is expanding (volatility returning after compression)
const squeeze = !isNaN(prevBw) && bands.bandwidth[i] > prevBw;
// Breakout up = bands expanding AND price closes above upper band
const breakoutUp = squeeze && price > upper;
// Exit = price falls below the middle band (mean)
const exitSignal = price < middle;
const signal = breakoutUp ? 1 : exitSignal ? -1 : 0;
return { index: i, signal, upper, middle, lower };
});
}- squeeze: True if bandwidth[i] > bandwidth[i-1]. Volatility is expanding — the squeeze is releasing.
- breakoutUp: True if we're in a squeeze expansion AND price broke above the upper band. Strong bullish signal.
- exitSignal: True if price drops below the middle band (the 20-SMA). Mean is reclaiming price — exit long.
One-Directional Strategy
This code only generates bullish breakout signals (price > upper band). A full implementation would also check breakouts below the lower band (price < lower) for short entries, making it symmetric.
- Bollinger Bands = 20-period SMA ± (2 × rolling standard deviation)
- Bandwidth measures how wide/tight the bands are relative to the middle
- A "squeeze" is when volatility contracts; a breakout follows as volatility expands
- Signal fires when bandwidth expands AND price closes above the upper band