Indicator TutorialPine Script v6

How to Code the Commodity Channel Index (CCI) in Pine Script

Understand mean absolute deviation, reference levels and confirmed threshold events.

The Commodity Channel Index (CCI), introduced by Donald Lambert, measures how far price deviates from its statistical average. Traders use CCI to spot cyclical turns, overextension, and momentum shifts. This guide walks you through the mathematics behind CCI, how to interpret readings around +100 and -100, and a complete Pine Script v6 implementation using ta.cci() with optional visual cues and alerts.

What Is the Commodity Channel Index?

The Commodity Channel Index (CCI) is a momentum oscillator without fixed RSI-style limits that compares the current typical price to its simple moving average, scaled by a constant and mean absolute deviation.

Unlike RSI, CCI is not capped at 0–100; extreme readings simply indicate unusually large deviations from the mean for the chosen lookback.

Core idea

When CCI is high, price is stretched above its recent average; when low, it is stretched below. Many practitioners treat crossings through +100 and -100 as trend or momentum cues rather than automatic reversal triggers.

TradingView documents the formula and divergence definitions. Its help page lists Close as the built-in source default; this example and Pineify’s CCI module use hlc3. Set the same source before comparing values.

Why Traders Use This Indicator

  • Spot overextension from a rolling mean using a normalized deviation scale.
  • Combine trend direction with momentum: sustained readings beyond +100 or -100 often accompany impulsive moves.
  • Use zero-line crosses and level hooks as simpler signal variants on liquid markets.
  • Layer CCI with volume or structure (swing highs/lows) to filter range chop.
ParameterDefaultDescription
Length (period)20Number of observations used for the mean and mean absolute deviation. Changing it changes the measurement window; no optimal length is established here.
Sourcehlc3 (typical price)This example uses typical price (high + low + close) / 3. Close is a different input series, not a guaranteed faster or better signal.
Overbought / oversold levels+100 / -100Common reference lines. Context matters: in strong trends CCI can remain beyond these levels for extended periods.
Constant (0.015)0.015Scaling constant in the CCI formula. It does not guarantee a fixed percentage of readings inside the reference levels on every dataset.

How CCI Is Calculated

For the chosen source, let mean = SMA(source, n). For the current window, MD = sum(abs(source[i] − current mean), i = 0…n−1) / n. Every observation uses the same current window mean; a moving average of each bar’s own deviation is a different calculation.

CCI = (current source − mean) / (0.015 × MD). This is mean absolute deviation, not standard deviation; there is no sample-versus-population variance switch.

For hypothetical source values 98, 100 and 102, mean = 100 and MD = 4/3. CCI at 102 is 2 / (0.015 × 4/3) = 100. Multiplying all three prices by the same positive factor scales numerator and denominator equally, leaving CCI unchanged. A small price-unit deviation alone does not imply an extreme CCI.

A flat window has MD = 0, so the ratio is undefined. The example uses ta.cci and leaves unavailable readings unavailable instead of replacing them with zero. It requires at least two bars as an input length.

Signal Interpretation

Above +100 and below −100 are reference states, not automatic entries or reversals. The code marks a crossing event, not every bar that remains beyond a threshold.

Regular bullish divergence pairs a lower price low with a higher CCI low; regular bearish divergence pairs a higher price high with a lower CCI high. A pivot-based detector would also need explicit pivot confirmation and timing rules. This script does not detect divergence.

The CCI line and table update during an open bar. Markers, zero-cross backgrounds and alert conditions wait for bar close. Create an alert in TradingView and recreate it after changing the script or inputs; alerts use a saved snapshot. Revised source data can still change history.

Combining with Other Indicators

A price moving average can provide a separate direction condition; ADX measures strength rather than direction. Adding volume, ATR or a trend filter changes the rule and requires its own test. No combination here has demonstrated better returns.

To use Strategy Optimizer, first create an order-executing strategy with entries, exits, costs and exposed inputs. This indicator does not generate trades or Strategy Tester performance.

Writing Pine Script Manually

Challenges of Manual Coding

Use the current window mean for every absolute-deviation term.

Match source, lookback, available history and rounding when comparing implementations.

Keep confirmed chart markers and alerts on the same condition.

Specify pivot timing separately if adding divergence detection.

Pine Script v6
//@version=6
indicator("CCI Tutorial (ta.cci)", shorttitle="CCI", overlay=false)

// -----------------------------------------------------------------------------
// Inputs
// -----------------------------------------------------------------------------
length = input.int(20, "CCI Length", minval=2, tooltip="Lookback for SMA and mean deviation")
src = input.source(hlc3, "Source", tooltip="Classic CCI uses typical price (hlc3)")
obLevel = input.float(100.0, "Overbought level", step=1.0)
osLevel = input.float(-100.0, "Oversold level", step=1.0)
showBands = input.bool(true, "Shade between reference levels")
showZero = input.bool(true, "Highlight zero line crosses")
showFill = input.bool(true, "Fill between CCI and zero")

// -----------------------------------------------------------------------------
// Core calculation (built-in matches textbook CCI)
// -----------------------------------------------------------------------------
if osLevel >= obLevel
    runtime.error("Lower reference must be below upper reference.")
cci = ta.cci(src, length)

// -----------------------------------------------------------------------------
// Signal helpers
// -----------------------------------------------------------------------------
crossAboveObRaw = ta.crossover(cci, obLevel)
crossAboveOb = barstate.isconfirmed and crossAboveObRaw
crossBelowOsRaw = ta.crossunder(cci, osLevel)
crossBelowOs = barstate.isconfirmed and crossBelowOsRaw
crossZeroUpRaw = ta.crossover(cci, 0)
crossZeroUp = barstate.isconfirmed and crossZeroUpRaw
crossZeroDnRaw = ta.crossunder(cci, 0)
crossZeroDn = barstate.isconfirmed and crossZeroDnRaw

// -----------------------------------------------------------------------------
// Plots
// -----------------------------------------------------------------------------
cciColor = cci > obLevel ? color.teal : cci < osLevel ? color.orange : color.silver
p_cci = plot(cci, "CCI", color=cciColor, linewidth=2)
p_zero = plot(0, "Zero baseline", color=color.new(color.gray, 100), display=display.all)
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)

hline(obLevel, "OB", color=color.new(color.teal, 40))
hline(osLevel, "OS", color=color.new(color.orange, 40))
hline(200, "+200 ref", color=color.new(color.teal, 80))
hline(-200, "-200 ref", color=color.new(color.orange, 80))

p_obBand = plot(showBands ? obLevel : na, display=display.none)
p_osBand = plot(showBands ? osLevel : na, display=display.none)
fill(p_obBand, p_osBand, color.new(color.blue, 92))

plotshape(crossAboveOb, title="Cross above OB", style=shape.triangleup, location=location.bottom, color=color.teal, size=size.tiny, text="OB+")
plotshape(crossBelowOs, title="Cross below OS", style=shape.triangledown, location=location.top, color=color.orange, size=size.tiny, text="OS-")

bgcolor(showZero and crossZeroUp ? color.new(color.green, 90) : showZero and crossZeroDn ? color.new(color.red, 90) : na)

fill(p_cci, p_zero, color=showFill ? (cci >= 0 ? color.new(color.teal, 85) : color.new(color.orange, 85)) : na)

// -----------------------------------------------------------------------------
// Alerts
// -----------------------------------------------------------------------------
alertcondition(crossAboveOb, "CCI cross above upper reference", "CCI crossed above overbought threshold")
alertcondition(crossBelowOs, "CCI cross below lower reference", "CCI crossed below oversold threshold")
alertcondition(crossZeroUp or crossZeroDn, "CCI cross zero", "CCI crossed the zero line")

// -----------------------------------------------------------------------------
// Data window helpers
// -----------------------------------------------------------------------------
var table info = table.new(position.top_right, 1, 1, bgcolor=color.new(color.black, 80))
if barstate.islast
    table.cell(info, 0, 0, "CCI: " + str.tostring(cci, "#.##"), text_color=color.white)

Maintenance Note: The line and table update intrabar; event markers and alerts wait for bar close. No divergence detector, trend filter, volatility floor or trading orders are implemented. Missing CCI is not zero. Compare matching sources after warm-up; syntax validity is not evidence of profitability.

Build with Pineify Visual Editor

Pineify lets you configure supported indicators and export Pine Script. Available settings and plan requirements vary by indicator. Compare the generated rules with the example before using its signals.

FeatureManual Pine ScriptPineify Visual Editor
CCI calculationUse ta.cci with explicit source and length.The CCI module maps to ta.cci and exposes Source and Length.
DrawingWrite reference levels, colors and event markers.Configure the CCI line and band color. Custom event behavior requires separate conditions or reviewed AI-generated code.
VerificationCompare matching chart values and event timing.Inspect generated source and perform the same checks.

Configure the existing CCI module with source and length.

Use its output in explicitly defined conditions.

Request custom logic from AI Coding Agent, then review and test it.

Step-by-Step Tutorial

1

Create a new indicator project

Open Pineify builder, choose indicator mode, and set the title and short title for your CCI variant.

2

Add the CCI core

Insert a CCI function block mapped to ta.cci() with length and source inputs.

3

Configure levels and styles

Set the CCI line and band color. Request custom reference levels and closed-bar events separately; do not assume the default module matches this example.

4

Layer confirmations

Specify the exact additional condition and compare its behavior with the unfiltered rule.

5

Add alerts

Wire alert conditions for level crosses, zero-line crosses, or custom boolean logic.

6

Export and validate

Inspect the generated source, compile in TradingView, and match source, length and chart settings before comparing values.

Trading Strategies & Pro Tips

Threshold sensitivity study

Keep source and length fixed. Count confirmed crossings at several thresholds over the same dates; then evaluate a separately specified strategy with costs.

Pro Tip: A smaller event count does not demonstrate better returns.

Source comparison

Compare hlc3 and close with matching length and timeframe. Record differences without assuming either series is more responsive in every situation.

Divergence study

For regular bullish divergence, compare lower price lows with higher CCI lows. Define the pivot lookback and detection delay; the example does not implement these rules.

Common Mistakes to Avoid

  • Treating +100/-100 as guaranteed reversal levels.
  • Using each historical bar’s own moving mean inside the deviation average.
  • Confusing mean absolute deviation with standard deviation.
  • Calling two higher lows regular bullish divergence.
  • Reporting an undefined flat-window ratio as zero.
  • Optimizing and evaluating a rule on the same sample.

Frequently Asked Questions

Yes. TradingView’s implementation follows the classic Lambert formulation with the 0.015 divisor applied to mean deviation.

Match the source and lookback, then subtract the current window mean from every source observation in that window. Do not average deviations from different rolling means, and do not replace mean absolute deviation with standard deviation. Check missing history and rounding separately.

Twenty bars is the default here. No universally best length is established. Compare explicit candidates on the same sample, keep costs consistent for a strategy test, and retain untouched data for evaluation.

CCI can remain beyond a reference level while a trend continues. A threshold is not an automatic reversal or an instruction to trade.

CCI can be calculated from available crypto price data, but the same formula does not establish a profitable crypto rule. Match symbol, session and timeframe before comparisons.

The initial history may be insufficient, the selected source may be missing, or a flat window can produce zero mean deviation. Keep unavailable values distinct from zero. Smoothing or filtering changes the indicator and does not guarantee better signals.

Build a CCI rule with explicit inputs

  • Set source and length in the CCI module.
  • Inspect custom confirmation and alert logic.
  • Compare chart values before testing a trading strategy.

Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.