Ehlers Adaptive CG
alpha = 0.07 (0–1) Overview
Ehlers Adaptive CG is a two-line oscillator built around John Ehlers-style adaptive cycle analysis. The implementation first smooths the input price series, then derives an adaptive cycle estimate that feeds a center-of-gravity calculation over a dynamically changing window length. The main CG line therefore reflects both the current price shape and the indicator’s current estimate of the dominant cycle length.
The second output is a simple trigger line created from the prior CG value. That keeps the structure familiar to traders who use paired oscillator and trigger crossings instead of a single standalone line. The alpha parameter controls how quickly the adaptive components react, with lower values tending to behave more slowly and higher values making the adaptive cycle estimate more responsive.
Defaults: Ehlers Adaptive CG uses `alpha = 0.07` and defaults candle input to `hl2`.
Implementation Examples
Compute the adaptive center-of-gravity line and its trigger from a slice or from candle `hl2` data.
use vector_ta::indicators::ehlers_adaptive_cg::{
ehlers_adaptive_cg,
EhlersAdaptiveCgInput,
EhlersAdaptiveCgParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let output = ehlers_adaptive_cg(&EhlersAdaptiveCgInput::from_slice(
&values,
EhlersAdaptiveCgParams { alpha: Some(0.07) },
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = ehlers_adaptive_cg(
&EhlersAdaptiveCgInput::with_default_candles(&candles)
)?;
println!("cg = {:?}", output.cg.last());
println!("trigger = {:?}", candle_output.trigger.last()); API Reference
Input Methods ▼
// From candles and a named source field
EhlersAdaptiveCgInput::from_candles(
&Candles,
&str,
EhlersAdaptiveCgParams,
) -> EhlersAdaptiveCgInput
// From a raw slice
EhlersAdaptiveCgInput::from_slice(&[f64], EhlersAdaptiveCgParams)
-> EhlersAdaptiveCgInput
// From candles with default parameters
EhlersAdaptiveCgInput::with_default_candles(&Candles)
-> EhlersAdaptiveCgInput Parameters Structure ▼
pub struct EhlersAdaptiveCgParams {
pub alpha: Option<f64>, // default 0.07
} Output Structure ▼
pub struct EhlersAdaptiveCgOutput {
pub cg: 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 strictly between0.0and1.0.- The implementation requires at least
14valid bars from the first finite value onward. - The CG output remains
NaNthrough the early adaptive bootstrap region, and the trigger line is just the one-bar-lagged CG value once CG is available. - Streaming resets on demand through
reset()and naturally returnsNoneuntil the adaptive state has enough history. - Batch mode validates alpha ranges and rejects unsupported kernels.
Builder, Streaming & Batch APIs ▼
// Builder
EhlersAdaptiveCgBuilder::new()
.alpha(f64)
.kernel(Kernel)
.apply_slice(&[f64])
EhlersAdaptiveCgBuilder::new()
.apply(&Candles)
EhlersAdaptiveCgBuilder::new()
.into_stream()
// Stream
EhlersAdaptiveCgStream::try_new(EhlersAdaptiveCgParams)
EhlersAdaptiveCgStream::update(f64) -> Option<(f64, f64)>
EhlersAdaptiveCgStream::reset()
// Batch
EhlersAdaptiveCgBatchBuilder::new()
.alpha_range(start, end, step)
.alpha_static(f64)
.kernel(Kernel)
.apply_slice(&[f64]) Error Handling ▼
pub enum EhlersAdaptiveCgError {
EmptyInputData,
AllValuesNaN,
InvalidAlpha { alpha: f64 },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: f64, end: f64, step: f64 },
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 CG array together with the trigger array. Batch returns the CG matrix, the trigger matrix, the tested alpha values, and the rows and cols shape.
import numpy as np
from vector_ta import (
ehlers_adaptive_cg,
ehlers_adaptive_cg_batch,
EhlersAdaptiveCgStream,
)
data = np.asarray(values, dtype=np.float64)
cg, trigger = ehlers_adaptive_cg(
data,
alpha=0.07,
kernel="auto",
)
stream = EhlersAdaptiveCgStream(alpha=0.07)
print(stream.update(data[-1]))
batch = ehlers_adaptive_cg_batch(
data,
alpha_range=(0.03, 0.15, 0.02),
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, in-place pointer exports, and a
dedicated streaming class. The standard JavaScript helper returns named cg and
trigger arrays, while the batch helper returns flattened CG and trigger matrices plus combos, rows,
and cols.
import init, {
ehlers_adaptive_cg_js,
ehlers_adaptive_cg_batch,
} from "/pkg/vector_ta.js";
await init();
const data = new Float64Array(values);
const single = ehlers_adaptive_cg_js(data, 0.07);
console.log(single.cg, single.trigger);
const batch = ehlers_adaptive_cg_batch(data, {
alpha_range: [0.03, 0.15, 0.02],
});
console.log(batch.cg, batch.trigger, batch.combos, 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)