Bollinger Bands Width (BBW)
period = 20 (5–250) | devtype = 0 (0–2) Overview
Bollinger Bands Width quantifies volatility contraction and expansion by calculating the percentage difference between the upper and lower bands relative to the middle band value. John Bollinger developed this derivative indicator to identify squeeze patterns where bands compress to historically tight levels before explosive moves. The calculation divides the difference between upper and lower bands by the middle band, then multiplies by 100 to express the result as a percentage. When BBW drops to multi month lows, it signals extreme compression that often precedes significant breakouts in either direction. Conversely, readings at historical highs indicate maximum volatility expansion typical of blow off tops or panic bottoms. Traders combine BBW with momentum indicators to determine breakout direction once the squeeze releases, using low width readings to position for volatility expansion trades. The indicator excels at timing option strategies, as compressed bands signal low implied volatility environments ideal for buying straddles before expansion phases begin.
Implementation Examples
Compute BBW in a few lines using closing prices:
use vectorta::indicators::bollinger_bands_width::{
bollinger_bands_width, BollingerBandsWidthInput, BollingerBandsWidthParams
};
use vectorta::utilities::data_loader::Candles;
let closes = vec![101.5, 102.1, 100.9, 99.8, 101.2, 103.4];
let params = BollingerBandsWidthParams {
period: Some(20),
devup: Some(2.0),
devdn: Some(2.0),
matype: Some("sma".into()),
devtype: Some(0),
};
let input = BollingerBandsWidthInput::from_slice(&closes, params);
let output = bollinger_bands_width(&input)?;
// Using the default helper with Candles (close price source)
let candles: Candles = load_candles()?;
let input = BollingerBandsWidthInput::with_default_candles(&candles);
let output = bollinger_bands_width(&input)?;
for (idx, value) in output.values.iter().enumerate() {
println!("BBW[{idx}] = {value}");
} API Reference
Input Methods ▼
use vectorta::indicators::bollinger_bands_width::{
BollingerBandsWidthInput, BollingerBandsWidthParams
};
use vectorta::utilities::data_loader::Candles;
// From slice of closes
let params = BollingerBandsWidthParams::default();
let input = BollingerBandsWidthInput::from_slice(&closes, params);
// From candles with explicit source column
let input = BollingerBandsWidthInput::from_candles(&candles, "close", params);
// Convenience helper (closes + default params)
let input = BollingerBandsWidthInput::with_default_candles(&candles); Parameters Structure ▼
pub struct BollingerBandsWidthParams {
pub period: Option<usize>, // Default: 20
pub devup: Option<f64>, // Default: 2.0
pub devdn: Option<f64>, // Default: 2.0
pub matype: Option<String>, // Default: "sma"
pub devtype: Option<usize>, // Default: 0 (stddev)
} Output Structure ▼
pub struct BollingerBandsWidthOutput {
pub values: Vec<f64>, // Width series: (upper - lower) / middle
} Error Handling ▼
BBW returns descriptive errors for invalid periods and insufficient data:
use vectorta::indicators::bollinger_bands_width::{
bollinger_bands_width, BollingerBandsWidthError
};
match bollinger_bands_width(&input) {
Ok(output) => consume(output.values),
Err(BollingerBandsWidthError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {period} for series length {data_len}"),
Err(BollingerBandsWidthError::AllValuesNaN) =>
eprintln!("All input values are NaN"),
Err(BollingerBandsWidthError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {needed} valid samples, got {valid}"),
Err(other) => eprintln!("BBW failed: {other}"),
} Python Bindings
Basic Usage ▼
Feed NumPy arrays directly into the BBW binding:
import numpy as np
from vectorta import bollinger_bands_width
closes = np.array([101.5, 102.1, 100.9, 99.8, 101.2, 103.4], dtype=np.float64)
values = bollinger_bands_width(
closes,
period=20,
devup=2.0,
devdn=2.0,
matype="sma",
devtype=0,
kernel="auto",
)
print("BBW tail:", values[-5:]) Streaming Updates ▼
Maintain a rolling window without recalculating the full series:
from vectorta import BollingerBandsWidthStream
stream = BollingerBandsWidthStream(period=20, devup=2.0, devdn=2.0)
for close in close_feed:
bbw_value = stream.update(close)
if bbw_value is not None:
monitor_volatility(bbw_value) Batch Parameter Optimization ▼
Explore BBW parameter grids within Python notebooks:
import numpy as np
from vectorta import bollinger_bands_width_batch
closes = np.asarray(load_prices(), dtype=np.float64)
results = bollinger_bands_width_batch(
closes,
period_range=(10, 40, 5),
devup_range=(1.5, 3.0, 0.5),
devdn_range=(1.5, 3.0, 0.5),
kernel="auto",
)
values = results["values"]
periods = results["periods"]
devups = results["devups"]
devdns = results["devdns"] CUDA Acceleration ▼
CUDA bindings for BBW are planned; this section will mirror other CUDA-enabled indicators once released.
JavaScript/WASM Bindings
Basic Usage ▼
Call BBW directly from TypeScript:
import { bollinger_bands_width_js } from 'vectorta-wasm';
const closes = new Float64Array([101.5, 102.1, 100.9, 99.8, 101.2, 103.4]);
const values = bollinger_bands_width_js(closes, 20, 2.0, 2.0, 'sma', 0);
console.log('BBW values:', values); Memory-Efficient Operations ▼
Reuse WASM buffers for high-frequency volatility feeds:
import { bbw_alloc, bbw_free, bollinger_bands_width_into, memory } from 'vectorta-wasm';
const closes = new Float64Array([...]);
const length = closes.length;
const bytes = length * Float64Array.BYTES_PER_ELEMENT;
const highLevelExports = vectortaWasmInstance.exports as any;
const inputPtr = highLevelExports.__wbindgen_malloc(bytes);
const wasmInput = new Float64Array(memory.buffer, inputPtr, length);
wasmInput.set(closes);
const outputPtr = bbw_alloc(length);
bollinger_bands_width_into(
inputPtr,
outputPtr,
length,
20,
2.0,
2.0,
null,
null,
);
const bbwValues = new Float64Array(memory.buffer, outputPtr, length).slice();
bbw_free(outputPtr, length);
highLevelExports.__wbindgen_free(inputPtr, bytes); Batch Processing ▼
Evaluate multiple deviation and period combinations in a single call:
import { bollinger_bands_width_batch_js, bollinger_bands_width_batch_metadata_js } from 'vectorta-wasm';
const closes = new Float64Array([...]);
const metadata = bollinger_bands_width_batch_metadata_js(
10, 40, 5,
1.5, 3.0, 0.5,
1.5, 3.0, 0.5,
);
const combos = metadata.length / 3;
const results = bollinger_bands_width_batch_js(
closes,
10, 40, 5,
1.5, 3.0, 0.5,
1.5, 3.0, 0.5,
);
const length = closes.length;
const matrix: number[][] = [];
for (let i = 0; i < combos; i++) {
const start = i * length;
matrix.push(Array.from(results.slice(start, start + length)));
}
const firstPeriod = metadata[0];
const firstDevUp = metadata[1];
const firstDevDn = metadata[2];
const bbwValues = matrix[0]; Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05