Files
wickra/docs/wiki/indicators/momentum/Indicator-Cci.md
T
kingchenc 278b6afaa4 E4: commit the documentation sources
The 33 Markdown files under docs/wiki/ were never tracked. Commit them
into the repository so the documentation is versioned alongside the
code: 8 top-level pages plus 25 per-indicator deep dives under
indicators/{momentum,trend,volatility,volume}/.

The pages are kept in-repo (not pushed to a flat GitHub Wiki), so the
relative indicators/<family>/... links in Home.md resolve correctly
when rendered on GitHub.
2026-05-22 16:18:04 +02:00

200 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |
| Sub-category | unbounded oscillator |
| 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 7080 % 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](Indicator-Rsi.md) — bounded sibling for comparison.
- [Indicator: WilliamsR](Indicator-WilliamsR.md) — another candle-input
oscillator, range-based rather than deviation-based.
- [Indicator: Mfi](Indicator-Mfi.md) — volume-weighted RSI; useful as a
confirmation alongside CCI.
- [Warmup Periods](../../Warmup-Periods.md) — `period` (no off-by-one).