Ehlers Detrending Filter
length = 10 Overview
Ehlers Detrending Filter is designed to strip out slower trend content so the remaining oscillatory structure is
easier to inspect. The implementation applies an Ehlers-style detrending stage first, then smooths that
detrended series with cosine-weighted averaging. The main edf output is therefore not the raw
difference from price, but a filtered residual that emphasizes shorter swings once the broader trend is removed.
The second output is a discrete slope-state signal derived from the filtered line. Rising states are encoded as
1 or 2, falling states as -1 or -2, and flat transitions as
0. That gives the page both a continuous detrended waveform and a compact categorical read on
whether that waveform is accelerating or decelerating. Candle mode defaults to `hlcc4`, but the builder can point
the filter at any supported candle source.
Defaults: Ehlers Detrending Filter uses `length = 10` and defaults candle input to `hlcc4`.
Implementation Examples
Compute the detrended filter line and its discrete slope signal from a slice or a candle source.
use vector_ta::indicators::ehlers_detrending_filter::{
ehlers_detrending_filter,
EhlersDetrendingFilterInput,
EhlersDetrendingFilterParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let output = ehlers_detrending_filter(&EhlersDetrendingFilterInput::from_slice(
&values,
EhlersDetrendingFilterParams { length: Some(10) },
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = ehlers_detrending_filter(
&EhlersDetrendingFilterInput::with_default_candles(&candles)
)?;
println!("edf = {:?}", output.edf.last());
println!("signal = {:?}", candle_output.signal.last()); API Reference
Input Methods ▼
// From candles and a named source field
EhlersDetrendingFilterInput::from_candles(
&Candles,
&str,
EhlersDetrendingFilterParams,
) -> EhlersDetrendingFilterInput
// From a raw slice
EhlersDetrendingFilterInput::from_slice(&[f64], EhlersDetrendingFilterParams)
-> EhlersDetrendingFilterInput
// From candles with default parameters
EhlersDetrendingFilterInput::with_default_candles(&Candles)
-> EhlersDetrendingFilterInput Parameters Structure ▼
pub struct EhlersDetrendingFilterParams {
pub length: Option<usize>, // default 10
} Output Structure ▼
pub struct EhlersDetrendingFilterOutput {
pub edf: Vec<f64>,
pub signal: Vec<f64>,
} Validation, Warmup & NaNs ▼
- The input slice must be non-empty and contain at least one finite source value.
lengthmust be greater than zero and no larger than the input length.- The implementation requires at least
lengthconsecutive finite values from the first valid source point onward. - Warmup lasts through
first_valid + length - 1in the standard output path. - Streaming resets on non-finite input and returns
Noneuntil the full weighted window is rebuilt. - The
signalline is a categorical slope-state read, not a second smoothed waveform. - Batch mode sweeps only
lengthand rejects unsupported kernels.
Builder, Streaming & Batch APIs ▼
// Builder
EhlersDetrendingFilterBuilder::new()
.length(usize)
.source(String)
.kernel(Kernel)
.apply_slice(&[f64])
EhlersDetrendingFilterBuilder::new()
.apply(&Candles)
EhlersDetrendingFilterBuilder::new()
.into_stream()
// Stream
EhlersDetrendingFilterStream::try_new(EhlersDetrendingFilterParams)
EhlersDetrendingFilterStream::update(f64) -> Option<(f64, f64)>
EhlersDetrendingFilterStream::reset()
// Batch
EhlersDetrendingFilterBatchBuilder::new()
.length_range(start, end, step)
.length_static(usize)
.kernel(Kernel)
.apply_slice(&[f64])
EhlersDetrendingFilterBatchBuilder::new()
.apply_candles(&Candles, &str) Error Handling ▼
pub enum EhlersDetrendingFilterError {
EmptyInputData,
AllValuesNaN,
InvalidLength { length: usize, data_len: usize },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
} Python Bindings
Python exposes a two-array single-run function, a streaming class, and a batch function. The single-run binding returns the detrended filter array together with the signal array. Batch returns both matrices plus the tested lengths and the rows and cols shape.
import numpy as np
from vector_ta import (
ehlers_detrending_filter,
ehlers_detrending_filter_batch,
EhlersDetrendingFilterStream,
)
data = np.asarray(values, dtype=np.float64)
edf, signal = ehlers_detrending_filter(
data,
length=10,
kernel="auto",
)
stream = EhlersDetrendingFilterStream(length=10)
print(stream.update(data[-1]))
batch = ehlers_detrending_filter_batch(
data,
length_range=(10, 20, 5),
kernel="auto",
)
print(batch["lengths"], batch["rows"], batch["cols"]) JavaScript/WASM Bindings
The WASM layer exposes an object-returning single-run wrapper, a batch wrapper, and pointer-oriented in-place
helpers. The single-run helper returns named edf and signal arrays, while the batch
helper adds combos, rows, and cols.
import init, {
ehlers_detrending_filter_js,
ehlers_detrending_filter_batch_js,
} from "/pkg/vector_ta.js";
await init();
const data = new Float64Array(values);
const single = ehlers_detrending_filter_js(data, 10);
console.log(single.edf, single.signal);
const batch = ehlers_detrending_filter_batch_js(data, {
length_range: [10, 20, 5],
});
console.log(batch.edf, batch.signal, batch.rows, batch.cols); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)
Related Indicators
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
Alligator
Technical analysis indicator
Aroon
Technical analysis indicator
Aroon Oscillator
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator