Kaufman Adaptive Stop

Parameters: period = 22 | mult = 2 (0.5–4) | direction = long | ma_type = kama

Overview

The Kaufman Adaptive Stop creates intelligent trailing stop levels that expand and contract based on market volatility, protecting profits during calm trends while allowing positions room to breathe during volatile expansions. By calculating a moving average of the high low range and multiplying it by a factor, the indicator positions stops at a distance that adapts to current market conditions rather than using fixed percentages that work poorly across different volatility regimes. During low volatility trends, the stop tightens to protect gains as the averaged range contracts, following price closely to lock in profits from steady advances. When volatility expands during news events or market stress, the stop automatically widens to prevent premature exits from positions that need more room to fluctuate without violating the underlying trend. The direction parameter allows traders to flip between long and short modes, with long stops trailing below price to protect bullish positions and short stops above price for bearish trades. Professional traders value this adaptive approach because it solves the common problem of stops being too tight in volatile markets causing whipsaws, or too wide in calm markets leaving excessive profits at risk, by dynamically adjusting to the market's current personality rather than imposing rigid rules.

Implementation Examples

Compute Kaufman Stop from high/low data:

use vectorta::indicators::kaufmanstop::{kaufmanstop, KaufmanstopInput, KaufmanstopParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with raw high/low slices
let high = vec![10.0, 11.0, 10.6, 11.5, 11.1];
let low  = vec![ 9.5, 10.2, 10.0, 11.0, 10.7];
let params = KaufmanstopParams { period: Some(22), mult: Some(2.0), direction: Some("long".into()), ma_type: Some("sma".into()) };
let input = KaufmanstopInput::from_slices(&high, &low, params);
let output = kaufmanstop(&input)?;

// Using with Candles
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = KaufmanstopInput::with_default_candles(&candles); // period=22, mult=2.0, long, sma
let output = kaufmanstop(&input)?;

// Access values
for v in output.values { println!("stop: {}", v); }

API Reference

Input Methods
// From high/low slices
KaufmanstopInput::from_slices(&[f64], &[f64], KaufmanstopParams) -> KaufmanstopInput

// From candles (uses 'high' and 'low')
KaufmanstopInput::from_candles(&Candles, KaufmanstopParams) -> KaufmanstopInput

// With default params (period=22, mult=2.0, direction="long", ma_type="kama" in this UI)
KaufmanstopInput::with_default_candles(&Candles) -> KaufmanstopInput
Parameters Structure
pub struct KaufmanstopParams {
    pub period: Option<usize>,   // Default: 22
    pub mult: Option<f64>,       // Default: 2.0
    pub direction: Option<String>, // Default: "long" ("long" or "short")
    pub ma_type: Option<String>,   // Default: "sma" ("sma", "ema", ...)
}
Output Structure
pub struct KaufmanstopOutput {
    pub values: Vec<f64>, // Stop values (below for long, above for short)
}
Validation, Warmup & NaNs
  • period > 0 and not greater than the lengths of high and low; otherwise KaufmanstopError::InvalidPeriod.
  • Requires at least period valid (finite) (high, low) pairs after the first finite pair; otherwise KaufmanstopError::NotEnoughValidData.
  • Indices before first_valid_idx + period − 1 are prefilled with NaN.
  • MA types "sma" and "ema" use optimized implementations; other strings are dispatched via the MA selector.
  • Streaming: update(high, low) yields None until period updates have been buffered; then returns per-tick stops.
Error Handling
use vectorta::indicators::kaufmanstop::{kaufmanstop, KaufmanstopError};

match kaufmanstop(&input) {
    Ok(output) => process(output.values),
    Err(KaufmanstopError::EmptyData) =>
        eprintln!("Empty high/low input"),
    Err(KaufmanstopError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for length {}", period, data_len),
    Err(KaufmanstopError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid pairs, have {}", needed, valid),
    Err(KaufmanstopError::AllValuesNaN) =>
        eprintln!("All input values are NaN"),
}

Python Bindings

Basic Usage

Compute Kaufman Stop from NumPy arrays of high and low:

import numpy as np
from vectorta import kaufmanstop, KaufmanstopStream, kaufmanstop_batch

high = np.array([10.0, 11.0, 10.6, 11.5, 11.1], dtype=float)
low  = np.array([ 9.5, 10.2, 10.0, 11.0, 10.7], dtype=float)
stops = kaufmanstop(high, low, period=22, mult=2.0, direction="long", ma_type="kama")

# Streaming
stream = KaufmanstopStream(period=22, mult=2.0, direction="long", ma_type="kama")
for h, l in zip(high, low):
    value = stream.update(h, l)
    if value is not None:
        print(value)

# Batch sweep (returns dict with values and metadata)
res = kaufmanstop_batch(high, low, period_range=(10, 30, 5), mult_range=(1.0, 3.0, 0.5), direction="long", ma_type="kama")
print(res['values'].shape, res['rows'], res['cols'])
CUDA Acceleration

CUDA support for Kaufman Stop is coming soon.

# Coming soon: CUDA-accelerated Kaufman Stop calculations
# API will mirror other CUDA-enabled indicators in this project.

JavaScript/WASM Bindings

Basic Usage

Calculate Kaufman Stop in JavaScript/TypeScript:

import { kaufmanstop_js } from 'vectorta-wasm';

const high = new Float64Array([10.0, 11.0, 10.6, 11.5, 11.1]);
const low  = new Float64Array([ 9.5, 10.2, 10.0, 11.0, 10.7]);

// Args: high, low, period, mult, direction, ma_type
const stops = kaufmanstop_js(high, low, 22, 2.0, 'long', 'sma');
console.log('stops:', stops);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { kaufmanstop_alloc, kaufmanstop_into, kaufmanstop_free, memory } from 'vectorta-wasm';

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

// Allocate WASM buffers
const highPtr = kaufmanstop_alloc(n);
const lowPtr  = kaufmanstop_alloc(n);
const outPtr  = kaufmanstop_alloc(n);

// Copy inputs into WASM memory
new Float64Array(memory.buffer, highPtr, n).set(high);
new Float64Array(memory.buffer, lowPtr, n).set(low);

// Compute into preallocated output
kaufmanstop_into(highPtr, lowPtr, outPtr, n, 22, 2.0, 'long', 'sma');

// Read back results (copy out)
const stops = new Float64Array(memory.buffer, outPtr, n).slice();

// Free buffers
kaufmanstop_free(highPtr, n);
kaufmanstop_free(lowPtr, n);
kaufmanstop_free(outPtr, n);
Batch Processing

Test multiple parameter combinations:

import { kaufmanstop_batch_js } from 'vectorta-wasm';

const high = new Float64Array([/* historical highs */]);
const low  = new Float64Array([/* historical lows */]);

// Returns object: { values: Float64Array, combos: Array, rows: number, cols: number }
const result = kaufmanstop_batch_js(
  high, low,
  10, 30, 10,   // period_start, period_end, step
  1.0, 3.0, 0.5,// mult_start, mult_end, step
  'long', 'sma'
);

// Reshape values into row-major matrix
const rows = result.rows, cols = result.cols;
const matrix: number[][] = [];
for (let r = 0; r < rows; r++) {
  const start = r * cols;
  matrix.push(Array.from(result.values.slice(start, start + cols)));
}
console.log('combos:', result.combos);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators