Chande Kroll Stop (CKSP)

Parameters: p = 10 | x = 1 (0.5–5) | q = 9

Overview

The Chande Kroll Stop (CKSP), developed by Tushar Chande and Stanley Kroll, provides volatility adjusted stop levels that help traders stay with trends while protecting against adverse moves. The indicator calculates two distinct stop lines using a combination of Average True Range (ATR) and rolling price extremes. For long positions, CKSP finds the highest high over the initial ATR period, subtracts the ATR multiplied by your chosen factor, then takes the highest value of this calculation over the smoothing period. Short stops work inversely, using the lowest low plus ATR and then finding the minimum over the smoothing window.

The dual smoothing approach makes CKSP more stable than single period stops while maintaining responsiveness to significant price moves. The first ATR period determines volatility measurement sensitivity, while the secondary smoothing period controls how quickly stops adjust to new market conditions. This two stage process filters out minor fluctuations that might trigger premature exits while allowing the stops to tighten during sustained trends. Typical settings use a 10 period ATR with a 9 period smoothing window, though traders often adjust these based on their holding periods.

Traders primarily use CKSP for position management and trend confirmation. When price remains above the long stop line, the uptrend remains intact. Dropping below signals potential trend weakness or reversal. The distance between the two stop lines also provides valuable information about market volatility and trend strength. Wider spreads indicate higher volatility or stronger trends, while narrowing stops suggest consolidation or trend exhaustion. Many systematic traders incorporate CKSP into their exit strategies, using the appropriate stop line based on their position direction to time exits objectively.

Implementation Examples

Compute CKSP long/short stops from OHLC inputs:

use vectorta::indicators::cksp::{cksp, CkspInput, CkspParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with OHLC slices
let high = vec![101.0, 102.5, 103.0, 102.0, 104.0];
let low = vec![ 99.0, 100.5, 100.8, 100.0, 101.5];
let close = vec![100.5, 101.7, 102.2, 101.0, 103.0];
let params = CkspParams { p: Some(10), x: Some(1.0), q: Some(9) };
let input = CkspInput::from_slices(&high, &low, &close, params);
let out = cksp(&input)?;

// Using with Candles (defaults: p=10, x=1.0, q=9; source=high/low/close)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = CkspInput::with_default_candles(&candles);
let out = cksp(&input)?;

// Access the stop lines
for (ls, ss) in out.long_values.iter().zip(out.short_values.iter()) {
    println!("long_stop={} short_stop={}", ls, ss);
}

API Reference

Input Methods
// From slices (high, low, close)
CkspInput::from_slices(&[f64], &[f64], &[f64], CkspParams) -> CkspInput

// From candles (fields: high/low/close)
CkspInput::from_candles(&Candles, CkspParams) -> CkspInput

// From candles with defaults (p=10, x=1.0, q=9)
CkspInput::with_default_candles(&Candles) -> CkspInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct CkspParams {
    pub p: Option<usize>, // Default: 10
    pub x: Option<f64>,   // Default: 1.0
    pub q: Option<usize>, // Default: 9
}
Output Structure
#[derive(Debug, Clone)]
pub struct CkspOutput {
    pub long_values: Vec<f64>,  // Long stop line
    pub short_values: Vec<f64>, // Short stop line
}
Validation, Warmup & NaNs
  • p > 0, q > 0; x must be finite (non-NaN).
  • Input lengths must match; otherwise CkspError::InconsistentLengths.
  • Returns CkspError::NoData if empty or all values are NaN.
  • CkspError::NotEnoughData { p, q, data_len } if p or q exceed available length.
  • When using Candles, field selection errors map to CkspError::CandleFieldError.
  • Warmup indices are NaN; first valid index is at first_valid + p + q - 1.
  • Streaming: update returns None until warmup completes; then yields finite stops.
Error Handling
use vectorta::indicators::cksp::{cksp, CkspError};

match cksp(&input) {
    Ok(output) => process(output.long_values, output.short_values),
    Err(CkspError::NoData) => eprintln!("cksp: no data or all NaNs"),
    Err(CkspError::NotEnoughData { p, q, data_len }) =>
        eprintln!("cksp: not enough data for p={} q={} (len={})", p, q, data_len),
    Err(CkspError::InconsistentLengths) => eprintln!("cksp: input length mismatch"),
    Err(CkspError::InvalidParam { param }) => eprintln!("cksp: invalid param: {}", param),
    Err(CkspError::CandleFieldError(msg)) => eprintln!("cksp: candle field: {}", msg),
}

Python Bindings

Basic Usage

Calculate CKSP using NumPy arrays (defaults: p=10, x=1.0, q=9):

import numpy as np
from vectorta import cksp

# Prepare OHLC data as NumPy arrays
high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
close = np.array([...], dtype=np.float64)

# Calculate CKSP with defaults
long, short = cksp(high, low, close)

# Or specify custom parameters and kernel
long, short = cksp(high, low, close, p=10, x=1.0, q=9, kernel="auto")

print(long.shape, short.shape)
Streaming Real-time Updates

Process OHLC updates efficiently:

import numpy as np
from vectorta import CkspStream

stream = CkspStream(p=10, x=1.0, q=9)
for (h, l, c) in ohlc_feed:
    val = stream.update(h, l, c)
    if val is not None:
        long_stop, short_stop = val
        do_something(long_stop, short_stop)
Batch Parameter Optimization

Test multiple combinations for p, x, q:

import numpy as np
from vectorta import cksp_batch

high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
close = np.array([...], dtype=np.float64)

results = cksp_batch(
    high, low, close,
    p_range=(10, 20, 5),
    x_range=(0.5, 2.0, 0.5),
    q_range=(9, 15, 3),
    kernel="auto"
)

long_vals = results['long_values']  # shape: (num_combos, len)
short_vals = results['short_values']
ps = results['p']
xs = results['x']
qs = results['q']
CUDA Acceleration

CUDA support for CKSP is coming soon. The API will follow other CUDA-enabled indicators.

JavaScript/WASM Bindings

Basic Usage

Compute CKSP from OHLC arrays:

import { cksp_js } from 'vectorta-wasm';

const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);

// Returns a flat array: [long..., short...]
const values = cksp_js(high, low, close, 10, 1.0, 9);
const n = close.length;
const longStops = values.slice(0, n);
const shortStops = values.slice(n);
Memory-Efficient Operations

Use zero-copy into pre-allocated WASM memory:

import { cksp_alloc, cksp_free, cksp_into, memory } from 'vectorta-wasm';

const n = close.length;
const longPtr = cksp_alloc(n);
const shortPtr = cksp_alloc(n);

// Compute directly into WASM memory
cksp_into(high, low, close, longPtr, shortPtr, n, 10, 1.0, 9);

// Read out results
const longStops = new Float64Array(memory.buffer, longPtr, n).slice();
const shortStops = new Float64Array(memory.buffer, shortPtr, n).slice();

// Free when done
cksp_free(longPtr, n);
cksp_free(shortPtr, n);
Batch Processing

Test multiple parameter combinations:

import { cksp_batch } from 'vectorta-wasm';

const config = { p_range: [10, 20, 5], x_range: [0.5, 2.0, 0.5], q_range: [9, 15, 3] };
const out = cksp_batch(high, low, close, config);

// out: { long_values, short_values, combos, rows, cols }
const { long_values, short_values, combos, rows, cols } = out;

// Access row i (flattened by rows)
const row = 0;
const longRow = long_values.slice(row * cols, (row + 1) * cols);
const shortRow = short_values.slice(row * cols, (row + 1) * cols);
const params = combos[row];

Performance Analysis

Comparison:
View:
Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators