Kaufman Efficiency Ratio (ER)

Parameters: period = 5

Overview

The Kaufman Efficiency Ratio measures market noise by comparing the straight line distance between two price points against the actual path traveled, producing values from 0 (pure noise) to 1 (perfect trend). The calculation divides net price change over a period by the sum of all absolute price changes, revealing whether movement occurs smoothly or erratically. When ER approaches 1, price advances directly with minimal retracements, signaling strong trending conditions ideal for trend following strategies. Conversely, ER values near 0 indicate choppy, directionless markets where prices oscillate heavily, suggesting range trading or sitting aside. Traders particularly value ER for adaptive systems since it quantifies the signal to noise ratio in price action, allowing dynamic adjustment of indicators, position sizing, and stop losses based on market efficiency. The ratio excels at filtering whipsaw trades by distinguishing genuine trends from noisy sideways action that produces false breakouts.

Implementation Examples

Compute ER from a slice or candles:

use vectorta::indicators::er::{er, ErInput, ErParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice
let prices = vec![100.0, 102.0, 101.0, 104.0, 106.0, 107.0];
let params = ErParams { period: Some(5) }; // Default = 5
let input = ErInput::from_slice(&prices, params);
let result = er(&input)?;

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

// Access the ER values (0.0..=1.0)
for value in result.values {
    println!("ER: {}", value);
}

API Reference

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

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

// From candles with defaults (close, period=5)
ErInput::with_default_candles(&Candles) -> ErInput
Parameters Structure
pub struct ErParams {
    pub period: Option<usize>, // Default: 5
}
Output Structure
pub struct ErOutput {
    pub values: Vec<f64>, // ER values in [0.0, 1.0]
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ len; else ErError::InvalidPeriod.
  • Requires at least period valid points after first finite value; else ErError::NotEnoughValidData.
  • Indices before first + period − 1 are NaN (warmup prefix).
  • Empty slice → ErError::EmptyInputData; all-NaN → ErError::AllValuesNaN.
  • er_into_slice: output slice length must match input; else ErError::OutputLenMismatch.
  • Streaming: ErStream::update returns None until the period buffer fills; then returns Some(f64) per update.
Error Handling
use vectorta::indicators::er::{er, ErError};

match er(&input) {
    Ok(output) => process(output.values),
    Err(ErError::EmptyInputData) => eprintln!("No data provided"),
    Err(ErError::AllValuesNaN) => eprintln!("All input values are NaN"),
    Err(ErError::InvalidPeriod { period, data_len }) => {
        eprintln!("Invalid period: {} (len={})", period, data_len);
    }
    Err(ErError::NotEnoughValidData { needed, valid }) => {
        eprintln!("Not enough valid data: needed={}, valid={}", needed, valid);
    }
    Err(ErError::OutputLenMismatch { dst_len, data_len }) => {
        eprintln!("Output length mismatch: dst_len={}, data_len={}", dst_len, data_len);
    }
}

Python Bindings

Basic Usage

Calculate ER in Python (NumPy arrays supported):

import numpy as np
from vectorta import er

prices = np.array([100.0, 102.0, 101.0, 104.0, 106.0, 107.0], dtype=np.float64)

# period defaults to library default (5) if not set by upstream wrapper
values = er(prices, period=5, kernel="auto")
print(values)  # numpy.ndarray[float64]
Streaming Real-time Updates

Incremental ER with warmup-aware output:

from vectorta import ErStream

stream = ErStream(period=5)
for price in price_feed:
    value = stream.update(price)
    if value is not None:
        print(f"ER: {value}")
Batch Parameter Range

Evaluate many period values efficiently:

import numpy as np
from vectorta import er_batch

prices = np.array([...], dtype=np.float64)

# (start, end, step)
period_range = (5, 20, 5)
res = er_batch(prices, period_range, kernel="auto")

# res is a dict with keys: values (rows x cols), periods, rows, cols
print(res["values"].shape)
print(res["periods"])
CUDA Acceleration

CUDA support for ER is currently under development. The API will align with other CUDA-enabled indicators in this project.

# Coming soon: CUDA-accelerated ER calculations
# from vectorta import er_cuda_batch, er_cuda_many_series_one_param

JavaScript/WASM Bindings

Basic Usage

Calculate ER in JavaScript/TypeScript:

import { er_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101, 104, 106, 107]);
const result = er_js(prices, 5); // period=5
console.log('ER values:', result);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { er_alloc, er_free, er_into, memory } from 'vectorta-wasm';

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

const inPtr = er_alloc(len);
const outPtr = er_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(prices);

// Args: in_ptr, out_ptr, len, period
er_into(inPtr, outPtr, len, 5);

const erValues = new Float64Array(memory.buffer, outPtr, len).slice();
er_free(inPtr, len);
er_free(outPtr, len);
console.log('ER values:', erValues);
Batch Processing

Test multiple period values efficiently:

import { er_batch } from 'vectorta-wasm';

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

// Unified batch API returns values matrix + combos metadata
const config = { period_range: [5, 20, 5] }; // [start, end, step]
const result = er_batch(prices, config);

// result: { values: number[], combos: { period: number|null }[], rows, cols }
console.log('rows x cols =', result.rows, 'x', result.cols);
console.log('first combo period =', result.combos[0].period);

// Reshape flat values into 2D [rows][cols]
const matrix: number[][] = [];
for (let r = 0; r < result.rows; r++) {
  const start = r * result.cols;
  matrix.push(result.values.slice(start, start + result.cols));
}
console.log('ER(row0) sample:', matrix[0].slice(0, 8));

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators