F13c: restructure the indicator catalogue into eight families
The original taxonomy was four classical families plus a statistics group, with the F1-F12 expansion slotted in as sub-categories. This regroups the whole 71-indicator catalogue into eight top-level families, each with at least five members: Moving Averages (12), Momentum Oscillators (13), Trend & Directional (9), Price Oscillators (5), Volatility & Bands (12), Trailing Stops (5), Volume (9), Price Statistics (7). - Wiki: docs/wiki/indicators/ reorganised into eight family folders; all 71 indicator pages moved with `git mv`. Every internal cross-link is normalised to `../<family>/Indicator-X.md`, each page's `Family` field is set to its new family, and two pre-existing `../Indicator-Chaining.md` links (should have been `../../`) are corrected. A link check confirms every relative wiki link resolves. - Indicators-Overview.md fully rewritten around the eight families; Home.md indicator reference and the README family table follow suit. - Warmup-Periods.md gains the eight F13 indicators; CHANGELOG records the 46-indicator expansion (25 -> 71) and the eight-family taxonomy. - Tests: Node indicators.test.js and Python test_new_indicators.py cover all eight new indicators (Node 91/91, Python 117/117 green). cargo fmt + clippy (core/wickra/data/wasm/node) clean; 508 core tests, 25 data tests and 74 doctests green.
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# BollingerBandwidth
|
||||
|
||||
> Bollinger Bandwidth — the width of the Bollinger Bands relative to the
|
||||
> middle band: a normalised volatility reading.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Family | Volatility & Bands |
|
||||
| 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
|
||||
[`BollingerBands`](../volatility-bands/Indicator-BollingerBands.md). Since the bands are
|
||||
`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
|
||||
|
||||
- [Indicator-BollingerBands.md](../volatility-bands/Indicator-BollingerBands.md) — the bands
|
||||
this measures.
|
||||
- [Indicator-PercentB.md](../volatility-bands/Indicator-PercentB.md) — the companion derived
|
||||
indicator: price *position* within the bands.
|
||||
- [Indicators-Overview.md](../../Indicators-Overview.md) — the full taxonomy.
|
||||
Reference in New Issue
Block a user