Ehlers Simple Cycle Indicator
alpha = 0.07 (0–1) Overview
Ehlers Simple Cycle Indicator is a lighter cycle detector built around a smoothed source series and a recursive cycle estimate. Like other Ehlers-style cycle studies, it focuses on oscillatory turning structure rather than on raw trend strength. The main output is the cycle line itself, while the second output is a trigger line created by lagging the cycle one step.
The implementation uses a simplified startup path on the early valid bars before it transitions into the full recursive cycle formula. That makes the warmup shorter than some heavier adaptive cycle models while still giving the indicator a distinct paired-line structure for cycle/trigger crosses. Candle mode defaults to `hl2`, but the builder can point the calculation at other candle sources when needed.
Defaults: Ehlers Simple Cycle Indicator uses `alpha = 0.07` and defaults candle input to `hl2`.
Implementation Examples
Compute the simple cycle line and trigger from a slice or from candle `hl2` data.
use vector_ta::indicators::ehlers_simple_cycle_indicator::{
ehlers_simple_cycle_indicator,
EhlersSimpleCycleIndicatorInput,
EhlersSimpleCycleIndicatorParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let output = ehlers_simple_cycle_indicator(&EhlersSimpleCycleIndicatorInput::from_slice(
&values,
EhlersSimpleCycleIndicatorParams { alpha: Some(0.07) },
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = ehlers_simple_cycle_indicator(
&EhlersSimpleCycleIndicatorInput::with_default_candles(&candles)
)?;
println!("cycle = {:?}", output.cycle.last());
println!("trigger = {:?}", candle_output.trigger.last()); API Reference
Input Methods ▼
// From candles and a named source field
EhlersSimpleCycleIndicatorInput::from_candles(
&Candles,
&str,
EhlersSimpleCycleIndicatorParams,
) -> EhlersSimpleCycleIndicatorInput
// From a raw slice
EhlersSimpleCycleIndicatorInput::from_slice(&[f64], EhlersSimpleCycleIndicatorParams)
-> EhlersSimpleCycleIndicatorInput
// From candles with default parameters
EhlersSimpleCycleIndicatorInput::with_default_candles(&Candles)
-> EhlersSimpleCycleIndicatorInput Parameters Structure ▼
pub struct EhlersSimpleCycleIndicatorParams {
pub alpha: Option<f64>, // default 0.07
} Output Structure ▼
pub struct EhlersSimpleCycleIndicatorOutput {
pub cycle: Vec<f64>,
pub trigger: Vec<f64>,
} Validation, Warmup & NaNs ▼
- The input slice must be non-empty and contain at least one finite value.
alphamust be finite and stay inside the closed0.0..=1.0range.- The implementation requires at least
3valid samples from the first finite value onward. - The standard output is NaN-prefixed through
first_valid + 2forcycleandfirst_valid + 3fortrigger. - Streaming returns a tuple on every call, but early warmup values can still be NaN.
- The earliest valid bars use a fallback cycle formula before the full recursive cycle expression takes over.
Builder, Streaming & Batch APIs ▼
// Builder
EhlersSimpleCycleIndicatorBuilder::new()
.source(&'static str)
.alpha(f64)
.kernel(Kernel)
.apply_slice(&[f64])
EhlersSimpleCycleIndicatorBuilder::new()
.apply(&Candles)
EhlersSimpleCycleIndicatorBuilder::new()
.into_stream()
// Stream
EhlersSimpleCycleIndicatorStream::try_new(EhlersSimpleCycleIndicatorParams)
EhlersSimpleCycleIndicatorStream::update(f64) -> (f64, f64)
// Batch
EhlersSimpleCycleIndicatorBatchBuilder::new()
.source(&'static str)
.alpha_range((f64, f64, f64))
.kernel(Kernel)
.apply_slice(&[f64])
EhlersSimpleCycleIndicatorBatchBuilder::new()
.apply(&Candles) Error Handling ▼
pub enum EhlersSimpleCycleIndicatorError {
EmptyInputData,
AllValuesNaN,
InvalidAlpha { alpha: f64 },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
} Python Bindings
Python exposes an object-returning single-run function, a streaming class, and a batch function. The single-run
binding returns a dict with cycle and trigger. Batch returns both matrices plus the
tested alpha values and the rows and cols shape.
import numpy as np
from vector_ta import (
ehlers_simple_cycle_indicator,
ehlers_simple_cycle_indicator_batch,
EhlersSimpleCycleIndicatorStream,
)
data = np.asarray(values, dtype=np.float64)
single = ehlers_simple_cycle_indicator(
data,
alpha=0.07,
kernel="auto",
)
stream = EhlersSimpleCycleIndicatorStream(alpha=0.07)
print(stream.update(data[-1]))
batch = ehlers_simple_cycle_indicator_batch(
data,
alpha_range=(0.07, 0.21, 0.07),
kernel="auto",
)
print(batch["alphas"], batch["rows"], batch["cols"]) JavaScript/WASM Bindings
The WASM layer exposes an object-returning single-run wrapper, a batch wrapper, and pointer-oriented helpers. The
single-run helper returns named cycle and trigger arrays, while the batch helper adds
alpha metadata, rows, and cols.
import init, {
ehlers_simple_cycle_indicator_js,
ehlers_simple_cycle_indicator_batch_js,
} from "/pkg/vector_ta.js";
await init();
const data = new Float64Array(values);
const single = ehlers_simple_cycle_indicator_js(data, 0.07);
console.log(single.cycle, single.trigger);
const batch = ehlers_simple_cycle_indicator_batch_js(data, {
alpha_range: [0.07, 0.21, 0.07],
});
console.log(batch.cycle, batch.trigger, batch.alphas, 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)