Skip to content

Indicator Cci

kingchenc edited this page May 23, 2026 · 2 revisions

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:

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

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

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

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

Wickra Wiki

Home

Getting started

Reference

Guides

Moving Averages

Price Oscillators

Momentum Oscillators

Trend & Directional

Price Oscillators

Volatility & Bands

Bands & Channels

Trailing Stops

Volume

Price Statistics

Clone this wiki locally