Commodity Channel Index (CCI)
period = 14 Overview
CCI measures the deviation of price from its statistical mean by calculating how many mean absolute deviations the current typical price sits away from its moving average. Donald Lambert developed this indicator to identify cyclical turns in commodities, though it works equally well across all markets. The calculation takes typical price (high plus low plus close divided by three), subtracts its simple moving average over the specified period, then divides by the mean absolute deviation multiplied by 0.015. This constant scaling factor ensures that approximately 70 to 80 percent of CCI values fall between -100 and +100 under normal market conditions. Readings above +100 indicate price sits significantly above its average, signaling potential overbought conditions or strong uptrend momentum depending on context. Values below -100 suggest oversold conditions or powerful downtrends. Traders use CCI to spot divergences when price makes new highs while CCI fails to exceed previous peaks, warning of weakening momentum, and to identify trend emergence when CCI moves from below -100 to above +100 rapidly.
Implementation Examples
Get started with CCI from slices or candles:
use vectorta::indicators::cci::{cci, CciInput, CciParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with price slice (typical price series)
let tp = vec![101.0, 102.5, 101.8, 103.2, 104.7, 103.9];
let input = CciInput::from_slice(&tp, CciParams { period: Some(14) });
let result = cci(&input)?;
// Using with Candles (defaults: period=14, source="hlc3")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = CciInput::with_default_candles(&candles);
let result = cci(&input)?;
// Access the CCI values
for v in result.values { println!("CCI: {}", v); } API Reference
Input Methods ▼
// From price slice (typical price series)
CciInput::from_slice(&[f64], CciParams) -> CciInput
// From candles with custom source (e.g., "close", "hlc3")
CciInput::from_candles(&Candles, &str, CciParams) -> CciInput
// From candles with default params (source="hlc3", period=14)
CciInput::with_default_candles(&Candles) -> CciInput Parameters Structure ▼
pub struct CciParams {
pub period: Option<usize>, // Default: 14
} Output Structure ▼
pub struct CciOutput {
pub values: Vec<f64>, // CCI values
} Validation, Warmup & NaNs ▼
period > 0; otherwiseCciError::InvalidPeriod.- Input cannot be empty; otherwise
CciError::EmptyInputData. - If all inputs are
NaN, returnsCciError::AllValuesNaN. - There must be at least
periodvalid points after the first finite value; otherwiseCciError::NotEnoughValidData. - Warmup: indices before
first_valid + period − 1areNaN. - If MAD is zero for a window, the corresponding output is
0.0(avoid division by zero). - Streaming:
CciStream::updatereturnsNoneuntil the buffer is filled.
Error Handling ▼
use vectorta::indicators::cci::{cci, CciError, CciInput, CciParams};
let params = CciParams { period: Some(0) };
let input = CciInput::from_slice(&[], params);
match cci(&input) {
Ok(output) => process(output.values),
Err(CciError::EmptyInputData) => eprintln!("input data is empty"),
Err(CciError::AllValuesNaN) => eprintln!("all values are NaN"),
Err(CciError::InvalidPeriod { period, data_len }) =>
eprintln!("invalid period: period = {}, data_len = {}", period, data_len),
Err(CciError::NotEnoughValidData { needed, valid }) =>
eprintln!("not enough valid data: needed = {}, valid = {}", needed, valid),
} Python Bindings
Basic Usage ▼
Operate on NumPy arrays of typical prices:
import numpy as np
from vectorta import cci
typical = np.asarray([101.4, 102.1, 100.8, 103.6, 104.2], dtype=np.float64)
values = cci(typical, period=14)
# Optional: choose a specific kernel ("auto", "scalar")
values_scalar = cci(typical, period=14, kernel="scalar") Streaming Updates ▼
Maintain a rolling CCI value:
from vectorta import CciStream
stream = CciStream(period=14)
for candle in ohlc_generator():
tp = (candle.high + candle.low + candle.close) / 3.0
val = stream.update(tp)
if val is not None:
handle_stream_value(candle.timestamp, val) Batch Processing ▼
Sweep period ranges in one call:
import numpy as np
from vectorta import cci_batch
tp = np.asarray([101.4, 102.1, 100.8, 103.6, 104.2], dtype=np.float64)
result = cci_batch(tp, period_range=(10, 40, 5), kernel="auto")
values = result['values'] # shape: (num_combinations, len(tp))
periods = result['periods'] # list of periods used (usize)
print(values.shape, periods) CUDA Acceleration ▼
CUDA support for CCI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated CCI calculations
# (API will mirror other CUDA-enabled indicators in this project) JavaScript/WASM Bindings
Basic Usage ▼
Calculate CCI in JavaScript/TypeScript:
import { cci_js } from 'vectorta-wasm';
const tp = new Float64Array([101.4, 102.1, 100.8, 103.6, 104.2]);
const values = cci_js(tp, 14);
console.log('CCI values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { cci_alloc, cci_free, cci_into, memory } from 'vectorta-wasm';
const tp = new Float64Array([/* your data */]);
const len = tp.length;
const inPtr = cci_alloc(len);
const outPtr = cci_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(tp);
cci_into(inPtr, outPtr, len, 14);
const cciValues = new Float64Array(memory.buffer, outPtr, len).slice();
cci_free(inPtr, len);
cci_free(outPtr, len); Batch Processing ▼
Test multiple period choices:
import { cci_batch_js, cci_batch_metadata_js, cci_batch as cci_batch_unified } from 'vectorta-wasm';
const tp = new Float64Array([/* historical typical prices */]);
// Metadata: [period1, period2, ...]
const metadata = cci_batch_metadata_js(10, 40, 5);
// Flat results: concatenate rows
const flat = cci_batch_js(tp, 10, 40, 5);
// Unified API: structured result
const unified = cci_batch_unified(tp, { period_range: [10, 40, 5] });
console.log(unified.rows, unified.cols, unified.combos.length); Performance Analysis
Across sizes, Rust CPU runs about 1.02× slower than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
CUDA note
In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.