Kaufman Adaptive Moving Average (KAMA)

Parameters: period = 30

Overview

The Kaufman Adaptive Moving Average intelligently adjusts its responsiveness based on market efficiency, becoming a fast moving average during clean trends and a slow moving average during choppy consolidations, solving the eternal trader's dilemma of choosing between responsiveness and stability. KAMA calculates an efficiency ratio by comparing net price change to the sum of all individual price movements, then maps this ratio to a smoothing constant that varies between fast and slow exponential moving average speeds. When markets trend efficiently with minimal retracements, the efficiency ratio approaches 1 and KAMA tracks price closely like a 2 period EMA, capturing profits from the earliest stages of breakouts. During sideways chop where prices oscillate without direction, efficiency drops toward 0 and KAMA behaves like a 30 period EMA, filtering out whipsaws that would trigger false signals with fixed period averages. Traders leverage KAMA as a universal trend filter that automatically adapts to market conditions without requiring manual parameter adjustments for different volatility regimes. The indicator particularly excels at staying with strong trends longer than traditional moving averages while quickly exiting when efficiency deteriorates, providing superior risk adjusted returns across diverse market environments from trending to ranging conditions.

Implementation Examples

Get started with KAMA in just a few lines:

use vectorta::indicators::kama::{kama, KamaInput, KamaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = KamaParams { period: Some(30) }; // Default period = 30
let input = KamaInput::from_slice(&prices, params);
let result = kama(&input)?;

// Using with Candles data structure (defaults: source="close", period=30)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = KamaInput::with_default_candles(&candles);
let result = kama(&input)?;

// Access the KAMA values
for value in result.values {
    println!("KAMA: {}", value);
}

API Reference

Input Methods
// From price slice
KamaInput::from_slice(&[f64], KamaParams) -> KamaInput

// From candles with custom source
KamaInput::from_candles(&Candles, &str, KamaParams) -> KamaInput

// From candles with default params (close, period=30)
KamaInput::with_default_candles(&Candles) -> KamaInput
Parameters Structure
pub struct KamaParams {
    pub period: Option<usize>, // Default: 30
}
Output Structure
pub struct KamaOutput {
    pub values: Vec<f64>, // KAMA values
}
Validation, Warmup & NaNs
  • Input must be non-empty; else KamaError::EmptyInputData.
  • All-NaN input is invalid; KamaError::AllValuesNaN.
  • period > 0 and period ≤ len; otherwise KamaError::InvalidPeriod.
  • There must be at least period+1 valid points after the first finite value; else KamaError::NotEnoughValidData.
  • Warmup: indices before first_valid + period are NaN; the first finite output equals the price at that index.
  • kama_into_slice requires output buffer length to match input; else KamaError::BufferLengthMismatch.
Error Handling
use vectorta::indicators::kama::{kama, KamaError};

match kama(&input) {
    Ok(output) => process(output.values),
    Err(KamaError::EmptyInputData) => eprintln!("input slice is empty"),
    Err(KamaError::AllValuesNaN) => eprintln!("all input values are NaN"),
    Err(KamaError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period: {} (len={})", period, data_len)
    }
    Err(KamaError::NotEnoughValidData { needed, valid }) => {
        eprintln!("not enough valid data: needed={}, valid={}", needed, valid)
    }
    Err(KamaError::BufferLengthMismatch { expected, got }) => {
        eprintln!("buffer mismatch: expected={}, got={}", expected, got)
    }
    Err(KamaError::InvalidKernel) => eprintln!("invalid kernel for batch context"),
}

Python Bindings

Basic Usage

Calculate KAMA using NumPy arrays (default period=30):

import numpy as np
from vectorta import kama

# Prepare price data as NumPy array
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Calculate with defaults (period=30)
result = kama(prices, period=30)

# Or specify kernel for performance optimization ("auto", "avx2", "avx512", "scalar")
result = kama(prices, period=30, kernel="auto")

print(f"KAMA values: {result}")
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import KamaStream

# Initialize streaming KAMA calculator
stream = KamaStream(period=30)

# Process real-time price updates
for price in price_feed:
    kama_value = stream.update(price)
    if kama_value is not None:
        print(f"Current KAMA: {kama_value}")
Batch Parameter Sweep

Test multiple KAMA periods efficiently:

import numpy as np
from vectorta import kama_batch

prices = np.array([...])

# Define period sweep as (start, end, step)
period_range = (10, 60, 5)

results = kama_batch(prices, period_range=period_range, kernel="auto")

print(results.keys())            # dict with 'values', 'periods'
print(results['values'].shape)   # (num_combinations, len(prices))
print(results['periods'])        # array of periods tested
CUDA Acceleration

CUDA support is available for KAMA in Python (device APIs).

import numpy as np
from vectorta import (
    kama_cuda_batch_dev,
    kama_cuda_many_series_one_param_dev,
)

# Option 1: One series, many parameters (parameter sweep on GPU)
prices_f32 = np.asarray(prices, dtype=np.float32)
res = kama_cuda_batch_dev(prices_f32, period_range=(10, 60, 5), device_id=0)
# res is a device-backed array wrapper; convert or use downstream APIs as provided

# Option 2: Many series, one parameter (time-major input: [T, N])
tm = np.random.rand(1000, 64).astype(np.float32)
res2 = kama_cuda_many_series_one_param_dev(tm, period=30, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Calculate KAMA in JavaScript/TypeScript:

import { kama_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// Calculate KAMA with specified period
const result = kama_js(prices, 30);

// Result is a Float64Array
console.log('KAMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { kama_alloc, kama_free, kama_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const length = prices.length;

// Allocate WASM memory for input and output
const inPtr = kama_alloc(length);
const outPtr = kama_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate KAMA directly into allocated memory
// Args: in_ptr, out_ptr, len, period
kama_into(inPtr, outPtr, length, 30);

// Read results from WASM memory (slice() to copy out)
const kamaValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
kama_free(inPtr, length);
kama_free(outPtr, length);

console.log('KAMA values:', kamaValues);
Batch Processing

Test multiple period values efficiently:

import { kama_batch_js, kama_batch_metadata_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

// Define period sweep
const start = 10, end = 60, step = 5; // 10, 15, 20, ... 60

// Get metadata (periods tested)
const metadata = kama_batch_metadata_js(start, end, step); // [period1, period2, ...]
const numCombos = metadata.length;

// Calculate all combinations
const results = kama_batch_js(prices, start, end, step);

// Results is a flat array of length numCombos * prices.length
const matrix = [] as number[][];
for (let i = 0; i < numCombos; i++) {
  const s = i * prices.length;
  const e = s + prices.length;
  matrix.push(results.slice(s, e));
}

// Access specific period results
const period = metadata[0];
const values = matrix[0];

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 8.76× faster than Tulip C in this benchmark.

Loading chart...

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

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