CCI Cycle
length = 10 | factor = 0.5 (0.1–1) Overview
CCI Cycle transforms the Commodity Channel Index into a smoothed cyclical oscillator by applying multiple stages of filtering and normalization to isolate market rhythms from noise. The indicator begins with standard CCI calculation, then processes it through double exponential moving averages followed by smoothed moving averages to remove erratic fluctuations. Next, it applies two layers of stochastic normalization that compress the output between 0 and 100, with the factor parameter controlling the final smoothing intensity. Values above 70 indicate cycle peaks where momentum exhausts and reversals become probable, while readings below 30 mark cycle troughs presenting potential bounce opportunities. The double stochastic transformation makes CCI Cycle particularly effective at identifying oversold and overbought conditions within the context of the dominant cycle rather than absolute price levels. Traders use this indicator to time entries at cycle lows and exits at cycle highs, especially in ranging markets where traditional momentum oscillators produce excessive whipsaws.
Implementation Examples
Get started with CCI Cycle in a few lines:
use vector_ta::indicators::cci_cycle::{cci_cycle, CciCycleInput, CciCycleParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
// From a price slice
let prices: Vec<f64> = vec![/* your data (needs >= 2*length finite points; stable after ~4*length) */];
let params = CciCycleParams { length: Some(10), factor: Some(0.5) };
let input = CciCycleInput::from_slice(&prices, params);
let out = cci_cycle(&input)?;
// From Candles using defaults (length=10, factor=0.5, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = CciCycleInput::with_default_candles(&candles);
let out = cci_cycle(&input)?;
// Access values
for v in out.values { println!("CCI Cycle: {}", v); } API Reference
Input Methods ▼
// From price slice
CciCycleInput::from_slice(&[f64], CciCycleParams) -> CciCycleInput
// From candles with custom source
CciCycleInput::from_candles(&Candles, &str, CciCycleParams) -> CciCycleInput
// Candles with VectorTA defaults (length=10, factor=0.5, source="close")
CciCycleInput::with_default_candles(&Candles) -> CciCycleInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct CciCycleParams {
pub length: Option<usize>, // Default: 10
pub factor: Option<f64>, // Default: 0.5
} Output Structure ▼
pub struct CciCycleOutput {
pub values: Vec<f64>, // Normalized 0–100 oscillator values
} Validation, Warmup & NaNs ▼
length > 0andlength ≤ data_len; otherwiseCciCycleError::InvalidPeriod.- Requires at least
length * 2valid points after the first finite value; otherwiseCciCycleError::NotEnoughValidData. - Empty input ⇒
CciCycleError::EmptyInputData. AllNaN⇒CciCycleError::AllValuesNaN. - Warmup: conservative prefix of about
first_valid + 4 × lengthis set toNaN. - Kernel selection:
Kernel::Autochooses a best-fit kernel; explicit kernels are supported viacci_cycle_with_kernel/cci_cycle_into_slice.
Error Handling ▼
use vector_ta::indicators::cci_cycle::{cci_cycle, CciCycleError, CciCycleInput, CciCycleParams};
let input = CciCycleInput::from_slice(&[], CciCycleParams::default());
match cci_cycle(&input) {
Ok(out) => consume(out.values),
Err(CciCycleError::EmptyInputData) => log::error!("no data provided"),
Err(CciCycleError::AllValuesNaN) => log::error!("all values were NaN"),
Err(CciCycleError::InvalidPeriod { period, data_len }) =>
log::warn!("length {period} exceeds data length {data_len}"),
Err(CciCycleError::NotEnoughValidData { needed, valid }) =>
log::warn!("need {needed} valid points, only {valid} available"),
Err(CciCycleError::CciError(e)) => log::error!("cci failed: {e}"),
Err(CciCycleError::EmaError(e)) => log::error!("ema failed: {e}"),
Err(CciCycleError::SmmaError(e)) => log::error!("smma failed: {e}"),
} Python Bindings
Basic Usage ▼
import numpy as np
from vector_ta import cci_cycle
prices = np.asarray(load_prices(), dtype=np.float64) # needs >= 2*length points; stable after ~4*length
values = cci_cycle(prices, length=10, factor=0.5, kernel="auto")
print(values)
# Force specific kernel during validation
values_scalar = cci_cycle(prices, length=10, factor=0.5, kernel="scalar") Streaming ▼
Use CciCycleStream for incremental updates. It returns None until it has enough history, and may return NaN during warmup.
import math
from vector_ta import CciCycleStream
stream = CciCycleStream(length=10, factor=0.5)
for px in feed_prices():
value = stream.update(float(px))
if value is not None and math.isfinite(value):
print(value)
Batch Processing ▼
import numpy as np
from vector_ta import cci_cycle_batch
prices = np.asarray(load_prices(), dtype=np.float64)
result = cci_cycle_batch(
prices,
length_range=(10, 20, 5),
factor_range=(0.3, 0.7, 0.2),
kernel="auto",
)
print(result["values"].shape) # (rows, len(prices))
print(result["lengths"]) # array of length per row
print(result["factors"]) # array of factor per row CUDA Acceleration ▼
CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible). Rust CUDA wrappers are documented below.
import numpy as np
from vector_ta import cci_cycle_cuda_batch_dev, cci_cycle_cuda_many_series_one_param_dev
prices_f32 = np.asarray(load_prices(), dtype=np.float32)
dev = cci_cycle_cuda_batch_dev(
prices_f32,
length_range=(10, 20, 5),
factor_range=(0.3, 0.7, 0.2),
device_id=0,
)
tm_f32 = np.asarray(load_time_major_matrix(), dtype=np.float32) # shape: (rows, cols)
dev_tm = cci_cycle_cuda_many_series_one_param_dev(
tm_f32.ravel(),
cols=tm_f32.shape[1],
rows=tm_f32.shape[0],
length=10,
factor=0.5,
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
import { cci_cycle_js } from 'vectorta-wasm';
const prices = new Float64Array(loadPrices()); // needs >= 2*length points; stable after ~4*length
const values = cci_cycle_js(prices, 10, 0.5);
console.log('CCI Cycle:', Array.from(values)); Memory-Efficient Operations ▼
import { cci_cycle_alloc, cci_cycle_free, cci_cycle_into, memory } from 'vectorta-wasm';
const data = new Float64Array(loadPrices());
const len = data.length;
const inPtr = cci_cycle_alloc(len);
const outPtr = cci_cycle_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(data);
// in_ptr, out_ptr, len, length, factor
cci_cycle_into(inPtr, outPtr, len, 10, 0.5);
const out = new Float64Array(memory.buffer, outPtr, len).slice();
cci_cycle_free(inPtr, len);
cci_cycle_free(outPtr, len); Batch Processing ▼
import { cci_cycle_batch } from 'vectorta-wasm';
const prices = new Float64Array(loadPrices());
const result = cci_cycle_batch(prices, {
length_range: [10, 20, 5],
factor_range: [0.3, 0.7, 0.2],
});
console.log(result.rows, result.cols, result.combos.length); CUDA Bindings (Rust)
use vector_ta::cuda::CudaCciCycle;
use vector_ta::indicators::cci_cycle::CciCycleBatchRange;
let cuda = CudaCciCycle::new(0)?;
let data_f32: [f32] = /* ... */;
let sweep = CciCycleBatchRange::default();
let out = cuda.cci_cycle_batch_dev(&data_f32, &sweep)?;
let _ = out; Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28
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.
Related Indicators
Acceleration Oscillator
Technical analysis indicator
Awesome Oscillator
Technical analysis indicator
Absolute Price Oscillator
Technical analysis indicator
Commodity Channel Index
Technical analysis indicator
Center of Gravity
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator