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,198 @@
|
||||
# CCI
|
||||
|
||||
> Commodity Channel Index — measures how far the current typical price
|
||||
> deviates from its rolling mean, in units of mean absolute deviation
|
||||
> scaled by Lambert's constant.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Family | Momentum Oscillators |
|
||||
| Input type | `Candle` |
|
||||
| Output type | `f64` |
|
||||
| Output range | unbounded (typically `[−200, +200]` thanks to the 0.015 factor) |
|
||||
| Default parameters | `period = 20` (Python) |
|
||||
| Warmup period | `period` (20 for `period = 20`) |
|
||||
| Interpretation | `> +100` overbought, `< −100` oversold (Lambert) |
|
||||
|
||||
## Formula
|
||||
|
||||
For each candle, compute the typical price `TP = (high + low + close) / 3`,
|
||||
then over the rolling `period`-bar window:
|
||||
|
||||
```
|
||||
SMA_TP_t = (TP_{t-period+1} + … + TP_t) / period
|
||||
MAD_t = (1 / period) · Σ |TP_i − SMA_TP_t| for i = t-period+1 … t
|
||||
|
||||
CCI_t = (TP_t − SMA_TP_t) / (factor · MAD_t)
|
||||
```
|
||||
|
||||
The default `factor` is Lambert's `0.015`, chosen empirically so that
|
||||
roughly 70–80 % of values fall inside `[−100, +100]`. The implementation
|
||||
exposes the factor through `Cci::with_factor(period, factor)` if you want
|
||||
to retune it for an asset with very different volatility characteristics.
|
||||
|
||||
When `MAD == 0` (a perfectly flat window), the implementation returns `0`
|
||||
rather than dividing by zero.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Name | Type | Default (Python) | Valid range | Description |
|
||||
|------|------|------------------|-------------|-------------|
|
||||
| `period` | `usize` | `20` | `>= 1` | Rolling window length for both the SMA of typical price and the MAD. |
|
||||
| `factor` | `f64` | `0.015` (`Cci::new`) | `> 0`, finite | Lambert's scaling constant; configurable via `Cci::with_factor`. |
|
||||
|
||||
`Cci::new(0)` returns `Error::PeriodZero`. `Cci::with_factor(_, factor)`
|
||||
returns `Error::NonPositiveMultiplier` when `factor <= 0` or non-finite.
|
||||
|
||||
## Inputs / Outputs
|
||||
|
||||
From `impl Indicator for Cci`:
|
||||
|
||||
```rust
|
||||
type Input = Candle;
|
||||
type Output = f64;
|
||||
fn update(&mut self, candle: Candle) -> Option<f64>;
|
||||
```
|
||||
|
||||
Python's `CCI.batch(high, low, close)` returns a 1-D `float64` `np.ndarray`
|
||||
with `NaN` during warmup. Node's `CCI.batch(high, low, close)` returns a
|
||||
flat `number[]` (also `NaN` during warmup); the Node binding does not
|
||||
expose a streaming `update()` (`bindings/node/index.d.ts` lists only
|
||||
`constructor` and `batch`).
|
||||
|
||||
## Warmup
|
||||
|
||||
`warmup_period()` returns exactly `period`. CCI does not consume diffs —
|
||||
it only needs `period` typical-price samples to populate its rolling
|
||||
window before it can compute an SMA and MAD. In streaming terms, calls
|
||||
`1..period` return `None`; the `period`-th call returns the first value.
|
||||
|
||||
## Edge cases
|
||||
|
||||
- **Flat input.** Every `TP` is the SMA, so `MAD == 0` and the
|
||||
implementation returns `0.0` (test `flat_candles_yield_zero`). This
|
||||
avoids the divide-by-zero that would otherwise produce `NaN` /
|
||||
`±∞`.
|
||||
- **Custom factor.** `Cci::with_factor(period, factor)` lets you replace
|
||||
Lambert's `0.015`. Picking a smaller factor widens the typical range
|
||||
of CCI values; picking a larger one compresses them.
|
||||
- **Reset.** `reset()` clears the rolling window and the running sum,
|
||||
returning the indicator to the freshly-constructed state.
|
||||
|
||||
## Examples
|
||||
|
||||
### Rust
|
||||
|
||||
```rust
|
||||
use wickra::{BatchExt, Candle, Cci, Indicator};
|
||||
|
||||
let candles: Vec<Candle> = (0..25)
|
||||
.map(|i| {
|
||||
let m = 50.0 + i as f64;
|
||||
Candle::new(m, m + 1.0, m - 1.0, m, 1.0, 0).unwrap()
|
||||
})
|
||||
.collect();
|
||||
let mut cci = Cci::new(20)?;
|
||||
let out = cci.batch(&candles);
|
||||
println!("row 19 = {}", out[19].unwrap());
|
||||
println!("row 24 = {}", out[24].unwrap());
|
||||
# Ok::<(), wickra::Error>(())
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
```
|
||||
row 19 = 126.66666666666667
|
||||
row 24 = 126.66666666666667
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import wickra as ta
|
||||
|
||||
i = np.arange(25, dtype=float)
|
||||
m = 50.0 + i
|
||||
high = m + 1.0
|
||||
low = m - 1.0
|
||||
close = m
|
||||
cci = ta.CCI(20)
|
||||
out = cci.batch(high, low, close)
|
||||
print('row 19:', out[19])
|
||||
print('row 24:', out[24])
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
```
|
||||
row 19: 126.66666666666667
|
||||
row 24: 126.66666666666667
|
||||
```
|
||||
|
||||
### Node
|
||||
|
||||
```javascript
|
||||
const wickra = require('wickra');
|
||||
|
||||
const n = 25;
|
||||
const high = [], low = [], close = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const m = 50 + i;
|
||||
high.push(m + 1);
|
||||
low.push(m - 1);
|
||||
close.push(m);
|
||||
}
|
||||
const cci = new wickra.CCI(20);
|
||||
const out = cci.batch(high, low, close);
|
||||
console.log('row 19:', out[19]);
|
||||
console.log('row 24:', out[24]);
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
```
|
||||
row 19: 126.66666666666667
|
||||
row 24: 126.66666666666667
|
||||
```
|
||||
|
||||
## Interpretation
|
||||
|
||||
- **±100 threshold.** Lambert's published convention is to treat values
|
||||
above `+100` as overbought and below `−100` as oversold. The choice
|
||||
of `0.015` for the divisor is what makes the threshold meaningful;
|
||||
changing the factor changes the threshold.
|
||||
- **Zero-line cross.** `CCI` crossing zero says the typical price has
|
||||
moved through its `period`-bar mean — sometimes used as a
|
||||
trend-direction filter.
|
||||
- **Divergence.** As with RSI/Stochastic, a price making a new high
|
||||
while CCI makes a lower high is a classic bearish divergence.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
- **CCI is unbounded.** Unlike RSI or Stochastic, CCI can spike well
|
||||
outside `±100` in volatile markets. Threshold-based rules should be
|
||||
paired with a maximum-absolute-value guard, or you will mis-classify
|
||||
legitimate breakouts as "extreme overbought".
|
||||
- **The 0.015 factor is empirical, not derived.** It was chosen by
|
||||
Lambert in 1980 for commodity futures markets. Modern equities and
|
||||
crypto have wider distributions; if your `|CCI|` distribution sits
|
||||
almost entirely outside `±100`, retune via `Cci::with_factor` rather
|
||||
than rewriting downstream thresholds.
|
||||
|
||||
## References
|
||||
|
||||
- Donald Lambert, "Commodity Channel Index: Tools for Trading Cyclical
|
||||
Trends", *Commodities Magazine*, October 1980 — the original
|
||||
publication, including the empirical choice of `0.015`.
|
||||
|
||||
## See also
|
||||
|
||||
- [Indicator: Rsi](../momentum-oscillators/Indicator-Rsi.md) — bounded sibling for comparison.
|
||||
- [Indicator: WilliamsR](../momentum-oscillators/Indicator-WilliamsR.md) — another candle-input
|
||||
oscillator, range-based rather than deviation-based.
|
||||
- [Indicator: Mfi](../momentum-oscillators/Indicator-Mfi.md) — volume-weighted RSI; useful as a
|
||||
confirmation alongside CCI.
|
||||
- [Warmup Periods](../../Warmup-Periods.md) — `period` (no off-by-one).
|
||||
Reference in New Issue
Block a user