Ehlers Autocorrelation Periodogram

Parameters: min_period = 8 | max_period = 48 | avg_length = 3 | enhance = true

Overview

Ehlers Autocorrelation Periodogram is not a trend oscillator in the usual sense. It is a cycle detector. The implementation high-pass filters and smooths the input series, measures how strongly that filtered signal correlates with lagged copies of itself, and then converts those lag relationships into a periodogram-like power surface across the candidate cycle lengths between the chosen minimum and maximum periods.

VectorTA distills that internal spectrum into two outputs. The dominant-cycle line is the smoothed winning cycle estimate, expressed in bars. The normalized-power line is the power value at the selected dominant-cycle bucket, which acts like a confidence read on how concentrated the spectral energy is around that chosen period. The optional enhance mode sharpens strong peaks by cubing normalized power before the final weighting step, making the dominant-cycle estimate less influenced by weaker candidate periods.

Defaults: Ehlers Autocorrelation Periodogram uses `min_period = 8`, `max_period = 48`, `avg_length = 3`, `enhance = true`, and defaults candle input to `close`.

Implementation Examples

Compute the dominant cycle and normalized power from a close series or a candle source.

use vector_ta::indicators::ehlers_autocorrelation_periodogram::{
    ehlers_autocorrelation_periodogram,
    EhlersAutocorrelationPeriodogramInput,
    EhlersAutocorrelationPeriodogramParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = ehlers_autocorrelation_periodogram(
    &EhlersAutocorrelationPeriodogramInput::from_slice(
        &close,
        EhlersAutocorrelationPeriodogramParams {
            min_period: Some(8),
            max_period: Some(48),
            avg_length: Some(3),
            enhance: Some(true),
        },
    )
)?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = ehlers_autocorrelation_periodogram(
    &EhlersAutocorrelationPeriodogramInput::with_default_candles(&candles)
)?;

println!("dominant cycle = {:?}", output.dominant_cycle.last());
println!("normalized power = {:?}", candle_output.normalized_power.last());

API Reference

Input Methods
// From candles and a named source field
EhlersAutocorrelationPeriodogramInput::from_candles(
    &Candles,
    &str,
    EhlersAutocorrelationPeriodogramParams,
) -> EhlersAutocorrelationPeriodogramInput

// From a raw slice
EhlersAutocorrelationPeriodogramInput::from_slice(
    &[f64],
    EhlersAutocorrelationPeriodogramParams,
) -> EhlersAutocorrelationPeriodogramInput

// From candles with default parameters
EhlersAutocorrelationPeriodogramInput::with_default_candles(&Candles)
    -> EhlersAutocorrelationPeriodogramInput
Parameters Structure
pub struct EhlersAutocorrelationPeriodogramParams {
    pub min_period: Option<usize>, // default 8
    pub max_period: Option<usize>, // default 48
    pub avg_length: Option<usize>, // default 3
    pub enhance: Option<bool>,     // default true
}
Output Structure
pub struct EhlersAutocorrelationPeriodogramOutput {
    pub dominant_cycle: Vec<f64>,
    pub normalized_power: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input slice must be non-empty and contain at least one finite value.
  • min_period must be at least 3.
  • max_period must be greater than min_period, and when real data length is known it cannot exceed that length.
  • The longest contiguous finite run must be at least warmup_period + 1 bars, where warmup_period = max_period + corr_window - 1.
  • Streaming resets on non-finite input and returns None until warmup completes again.
  • The normalized-power output is the power at the selected dominant-cycle bucket, not the full spectrum.
  • When enhance is enabled, normalized power is cubed before the dominant-cycle weighting step, making strong peaks dominate more aggressively.
Builder, Streaming & Batch APIs
// Builder
EhlersAutocorrelationPeriodogramBuilder::new()
    .min_period(usize)
    .max_period(usize)
    .avg_length(usize)
    .enhance(bool)
    .kernel(Kernel)
    .apply_slice(&[f64])

EhlersAutocorrelationPeriodogramBuilder::new()
    .apply(&Candles, &str)

EhlersAutocorrelationPeriodogramBuilder::new()
    .into_stream()

// Stream
EhlersAutocorrelationPeriodogramStream::try_new(EhlersAutocorrelationPeriodogramParams)
EhlersAutocorrelationPeriodogramStream::update(f64) -> Option<(f64, f64)>
EhlersAutocorrelationPeriodogramStream::get_warmup_period() -> usize

// Batch
EhlersAutocorrelationPeriodogramBatchBuilder::new()
    .min_period_range((usize, usize, usize))
    .max_period_range((usize, usize, usize))
    .avg_length_range((usize, usize, usize))
    .enhance(bool)
    .kernel(Kernel)
    .apply_slice(&[f64])

EhlersAutocorrelationPeriodogramBatchBuilder::new()
    .apply_candles(&Candles, &str)
Error Handling
pub enum EhlersAutocorrelationPeriodogramError {
    EmptyInputData,
    AllValuesNaN,
    InvalidMinPeriod { min_period: usize },
    InvalidMaxPeriod { max_period: usize, data_len: usize },
    InvalidPeriodOrder { min_period: usize, max_period: usize },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch {
        expected: usize,
        dominant_cycle_got: usize,
        normalized_power_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 dominant-cycle array together with the normalized-power array. Batch returns both matrices plus the tested min periods, max periods, averaging lengths, enhance flags, and the rows and cols shape.

import numpy as np
from vector_ta import (
    ehlers_autocorrelation_periodogram,
    ehlers_autocorrelation_periodogram_batch,
    EhlersAutocorrelationPeriodogramStream,
)

data = np.asarray(close_values, dtype=np.float64)

dominant_cycle, normalized_power = ehlers_autocorrelation_periodogram(
    data,
    min_period=8,
    max_period=48,
    avg_length=3,
    enhance=True,
    kernel="auto",
)

stream = EhlersAutocorrelationPeriodogramStream(
    min_period=8,
    max_period=48,
    avg_length=3,
    enhance=True,
)
print(stream.update(data[-1]))
print(stream.warmup_period)

batch = ehlers_autocorrelation_periodogram_batch(
    data,
    min_period_range=(8, 12, 2),
    max_period_range=(36, 48, 12),
    avg_length_range=(3, 5, 2),
    enhance=True,
    kernel="auto",
)

print(batch["min_periods"], batch["enhance_flags"], batch["rows"], batch["cols"])

JavaScript/WASM Bindings

The WASM layer exposes an object-returning single-run wrapper, pointer-oriented in-place helpers, and a batch wrapper that returns both outputs together with parameter metadata. The single-run helper returns named dominant_cycle and normalized_power arrays. The batch helper adds combos, parameter vectors, rows, and cols.

import init, {
  ehlers_autocorrelation_periodogram_js,
  ehlers_autocorrelation_periodogram_batch_js,
} from "/pkg/vector_ta.js";

await init();

const data = new Float64Array(closeValues);

const single = ehlers_autocorrelation_periodogram_js(data, 8, 48, 3, true);
console.log(single.dominant_cycle, single.normalized_power);

const batch = ehlers_autocorrelation_periodogram_batch_js(data, {
  min_period_range: [8, 12, 2],
  max_period_range: [36, 48, 12],
  avg_length_range: [3, 5, 2],
  enhance: true,
});

console.log(
  batch.dominant_cycle,
  batch.normalized_power,
  batch.min_periods,
  batch.enhance_flags,
  batch.rows,
  batch.cols
);

CUDA Bindings (Rust)

Additional details for the CUDA bindings can be found inside the VectorTA repository.

Performance Analysis

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

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

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators