Commodity Channel Index (CCI)

Haiyue
9min

I. What is CCI

The Commodity Channel Index (CCI) is a momentum oscillator that measures how far the price has deviated from its statistical average. It calculates the deviation between the Typical Price and its moving average, normalized by the Mean Deviation, to identify the beginning and end of trends as well as overbought/oversold conditions.

Historical Background

CCI was published by Donald Lambert in 1980 in Commodities magazine. Despite the “Commodity” in its name, CCI is applicable to all financial markets including stocks, forex, and cryptocurrencies. Lambert designed CCI with the original purpose of identifying cyclical turning points in commodity prices.

Indicator Classification

  • Type: Oscillator, displayed in a separate panel
  • Category: Momentum
  • Default Parameters: Period n=20n = 20
  • Value Range: Unbounded, typically oscillates between -200 and +200
The Meaning of the Constant 0.015

Lambert used the constant 0.015 in the formula so that approximately 70%-80% of CCI values would fall within the -100 to +100 range. This constant is based on statistical assumptions: under a normal distribution, the mean absolute deviation is approximately 0.7979 times the standard deviation, and 0.0151/(0.7979×2π×...)0.015 \approx 1 / (0.7979 \times \sqrt{2\pi} \times ...), empirically calibrating the scale.


II. Mathematical Principles and Calculation

Core Formulas

Step 1: Calculate Typical Price

TPt=Ht+Lt+Ct3TP_t = \frac{H_t + L_t + C_t}{3}

Step 2: Calculate SMA of Typical Price

SMA(TP)t=1ni=0n1TPtiSMA(TP)_t = \frac{1}{n}\sum_{i=0}^{n-1} TP_{t-i}

Step 3: Calculate Mean Deviation

MDt=1ni=0n1TPtiSMA(TP)tMD_t = \frac{1}{n}\sum_{i=0}^{n-1} |TP_{t-i} - SMA(TP)_t|

Step 4: Calculate CCI

CCIt=TPtSMA(TP)t0.015×MDtCCI_t = \frac{TP_t - SMA(TP)_t}{0.015 \times MD_t}

Step-by-Step Calculation Logic

  1. Calculate Typical Price: (High + Low + Close) / 3 for each bar
  2. Calculate SMA(20) of TP: Simple average of the past 20 TP values
  3. Calculate Mean Deviation: Average of the absolute differences between each TP and the SMA over the past 20 periods
  4. Normalize: (Current TP - SMA) / (0.015 x MD)

Value Interpretation

CCI ValueMeaning
> +100Price significantly above statistical average, potential overbought or strong uptrend
0Price exactly at the statistical average
< -100Price significantly below statistical average, potential oversold or strong downtrend
Why Mean Deviation Instead of Standard Deviation?

Lambert chose Mean Deviation over standard deviation because MD is less sensitive to extreme values, making CCI more stable. Under normal distribution assumptions, MD is approximately 0.7979 times the standard deviation.


III. Python Implementation

import numpy as np
import pandas as pd


def cci(high: pd.Series,
        low: pd.Series,
        close: pd.Series,
        period: int = 20) -> pd.Series:
    """
    Calculate the Commodity Channel Index (CCI)

    Parameters
    ----------
    high : pd.Series
        High price series
    low : pd.Series
        Low price series
    close : pd.Series
        Close price series
    period : int
        Calculation period, default 20

    Returns
    -------
    pd.Series
        CCI values
    """
    tp = (high + low + close) / 3

    tp_sma = tp.rolling(window=period, min_periods=period).mean()

    # Calculate mean deviation
    md = tp.rolling(window=period, min_periods=period).apply(
        lambda x: np.mean(np.abs(x - x.mean())), raw=True
    )

    cci_values = (tp - tp_sma) / (0.015 * md)

    return cci_values


# ========== Usage Example ==========
if __name__ == "__main__":
    np.random.seed(42)
    dates = pd.date_range("2024-01-01", periods=120, freq="D")
    base = 100 + np.cumsum(np.random.randn(120) * 1.0)

    df = pd.DataFrame({
        "date": dates,
        "open":  base + np.random.randn(120) * 0.3,
        "high":  base + np.abs(np.random.randn(120) * 0.8),
        "low":   base - np.abs(np.random.randn(120) * 0.8),
        "close": base,
        "volume": np.random.randint(1000, 10000, size=120),
    })
    df.set_index("date", inplace=True)

    # Calculate CCI
    df["CCI_20"] = cci(df["high"], df["low"], df["close"], period=20)

    print("=== CCI Results (last 15 rows) ===")
    print(df[["close", "CCI_20"]].tail(15).to_string())

    # Signal generation
    df["signal"] = np.where(
        df["CCI_20"] > 100, 1,       # Strong zone -> Trend long
        np.where(df["CCI_20"] < -100, -1, 0)  # Weak zone -> Trend short
    )

    # Crossover of +100/-100
    df["zone"] = np.sign(df["CCI_20"].clip(-100, 100).replace(0, np.nan)).ffill()
    cross_up = (df["CCI_20"] > 100) & (df["CCI_20"].shift(1) <= 100)
    cross_down = (df["CCI_20"] < -100) & (df["CCI_20"].shift(1) >= -100)

    print("\n=== CCI Crosses Above +100 ===")
    print(df.loc[cross_up, ["close", "CCI_20"]].to_string())
    print("\n=== CCI Crosses Below -100 ===")
    print(df.loc[cross_down, ["close", "CCI_20"]].to_string())

IV. Problems the Indicator Solves

1. Trend Identification and Confirmation

CCI can be used to identify the start of new trends:

  • CCI crosses above +100 — Price breaks above the statistically normal range, potentially starting a new uptrend
  • CCI crosses below -100 — Price breaks below the statistically normal range, potentially starting a new downtrend

2. Overbought / Oversold

Traditional oscillator usage:

ConditionSignal
CCI drops back below +100 from aboveSell (overbought reversion)
CCI rises back above -100 from belowBuy (oversold reversion)

3. Divergence Signals

  • Bearish Divergence: Price new high + CCI fails to make new high — upward momentum weakening
  • Bullish Divergence: Price new low + CCI fails to make new low — downward momentum weakening

4. Zero Line Analysis

  • CCI operating above zero — price above statistical average, bullish bias
  • CCI operating below zero — price below statistical average, bearish bias
Caution

CCI has two fundamentally different usage modes: (1) as a mean reversion indicator, trading reversals when CCI returns to the +/-100 zone; (2) as a trend-following indicator, trading breakouts when CCI exceeds +/-100. These two approaches must not be mixed; you must choose the strategy based on the current market state.


V. Advantages, Disadvantages, and Use Cases

Advantages

AdvantageDescription
UnboundedUnlike RSI’s 0-100 limit, CCI can reflect extreme deviations
Solid statistical foundationNormalization via mean absolute deviation is statistically sound
Dual purposeCan be used for both trend following and mean reversion
Uses HLCCombines high, low, and close price information

Disadvantages

DisadvantageDescription
Ambiguous signalsUnbounded range makes extreme value definition less clear than RSI
Mode selection requiredChoosing between trend-following vs. mean-reversion adds subjectivity
Unstable in extremesWhen MD is very small, CCI values can be extremely amplified
Single periodDefault 20-period may not suit all markets

Use Cases

  • Commodity futures: CCI was originally designed for commodities and excels with cyclical instruments
  • Trend breakouts: CCI’s first breach of +/-100 can signal trend initiation
  • Cyclical markets: Most effective on assets with clear cyclical patterns
ComparisonCCIRSIStochastic
Value RangeUnbounded0-1000-100
NormalizationMean DeviationWilder SmoothingPrice Range
Usage ModeTrend + Mean ReversionMainly Mean ReversionMainly Mean Reversion
Data InputH/L/CCH/L/C
Practical Tips
  1. Clarify strategy mode: Before using CCI, determine whether the market is trending or ranging, then choose the corresponding signal logic.
  2. Combine with ADX: When ADX > 25, use trend mode (follow breakouts of +/-100); when ADX < 20, use mean reversion mode.
  3. Extreme values as stop-loss reference: CCI > +200 or < -200 typically indicates extreme deviation and serves as a potential risk warning.
  4. Multi-period validation: Observe CCI(14) and CCI(50) simultaneously — short period for signals, long period for filtering.