What Is the CCI?
The Commodity Channel Index, developed by Donald Lambert in 1980, measures the deviation of the current price from its statistical mean. Despite the name, it works on any instrument -- forex, indices, crypto, and of course commodities.
CCI oscillates around zero with no fixed upper or lower bound. Readings above +100 suggest the price is well above its average (potentially overbought), while readings below -100 suggest the price is well below its average (potentially oversold).
Typical Price = (High + Low + Close) / 3
SMA = Simple Moving Average of Typical Price over N periods
Mean Deviation = Average absolute deviation from SMA
CCI = (Typical Price - SMA) / (0.015 * Mean Deviation) The 0.015 constant scales CCI so that approximately 75% of values fall between -100 and +100.
The Mean Reversion Thesis
Mean reversion is the idea that prices tend to return to their average after extreme moves. CCI quantifies "extreme" in statistical terms. When CCI reaches -150 or lower, the price has moved significantly below its rolling average, and the probability of a bounce increases.
Above average -- look for shorts
Normal range -- no signal
Below average -- look for longs
Fetching CCI via API
curl -H "X-API-Key: YOUR_API_KEY" \
"https://tickatlas.com/v1/indicator?symbol=EURUSD&indicator=CCI_20&timeframe=H4" Response:
{
"success": true,
"data": {
"symbol": "EURUSD",
"timeframe": "H4",
"indicator": "CCI_20",
"value": -142.87,
"bid": 1.0829,
"ask": 1.0830,
"updated_at": 1787229614,
"server_time": "2026-08-20T12:40:14+00:00"
}
} Python: CCI Mean Reversion Strategy
import requests
from dataclasses import dataclass
from typing import Optional
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://tickatlas.com/v1"
@dataclass
class CCISignal:
symbol: str
cci_value: float
signal_type: Optional[str] # "LONG", "SHORT", or None
entry_price: float
confidence: str # "standard", "high", "extreme"
def cci_mean_reversion(
symbol: str,
timeframe: str,
entry_threshold: float = 100,
high_threshold: float = 150,
extreme_threshold: float = 200,
) -> CCISignal:
"""Generate mean reversion signals based on CCI levels."""
resp = requests.get(
f"{BASE_URL}/indicator",
headers={"X-API-Key": API_KEY},
params={
"symbol": symbol,
"indicator": "CCI_20",
"timeframe": timeframe,
},
)
resp.raise_for_status()
data = resp.json()["data"]
cci = data["value"]
close = data["bid"] # current bid as the reference price
signal_type = None
confidence = "standard"
if cci <= -extreme_threshold:
signal_type = "LONG"
confidence = "extreme"
elif cci <= -high_threshold:
signal_type = "LONG"
confidence = "high"
elif cci <= -entry_threshold:
signal_type = "LONG"
confidence = "standard"
elif cci >= extreme_threshold:
signal_type = "SHORT"
confidence = "extreme"
elif cci >= high_threshold:
signal_type = "SHORT"
confidence = "high"
elif cci >= entry_threshold:
signal_type = "SHORT"
confidence = "standard"
return CCISignal(
symbol=symbol,
cci_value=round(cci, 2),
signal_type=signal_type,
entry_price=close,
confidence=confidence,
)
# Scan multiple symbols
symbols = ["EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "XAUUSD"]
for sym in symbols:
signal = cci_mean_reversion(sym, "H4")
if signal.signal_type:
print(f"{sym}: {signal.signal_type} (CCI={signal.cci_value}, {signal.confidence})")
else:
print(f"{sym}: No signal (CCI={signal.cci_value})") Adding a Trend Filter
Pure mean reversion without trend context is dangerous. A strong downtrend can keep CCI below -100 for weeks. Add a higher-timeframe trend filter:
def filtered_cci_signal(symbol: str) -> dict:
"""CCI mean reversion with EMA trend filter."""
# H4 CCI for the signal
cci_resp = requests.get(
f"{BASE_URL}/indicator",
headers={"X-API-Key": API_KEY},
params={"symbol": symbol, "indicator": "CCI_20", "timeframe": "H4"},
).json()["data"]
# D1 EMA for the trend
ema_resp = requests.get(
f"{BASE_URL}/indicator",
headers={"X-API-Key": API_KEY},
params={"symbol": symbol, "indicator": "EMA_50", "timeframe": "D1"},
).json()["data"]
cci = cci_resp["value"]
close = cci_resp["bid"] # current bid as the reference price
ema = ema_resp["value"]
daily_trend = "UP" if close > ema else "DOWN"
signal = None
# Only take longs when daily trend is up
if cci < -100 and daily_trend == "UP":
signal = "LONG"
# Only take shorts when daily trend is down
elif cci > 100 and daily_trend == "DOWN":
signal = "SHORT"
return {
"symbol": symbol,
"cci": round(cci, 2),
"daily_trend": daily_trend,
"signal": signal,
"reason": f"CCI={cci:.0f}, trend={daily_trend}",
} Exit Strategy
For a mean reversion strategy, the exit is when price returns to the mean:
CCI crosses back through zero
The most conservative exit. Price has returned to its average. Close the full position.
CCI reaches the opposite extreme
If you entered long at CCI -150, exit when CCI reaches +100. This captures the full mean reversion move.
Time-based exit
If CCI has not returned to zero within 10 bars, exit at market. The mean reversion thesis has failed.
Run this against live data.
Every account starts pay-as-you-go with $2.50 of credit and no card. Paste the key into the samples above and the requests work unchanged.