Commodity Channel Index (CCI)

Parameters: period = 14 (2–200)

Overview

The Commodity Channel Index (CCI) normalizes the distance between the current typical price (high + low + close / 3) and its moving average by the mean absolute deviation. This produces an oscillator where ±100 traditionally marks statistically unusual conditions, empowering traders to isolate overbought, oversold, and emerging divergence setups.

VectorTA's implementation embraces that statistical foundation with SIMD-aware kernels, candle integration, and streaming support. You can sweep parameter regimes, feed real-time typical prices, or plug into Python and WASM bindings while relying on consistent output semantics across every interface.

Implementation Examples

Compute CCI from candles or typical-price slices with minimal setup:

use vectorta::indicators::cci::{cci, CciInput, CciParams};
use vectorta::utilities::data_loader::Candles;

// Use candles with hlc3 typical price and default period = 14
let candles: Candles = load_workspace_candles()?;
let candle_input = CciInput::with_default_candles(&candles);
let candle_output = cci(&candle_input)?;

for (idx, value) in candle_output.values.iter().enumerate() {
    println!("CCI[{idx}] = {value:.2}");
}

// Or feed pre-computed typical price values directly
fn compute_typical_price(candles: &Candles) -> Vec<f64> {
    candles
        .high
        .iter()
        .zip(&candles.low)
        .zip(&candles.close)
        .map(|((h, l), c)| (h + l + c) / 3.0)
        .collect()
}

let typical_price = compute_typical_price(&candles);
let params = CciParams { period: Some(20) };
let slice_input = CciInput::from_slice(&typical_price, params);
let slice_output = cci(&slice_input)?;

API Reference

Input Methods
// From candles using hlc3 typical price
CciInput::from_candles(&Candles, &str, CciParams) -> CciInput

// From pre-computed typical price slice
CciInput::from_slice(&[f64], CciParams) -> CciInput

// Shortcut: candles + default params (period = 14, hlc3 source)
CciInput::with_default_candles(&Candles) -> CciInput
Parameter Structure
#[derive(Default, Clone)]
pub struct CciParams {
    pub period: Option<usize>, // defaults to Some(14)
}
Output Structure
pub struct CciOutput {
    pub values: Vec<f64>, // CCI per bar aligned with the source data
}
Builder Utilities
use vectorta::indicators::cci::CciBuilder;
use vectorta::utilities::enums::Kernel;

let builder = CciBuilder::new()
    .period(25)
    .kernel(Kernel::Auto);

let candle_values = builder.clone().apply(&candles)?;
let slice_values = builder.clone().apply_slice(&typical_price)?;
let mut stream = builder.into_stream()?;
Streaming API
use vectorta::indicators::cci::{CciParams, CciStream};

let mut stream = CciStream::try_new(CciParams::default())?;
while let Some(typical) = next_typical_price() {
    if let Some(value) = stream.update(typical) {
        push_indicator_value(value);
    }
}
Batch Operations
use vectorta::indicators::cci::{CciBatchBuilder, CciParams};

let batch = CciBatchBuilder::new()
    .period_range(5, 45, 5)
    .apply_slice(&typical_price)?;

if let Some(row) = batch.values_for(&CciParams { period: Some(30) }) {
    score_strategy(row);
}
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) => consume(output),
    Err(CciError::EmptyInputData) => log::error!("no data provided"),
    Err(CciError::InvalidPeriod { period, data_len }) =>
        log::warn!("period {period} exceeds data length {data_len}"),
    Err(CciError::AllValuesNaN) => log::error!("all values were NaN"),
    Err(CciError::NotEnoughValidData { needed, valid }) =>
        log::warn!("need {needed} clean rows, only {valid} available"),
}

Python Bindings

Basic Usage

Operate on NumPy arrays of typical price values and optionally pin the kernel:

import numpy as np
from vectorta import cci

typical_price = np.asarray([101.4, 102.1, 100.8, 103.6, 104.2], dtype=np.float64)

values = cci(typical_price, period=20)
print("CCI", values)

# Force scalar execution when validating results against references
values_scalar = cci(typical_price, period=20, kernel="scalar")
Streaming Updates

Maintain a rolling CCI value by feeding live typical prices:

from vectorta import CciStream

stream = CciStream(period=20)

for candle in ohlc_generator():
    typical = (candle.high + candle.low + candle.close) / 3.0
    value = stream.update(typical)
    if value is not None:
        handle_stream_value(candle.timestamp, value)
Batch Sweeps

Sweep period ranges and analyze the resulting grid inside Python:

from vectorta import cci_batch

result = cci_batch(
    typical_price,
    period_range=(10, 40, 5),
    kernel="auto",
)

values = result["values"]      # reshaped to [rows, len(typical_price)]
periods = result["periods"]    # vector of period lengths
CUDA Acceleration

CUDA-backed Python bindings for CCI are planned. The API will mirror other CUDA-enabled indicators once released.

# Coming soon: GPU-accelerated CCI sweeps
# from vectorta import cci_cuda_batch
#
# gpu_result = cci_cuda_batch(
#     typical_price_matrix,
#     period_range=(10, 40, 1),
#     device_id=0,
# )
Return Structure
# cci(...)
np.ndarray  # shape: (len(typical_price),)

# cci_batch(...)
{
    "values": np.ndarray,   # shape: (rows, len(typical_price))
    "periods": np.ndarray,  # period length per row
}

JavaScript / WASM Bindings

Quick Usage

Run CCI inside the browser or Node using Float64Array inputs:

import { cci_js } from 'vectorta-wasm';

const typical = new Float64Array([101.4, 102.1, 100.8, 103.6, 104.2]);
const values = cci_js(typical, 20);

console.log('CCI values', Array.from(values));
Memory-Efficient Operations

Reuse WASM memory to avoid repeated allocations during tight loops:

import { cci_alloc, cci_free, cci_into, memory } from 'vectorta-wasm';

const len = typical.length;
const inPtr = cci_alloc(len);
const outPtr = cci_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(typical);

cci_into(inPtr, outPtr, len, 20);

const cciValues = new Float64Array(memory.buffer, outPtr, len).slice();

cci_free(inPtr, len);
cci_free(outPtr, len);
Batch & Metadata

Sweep period ranges and inspect configuration metadata from JavaScript:

import { cci_batch_js, cci_batch_metadata_js, cci_batch as cci_batch_unified } from 'vectorta-wasm';

const typical = new Float64Array(loadTypicalPrices());

const metadata = cci_batch_metadata_js(10, 40, 5); // returns [10, 15, ...]
const flattened = cci_batch_js(typical, 10, 40, 5);

const block = typical.length;
const firstRow = flattened.slice(0, block);

const unified = cci_batch_unified(typical, {
  period_range: [10, 40, 5],
});

console.log(unified.rows, unified.cols, unified.combos.length);

Performance Analysis

Related Indicators