2026-05-22 18:30:49 +02:00
|
|
|
|
# BollingerBandwidth
|
|
|
|
|
|
|
|
|
|
|
|
> Bollinger Bandwidth — the width of the Bollinger Bands relative to the
|
|
|
|
|
|
> middle band: a normalised volatility reading.
|
|
|
|
|
|
|
|
|
|
|
|
## Quick reference
|
|
|
|
|
|
|
|
|
|
|
|
| Field | Value |
|
|
|
|
|
|
|-------|-------|
|
2026-05-22 21:21:56 +02:00
|
|
|
|
| Family | Volatility & Bands |
|
2026-05-22 18:30:49 +02:00
|
|
|
|
| Input type | `f64` (single close) |
|
|
|
|
|
|
| Output type | `f64` |
|
|
|
|
|
|
| Output range | `[0, ∞)` |
|
|
|
|
|
|
| Default parameters | `(period = 20, multiplier = 2.0)` (Python) |
|
|
|
|
|
|
| Warmup period | `period` |
|
|
|
|
|
|
| Interpretation | Band width as a fraction of price; lows flag a "squeeze". |
|
|
|
|
|
|
|
|
|
|
|
|
## Formula
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Bandwidth = (upper − lower) / middle
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
where `upper`, `middle` and `lower` come from
|
2026-05-22 21:21:56 +02:00
|
|
|
|
[`BollingerBands`](../volatility-bands/Indicator-BollingerBands.md). Since the bands are
|
2026-05-22 18:30:49 +02:00
|
|
|
|
`middle ± multiplier · stddev`, the bandwidth simplifies to
|
|
|
|
|
|
`2 · multiplier · stddev / middle` — volatility normalised by price level.
|
|
|
|
|
|
Its extremes name two classic patterns: the **squeeze** (bandwidth at a
|
|
|
|
|
|
multi-month low — a coiled, quiet market that often precedes a sharp
|
|
|
|
|
|
move) and the **bulge** (bandwidth at an extreme high — an exhausted,
|
|
|
|
|
|
over-extended move).
|
|
|
|
|
|
|
|
|
|
|
|
## Parameters
|
|
|
|
|
|
|
|
|
|
|
|
| Name | Type | Default | Valid range | Description |
|
|
|
|
|
|
|--------------|---------|----------------|-------------|-------------|
|
|
|
|
|
|
| `period` | `usize` | `20` (Python) | `>= 1` | Bollinger Bands period. `0` errors with `Error::PeriodZero`. |
|
|
|
|
|
|
| `multiplier` | `f64` | `2.0` (Python) | `> 0` | Band standard-deviation multiplier. `<= 0` errors with `Error::NonPositiveMultiplier`. |
|
|
|
|
|
|
|
|
|
|
|
|
The Python binding defaults the pair to `(20, 2.0)`.
|
|
|
|
|
|
|
|
|
|
|
|
## Inputs / Outputs
|
|
|
|
|
|
|
|
|
|
|
|
From `crates/wickra-core/src/indicators/bollinger_bandwidth.rs`:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
impl Indicator for BollingerBandwidth {
|
|
|
|
|
|
type Input = f64;
|
|
|
|
|
|
type Output = f64;
|
|
|
|
|
|
// update(&mut self, input: f64) -> Option<f64>
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
A single `f64` close in, an `Option<f64>` out. Python maps this to
|
|
|
|
|
|
`float | None` / `numpy.ndarray` (NaN warmup); Node to `number | null` /
|
|
|
|
|
|
`Array<number>` (NaN warmup).
|
|
|
|
|
|
|
|
|
|
|
|
## Warmup
|
|
|
|
|
|
|
|
|
|
|
|
`warmup_period() == period` — identical to the underlying `BollingerBands`.
|
|
|
|
|
|
|
|
|
|
|
|
## Edge cases
|
|
|
|
|
|
|
|
|
|
|
|
- **Constant series.** Flat prices collapse the bands onto the middle, so
|
|
|
|
|
|
the width — and bandwidth — is `0.0` (`constant_series_yields_zero`
|
|
|
|
|
|
pins this).
|
|
|
|
|
|
- **Zero middle band.** Bandwidth is undefined against a `0.0` middle
|
|
|
|
|
|
band; the indicator reports `0.0` for that bar.
|
|
|
|
|
|
- **Non-negative.** Bandwidth is `(upper − lower) / middle` with
|
|
|
|
|
|
`upper >= lower` and a positive middle band, so it is never negative
|
|
|
|
|
|
(`output_is_non_negative` pins this).
|
|
|
|
|
|
- **Reset.** `bbw.reset()` clears the underlying bands.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
### Rust
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use wickra::{BatchExt, Indicator, BollingerBandwidth};
|
|
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
|
|
let mut bbw = BollingerBandwidth::new(20, 2.0)?;
|
|
|
|
|
|
// A flat stretch then a volatile stretch: bandwidth rises.
|
|
|
|
|
|
let mut prices: Vec<f64> = vec![100.0; 30];
|
|
|
|
|
|
prices.extend((0..30).map(|i| 100.0 + (f64::from(i)).sin() * 10.0));
|
|
|
|
|
|
let out = bbw.batch(&prices);
|
|
|
|
|
|
println!("flat-window bandwidth: {:?}", out[25]);
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Output:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
flat-window bandwidth: Some(0.0)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
While prices are flat the bands sit on top of each other, so bandwidth is
|
|
|
|
|
|
`0`; once volatility arrives it climbs.
|
|
|
|
|
|
|
|
|
|
|
|
### Python
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
import wickra as ta
|
|
|
|
|
|
|
|
|
|
|
|
bbw = ta.BollingerBandwidth(20, 2.0)
|
|
|
|
|
|
prices = np.full(40, 100.0) # flat series
|
|
|
|
|
|
print(bbw.batch(prices)[-1]) # 0.0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Output:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
0.0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Node
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
const ta = require('wickra');
|
|
|
|
|
|
const bbw = new ta.BollingerBandwidth(20, 2.0);
|
|
|
|
|
|
const prices = Array.from({ length: 60 }, (_, i) => 100 + Math.sin(i * 0.3) * 6);
|
|
|
|
|
|
console.log('warmupPeriod:', bbw.warmupPeriod());
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Interpretation
|
|
|
|
|
|
|
|
|
|
|
|
`BollingerBandwidth` is the standard way to quantify the Bollinger
|
|
|
|
|
|
"squeeze". Volatility is mean-reverting and cyclical: extended periods of
|
|
|
|
|
|
low bandwidth tend to be followed by expansion, and vice versa. Traders
|
|
|
|
|
|
watch for bandwidth dropping to a multi-month low (the squeeze) as a
|
|
|
|
|
|
heads-up that a directional move is loading — then take the direction
|
|
|
|
|
|
from price breaking the band, or from a separate trend indicator.
|
|
|
|
|
|
|
|
|
|
|
|
## Common pitfalls
|
|
|
|
|
|
|
|
|
|
|
|
- **Treating the squeeze as directional.** Low bandwidth says a move is
|
|
|
|
|
|
*coming*, not which way. Confirm direction separately.
|
|
|
|
|
|
- **Comparing raw bandwidth across instruments without context.** It is
|
|
|
|
|
|
normalised by price, which helps, but "low" is relative to each
|
|
|
|
|
|
instrument's own history — compare against its own range.
|
|
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
|
|
John Bollinger, *Bollinger on Bollinger Bands* (2001). Bandwidth is one
|
|
|
|
|
|
of Bollinger's two derived indicators (with %b).
|
|
|
|
|
|
|
|
|
|
|
|
## See also
|
|
|
|
|
|
|
2026-05-22 21:21:56 +02:00
|
|
|
|
- [Indicator-BollingerBands.md](../volatility-bands/Indicator-BollingerBands.md) — the bands
|
2026-05-22 18:30:49 +02:00
|
|
|
|
this measures.
|
2026-05-22 21:21:56 +02:00
|
|
|
|
- [Indicator-PercentB.md](../volatility-bands/Indicator-PercentB.md) — the companion derived
|
2026-05-22 18:30:49 +02:00
|
|
|
|
indicator: price *position* within the bands.
|
|
|
|
|
|
- [Indicators-Overview.md](../../Indicators-Overview.md) — the full taxonomy.
|