Choppiness Index (CHOP)
period = 14 | scalar = 100 | drift = 1 Overview
The Choppiness Index (CHOP), created by E.W. Dreiss, measures whether the market is trending or consolidating by comparing price movement efficiency to the available range. The indicator calculates the sum of Average True Range values over the lookback period and divides it by the difference between the highest high and lowest low. This ratio is then normalized using a base 10 logarithm and scaled to oscillate between 0 and 100. The formula essentially measures how much of the available range was actually traversed by price movements.
CHOP readings above 61.8 indicate choppy, directionless markets where prices move sideways without clear trend. Values below 38.2 suggest strong trending conditions with efficient price movement in one direction. The indicator remains neutral between 38.2 and 61.8, representing transitional states between trending and ranging behavior. These Fibonacci based thresholds help traders identify when to employ trend following strategies versus range trading approaches. Notably, CHOP doesn't indicate trend direction, only whether a trend exists.
Traders use CHOP to filter trading signals and select appropriate strategies for current market conditions. During high choppiness periods, mean reversion and range trading strategies typically perform better. When CHOP drops to low levels, trend following systems and breakout strategies become more effective. The indicator works across all timeframes and markets, making it valuable for adaptive trading systems that switch between trending and ranging modes. Many traders combine CHOP with directional indicators like ADX or moving averages to build complete market regime filters.
Implementation Examples
Get started with CHOP using slices or candles:
use vectorta::indicators::chop::{chop, ChopInput, ChopParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with OHLC slices
let high = vec![101.0, 103.0, 102.5, 104.0, 105.5];
let low = vec![ 99.5, 101.5, 100.8, 102.0, 104.0];
let close = vec![100.8, 102.0, 101.9, 103.2, 104.8];
let params = ChopParams { period: Some(14), scalar: Some(100.0), drift: Some(1) };
let input = ChopInput::from_slices(&high, &low, &close, params);
let result = chop(&input)?;
// Using Candles with defaults (period=14, scalar=100, drift=1)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = ChopInput::with_default_candles(&candles);
let result = chop(&input)?;
// Access values
for v in result.values { println!("CHOP: {}", v); } API Reference
Input Methods ▼
// From OHLC slices
ChopInput::from_slices(&[f64], &[f64], &[f64], ChopParams) -> ChopInput
// From candles
ChopInput::from_candles(&Candles, ChopParams) -> ChopInput
// Defaults from candles (period=14, scalar=100, drift=1)
ChopInput::with_default_candles(&Candles) -> ChopInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct ChopParams {
pub period: Option<usize>, // Default: 14
pub scalar: Option<f64>, // Default: 100.0
pub drift: Option<usize>, // Default: 1
} Output Structure ▼
pub struct ChopOutput {
pub values: Vec<f64>, // CHOP values in [0, 100] typically
} Validation, Warmup & NaNs ▼
- Inputs must be equal length; otherwise
UnderlyingFunctionFailed("mismatched input lengths"). periodmust be ≥ 1 and ≤ data length; elseInvalidPeriod { period, data_len }.driftmust be ≥ 1;drift=0yieldsUnderlyingFunctionFailed("Invalid drift=0 for ATR").- If all H/L/C values are
NaN, returnsAllValuesNaN. - If valid points after the first non‑NaN triple are fewer than
period, returnsNotEnoughValidData { needed, valid }. - Warmup: values are
NaNthrough indexfirst_valid + period − 1.
Error Handling ▼
use vectorta::indicators::chop::{chop, ChopInput, ChopParams, ChopError};
match chop(&input) {
Ok(output) => process(output.values),
Err(ChopError::EmptyData) => eprintln!("empty input"),
Err(ChopError::InvalidPeriod { period, data_len }) =>
eprintln!("invalid period {} for len {}", period, data_len),
Err(ChopError::AllValuesNaN) => eprintln!("all H/L/C values are NaN"),
Err(ChopError::NotEnoughValidData { needed, valid }) =>
eprintln!("need {} valid points; have {}", needed, valid),
Err(ChopError::UnderlyingFunctionFailed(msg)) =>
eprintln!("internal error: {}", msg),
} Python Bindings
Basic Usage ▼
Calculate CHOP from NumPy arrays (defaults: 14/100/1):
import numpy as np
from vectorta import chop
# Prepare OHLC as NumPy arrays
high = np.array([/* ... */], dtype=np.float64)
low = np.array([/* ... */], dtype=np.float64)
close = np.array([/* ... */], dtype=np.float64)
# Defaults
values = chop(high, low, close, period=14, scalar=100.0, drift=1)
# Custom params and optional kernel name: "auto", "scalar", "avx2", "avx512"
values = chop(high, low, close, period=20, scalar=100.0, drift=1, kernel="auto")
print(values) Streaming Real-time Updates ▼
Process OHLC bars incrementally:
from vectorta import ChopStream
stream = ChopStream(period=14, scalar=100.0, drift=1)
for h, l, c in ohlc_feed:
val = stream.update(h, l, c)
if val is not None:
print(f"CHOP: {val}") Batch Parameter Optimization ▼
Sweep ranges for period/scalar/drift:
import numpy as np
from vectorta import chop_batch
high = np.array([/* ... */], dtype=np.float64)
low = np.array([/* ... */], dtype=np.float64)
close = np.array([/* ... */], dtype=np.float64)
res = chop_batch(
high, low, close,
period_range=(10, 30, 5),
scalar_range=(100.0, 100.0, 0.0),
drift_range=(1, 3, 1),
kernel="auto",
)
# res["values"] is shape [rows, len]
print(res["periods"], res["scalars"], res["drifts"]) CUDA Acceleration ▼
CUDA support for CHOP is coming soon. APIs will mirror other CUDA-enabled indicators in this project.
# Coming soon: CUDA-accelerated CHOP parameter sweeps and portfolio modes JavaScript/WASM Bindings
Basic Usage ▼
Compute CHOP with three series (high/low/close):
import { chop_js } from 'vectorta-wasm';
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);
const values = chop_js(high, low, close, 14, 100.0, 1);
console.log('CHOP:', values); Memory-Efficient Operations ▼
Use zero-copy buffers for large datasets:
import { chop_alloc, chop_free, chop_into, memory } from 'vectorta-wasm';
const len = 10000;
const high = new Float64Array(len);
const low = new Float64Array(len);
const close = new Float64Array(len);
// ...fill arrays...
// Allocate WASM memory for inputs and output
const hp = chop_alloc(len);
const lp = chop_alloc(len);
const cp = chop_alloc(len);
const outp = chop_alloc(len);
// Copy inputs into WASM memory
new Float64Array(memory.buffer, hp, len).set(high);
new Float64Array(memory.buffer, lp, len).set(low);
new Float64Array(memory.buffer, cp, len).set(close);
// Compute directly into preallocated output buffer
chop_into(hp, lp, cp, outp, len, 14, 100.0, 1);
const values = new Float64Array(memory.buffer, outp, len).slice();
// Free buffers
chop_free(hp, len);
chop_free(lp, len);
chop_free(cp, len);
chop_free(outp, len); Batch Processing ▼
Test many parameter combinations at once:
import { chop_batch } from 'vectorta-wasm';
const cfg = {
period_range: [10, 20, 5],
scalar_range: [100.0, 100.0, 0.0],
drift_range: [1, 3, 1],
};
const out = chop_batch(high, low, close, cfg);
// out = { values: Float64Array, combos: Array<ChopParams>, rows: number, cols: number }
console.log(out.rows, out.cols, out.combos[0]); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05