Commodity Channel Index (CCI)
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
- Value Range: Unbounded, typically oscillates between -200 and +200
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 , empirically calibrating the scale.
II. Mathematical Principles and Calculation
Core Formulas
Step 1: Calculate Typical Price
Step 2: Calculate SMA of Typical Price
Step 3: Calculate Mean Deviation
Step 4: Calculate CCI
Step-by-Step Calculation Logic
- Calculate Typical Price: (High + Low + Close) / 3 for each bar
- Calculate SMA(20) of TP: Simple average of the past 20 TP values
- Calculate Mean Deviation: Average of the absolute differences between each TP and the SMA over the past 20 periods
- Normalize: (Current TP - SMA) / (0.015 x MD)
Value Interpretation
| CCI Value | Meaning |
|---|---|
| > +100 | Price significantly above statistical average, potential overbought or strong uptrend |
| 0 | Price exactly at the statistical average |
| < -100 | Price significantly below statistical average, potential oversold or strong downtrend |
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:
| Condition | Signal |
|---|---|
| CCI drops back below +100 from above | Sell (overbought reversion) |
| CCI rises back above -100 from below | Buy (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
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
| Advantage | Description |
|---|---|
| Unbounded | Unlike RSI’s 0-100 limit, CCI can reflect extreme deviations |
| Solid statistical foundation | Normalization via mean absolute deviation is statistically sound |
| Dual purpose | Can be used for both trend following and mean reversion |
| Uses HLC | Combines high, low, and close price information |
Disadvantages
| Disadvantage | Description |
|---|---|
| Ambiguous signals | Unbounded range makes extreme value definition less clear than RSI |
| Mode selection required | Choosing between trend-following vs. mean-reversion adds subjectivity |
| Unstable in extremes | When MD is very small, CCI values can be extremely amplified |
| Single period | Default 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
Comparison with Related Indicators
| Comparison | CCI | RSI | Stochastic |
|---|---|---|---|
| Value Range | Unbounded | 0-100 | 0-100 |
| Normalization | Mean Deviation | Wilder Smoothing | Price Range |
| Usage Mode | Trend + Mean Reversion | Mainly Mean Reversion | Mainly Mean Reversion |
| Data Input | H/L/C | C | H/L/C |
- Clarify strategy mode: Before using CCI, determine whether the market is trending or ranging, then choose the corresponding signal logic.
- Combine with ADX: When ADX > 25, use trend mode (follow breakouts of +/-100); when ADX < 20, use mean reversion mode.
- Extreme values as stop-loss reference: CCI > +200 or < -200 typically indicates extreme deviation and serves as a potential risk warning.
- Multi-period validation: Observe CCI(14) and CCI(50) simultaneously — short period for signals, long period for filtering.