Nonlinear Regression Zero Lag Moving Average

Parameters: zlma_period = 15 | regression_period = 15

Overview

Nonlinear Regression Zero Lag Moving Average starts by running two weighted moving averages in sequence, then doubles the first response and subtracts the second to build a zero-lag input series. That series is fed into a rolling quadratic regression, which produces the final trend value with less phase delay than a plain moving average crossover.

The indicator returns the regression value, a one-bar delayed signal line, and two event flags that turn on when the value crosses above or below the signal. It can run on a candle close source or on a direct price slice, and the streaming variant resets itself whenever a NaN sample appears.

Defaults: `zlma_period = 15` and `regression_period = 15`.

Implementation Examples

Apply the indicator to a close slice or let it read the default close source from candles.

use vector_ta::indicators::nonlinear_regression_zero_lag_moving_average::{
    nonlinear_regression_zero_lag_moving_average,
    NonlinearRegressionZeroLagMovingAverageInput,
    NonlinearRegressionZeroLagMovingAverageParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = nonlinear_regression_zero_lag_moving_average(
    &NonlinearRegressionZeroLagMovingAverageInput::from_slice(
        &close,
        NonlinearRegressionZeroLagMovingAverageParams {
            zlma_period: Some(15),
            regression_period: Some(15),
        },
    ),
)?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = nonlinear_regression_zero_lag_moving_average(
    &NonlinearRegressionZeroLagMovingAverageInput::with_default_candles(&candles),
)?;

println!("value = {:?}", output.value.last());
println!("signal = {:?}", candle_output.signal.last());
println!("long crossover = {:?}", candle_output.long_signal.last());

API Reference

Input Methods
NonlinearRegressionZeroLagMovingAverageInput::from_candles(
    &Candles,
    "close",
    NonlinearRegressionZeroLagMovingAverageParams,
) -> NonlinearRegressionZeroLagMovingAverageInput

NonlinearRegressionZeroLagMovingAverageInput::from_slice(
    &[f64],
    NonlinearRegressionZeroLagMovingAverageParams,
) -> NonlinearRegressionZeroLagMovingAverageInput

NonlinearRegressionZeroLagMovingAverageInput::with_default_candles(&Candles)
    -> NonlinearRegressionZeroLagMovingAverageInput
Parameters Structure
pub struct NonlinearRegressionZeroLagMovingAverageParams {
    pub zlma_period: Option<usize>,       // default 15
    pub regression_period: Option<usize>, // default 15
}
Output Structure
pub struct NonlinearRegressionZeroLagMovingAverageOutput {
    pub value: Vec<f64>,
    pub signal: Vec<f64>,
    pub long_signal: Vec<f64>,
    pub short_signal: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input slice must not be empty and must contain at least one finite value.
  • zlma_period and regression_period must both be greater than 0.
  • The longest consecutive finite run must be at least 2 * zlma_period + regression_period - 2.
  • Warmup equals that required valid count minus 1, and earlier outputs are emitted as NaN with zeroed crossover flags.
  • The streaming implementation resets itself and returns None when it receives NaN.
  • Batch mode rejects non-batch kernels through InvalidKernelForBatch.
Builder, Streaming & Batch APIs
NonlinearRegressionZeroLagMovingAverageBuilder::new()
    .zlma_period(usize)
    .regression_period(usize)
    .kernel(Kernel)
    .apply(&Candles)
    .apply_candles(&Candles, "close")
    .apply_slice(&[f64])
    .into_stream()

NonlinearRegressionZeroLagMovingAverageStream::try_new(params)
stream.update(f64) -> Option<NonlinearRegressionZeroLagMovingAveragePoint>
stream.reset()
stream.get_warmup_period() -> usize

NonlinearRegressionZeroLagMovingAverageBatchBuilder::new()
    .zlma_period_range((start, end, step))
    .regression_period_range((start, end, step))
    .kernel(Kernel)
    .apply_slice(&[f64])
    .apply_candles(&Candles, "close")

Python Bindings

Python exposes a single-array function, a stream class, and a batch helper. The scalar call returns four NumPy arrays in value-signal-crossover order, the stream emits four scalars or None, and the batch helper returns a dictionary containing reshaped matrices plus the swept period axes.

from vector_ta import (
    nonlinear_regression_zero_lag_moving_average,
    nonlinear_regression_zero_lag_moving_average_batch,
    NonlinearRegressionZeroLagMovingAverageStream,
)

value, signal, long_signal, short_signal = nonlinear_regression_zero_lag_moving_average(
    close,
    zlma_period=15,
    regression_period=15,
)

stream = NonlinearRegressionZeroLagMovingAverageStream(15, 15)
point = stream.update(close[-1])

batch = nonlinear_regression_zero_lag_moving_average_batch(
    close,
    zlma_period_range=(10, 20, 5),
    regression_period_range=(10, 20, 5),
)

print(batch.keys())
# dict_keys(['value', 'signal', 'long_signal', 'short_signal',
#            'zlma_periods', 'regression_periods', 'rows', 'cols'])

JavaScript/WASM Bindings

The WASM surface includes high-level scalar and batch entry points plus low-level allocation and into-buffer helpers. The high-level calls return plain JS objects, while the low-level calls write four contiguous output arrays into caller-managed memory.

import init, {
  nonlinear_regression_zero_lag_moving_average_js,
  nonlinear_regression_zero_lag_moving_average_batch_js,
  nonlinear_regression_zero_lag_moving_average_alloc,
  nonlinear_regression_zero_lag_moving_average_free,
  nonlinear_regression_zero_lag_moving_average_into,
  nonlinear_regression_zero_lag_moving_average_batch_into,
} from "vector-ta-wasm";

await init();

const out = nonlinear_regression_zero_lag_moving_average_js(close, 15, 15);
const batch = nonlinear_regression_zero_lag_moving_average_batch_js(close, {
  zlma_period_range: [10, 20, 5],
  regression_period_range: [10, 20, 5],
});

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