Kaufman Efficiency Ratio (ER)
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 vector_ta::indicators::er::{er, ErInput, ErParams};
use vector_ta::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 > 0andperiod ≤ len; elseErError::InvalidPeriod.- Requires at least
periodvalid points after first finite value; elseErError::NotEnoughValidData. - Indices before
first + period − 1areNaN(warmup prefix). - Empty slice →
ErError::EmptyInputData; all-NaN →ErError::AllValuesNaN. er_into_slice: output slice length must match input; elseErError::OutputLenMismatch.- Streaming:
ErStream::updatereturnsNoneuntil the period buffer fills; then returnsSome(f64)per update.
Error Handling ▼
use vector_ta::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 vector_ta 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 vector_ta 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 vector_ta 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 helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).
import numpy as np
from vector_ta import er_cuda_batch_dev, er_cuda_many_series_one_param_dev
# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)
dev = er_cuda_batch_dev(
data_f32=data_f32,
period_range=(5, 30, 5),
device_id=0,
)
# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)
rows, cols = data_tm_f32.shape
data_tm_f32 = data_tm_f32.ravel()
dev_tm = er_cuda_many_series_one_param_dev(
data_tm_f32=data_tm_f32,
cols=cols,
rows=rows,
period=14,
device_id=0,
) 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)); CUDA Bindings (Rust)
use vector_ta::cuda::CudaEr;
use vector_ta::indicators::er::ErBatchRange;
let cuda = CudaEr::new(0)?;
let data_f32: [f32] = /* ... */;
let sweep = ErBatchRange::default();
let out = cuda.er_batch_dev(&data_f32, &sweep)?;
let _ = out; Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28