Bollinger Bands Width (BBW)
period = 20 (1–250) | devup = 2 (0–5) | devdn = 2 (0–5) | matype = sma | 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 to express the result as a ratio. 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 vector_ta::indicators::bollinger_bands_width::{
bollinger_bands_width, BollingerBandsWidthInput, BollingerBandsWidthParams
};
use vector_ta::utilities::data_loader::Candles;
let closes: Vec<f64> = vec![/* ... */]; // ensure closes.len() >= period
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 vector_ta::indicators::bollinger_bands_width::{
BollingerBandsWidthInput, BollingerBandsWidthParams
};
use vector_ta::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 vector_ta::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 vector_ta import bollinger_bands_width
closes = np.asarray(close_series, dtype=np.float64) # length >= period
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 vector_ta 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 vector_ta 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 helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).
import numpy as np
from vector_ta import (
bollinger_bands_width_cuda_batch_dev,
bollinger_bands_width_cuda_many_series_one_param_dev,
)
closes_f32 = np.asarray(close_series, dtype=np.float32)
# 1) One series, many parameter combinations (device output)
dev_values, meta = bollinger_bands_width_cuda_batch_dev(
closes_f32,
period_range=(10, 40, 5),
devup_range=(1.5, 3.0, 0.5),
devdn_range=(1.5, 3.0, 0.5),
device_id=0,
)
# 2) Many series (time-major), one parameter set (device output)
tm_f32 = np.asarray(close_matrix, dtype=np.float32) # shape: (rows, cols)
dev_values_tm = bollinger_bands_width_cuda_many_series_one_param_dev(
tm_f32.ravel(),
cols=tm_f32.shape[1],
rows=tm_f32.shape[0],
period=20,
devup=2.0,
devdn=2.0,
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
Call BBW directly from TypeScript:
import { bollinger_bands_width_js } from 'vectorta-wasm';
const closes = new Float64Array(loadPrices()); // length >= period
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]; CUDA Bindings (Rust)
use vector_ta::cuda::CudaBbw;
let cuda = CudaBbw::new(0)?;
// Entry points vary by indicator; call the appropriate method on the CUDA struct. Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28