Corrected Moving Average

Parameters: period = 35

Overview

Corrected Moving Average begins with a standard rolling mean, but it does not simply return that SMA line. Once enough samples are available, the algorithm compares the previous corrected value against the current rolling mean and measures that difference against the rolling variance. That comparison produces an adaptive gain factor, which determines how aggressively the corrected line should move back toward the current mean.

In quiet conditions, where the rolling variance is small relative to the difference between the old corrected value and the current mean, the line can respond more quickly. When the local variance already explains most of that distance, the correction becomes gentler. The result is a one-line smoother that keeps the interpretability of a moving average while reacting more selectively than a plain SMA.

Defaults: Corrected Moving Average uses `period = 35`.

Implementation Examples

Compute the corrected average from a slice or from a candle field.

use vector_ta::indicators::moving_averages::corrected_moving_average::{
    corrected_moving_average,
    CorrectedMovingAverageInput,
    CorrectedMovingAverageParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let close = vec![100.0, 100.8, 101.6, 101.1, 102.4, 102.0, 103.1];

let output = corrected_moving_average(&CorrectedMovingAverageInput::from_slice(
    &close,
    CorrectedMovingAverageParams { period: Some(35) },
))?;

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

println!("latest corrected value = {:?}", output.values.last());
println!("series length = {}", candle_output.values.len());

API Reference

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

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

// From candles with default parameters
CorrectedMovingAverageInput::with_default_candles(&Candles)
    -> CorrectedMovingAverageInput
Parameters Structure
pub struct CorrectedMovingAverageParams {
    pub period: Option<usize>, // default 35
}
Output Structure
pub struct CorrectedMovingAverageOutput {
    pub values: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input slice must be non-empty and contain at least one finite value.
  • period must be greater than zero and no larger than the input length.
  • The longest contiguous finite run must be at least period bars or the function returns NotEnoughValidData.
  • Warmup lasts for period - 1 bars, which is also what the streaming API reports through get_warmup_period().
  • Streaming resets its rolling statistics and previous corrected value when it sees a non-finite input.
  • Destination slices for in-place APIs must match the input length exactly.
  • Batch mode rejects invalid integer ranges and unsupported kernels.
Builder, Streaming & Batch APIs
// Builder
CorrectedMovingAverageBuilder::new()
    .period(usize)
    .kernel(Kernel)
    .apply_slice(&[f64])

CorrectedMovingAverageBuilder::new()
    .apply(&Candles)

CorrectedMovingAverageBuilder::new()
    .into_stream()

// Stream
CorrectedMovingAverageStream::try_new(CorrectedMovingAverageParams)
CorrectedMovingAverageStream::update(f64) -> Option<f64>
CorrectedMovingAverageStream::reset()
CorrectedMovingAverageStream::get_warmup_period() -> usize

// Batch
CorrectedMovingAverageBatchBuilder::new()
    .period_range(start, end, step)
    .period_static(usize)
    .kernel(Kernel)
    .apply_slice(&[f64])
Error Handling
pub enum CorrectedMovingAverageError {
    EmptyInputData,
    AllValuesNaN,
    InvalidPeriod { period: usize, data_len: usize },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    InvalidRange { start: usize, end: usize, step: usize },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes an array-returning single-run function, a streaming class, and a batch function. The single-run binding returns one NumPy array of corrected moving-average values. Batch returns a values matrix plus the tested periods and the final rows and cols shape.

import numpy as np
from vector_ta import (
    corrected_moving_average,
    corrected_moving_average_batch,
    CorrectedMovingAverageStream,
)

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

values = corrected_moving_average(
    data,
    period=35,
    kernel="auto",
)

stream = CorrectedMovingAverageStream(35)
print(stream.update(data[-1]))

batch = corrected_moving_average_batch(
    data,
    period_range=(20, 50, 5),
    kernel="auto",
)

print(batch["periods"], batch["rows"], batch["cols"])

JavaScript/WASM Bindings

The WASM layer exposes a direct array-returning single-run wrapper, an object-returning batch wrapper, and lower-level allocation and in-place exports. The standard JavaScript path returns the corrected-average array, while the batch wrapper returns the flattened values matrix, tested combos, and the rows and cols shape.

import init, {
  corrected_moving_average_js,
  corrected_moving_average_batch_js,
} from "/pkg/vector_ta.js";

await init();

const data = new Float64Array(closeValues);

const values = corrected_moving_average_js(data, 35);
console.log(values);

const batch = corrected_moving_average_batch_js(data, {
  period_range: [20, 50, 5],
});

console.log(batch.values, batch.combos, 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