Gaussian Filter

Parameters: period = 14 | poles = 4 (1–4)

Overview

The Gaussian Filter creates an exceptionally smooth price curve by cascading multiple stages of exponential smoothing to approximate the bell shaped Gaussian distribution, producing minimal lag while eliminating market noise more effectively than traditional moving averages. Each pole represents a filtering stage that recursively smooths the output of the previous stage, with more poles creating a response closer to the ideal Gaussian curve that balances responsiveness with stability. When using four poles, the filter achieves near perfect Gaussian characteristics, removing high frequency noise while preserving the underlying trend structure with less delay than comparable smooth averages. Traders employ Gaussian filters to identify clean trend direction without the jagged movements of faster averages or the excessive lag of slower ones, making it ideal for automated systems that require stable signals. The adjustable pole count allows fine tuning between aggressive filtering for noisy markets (4 poles) and responsive tracking for cleaner price action (1 or 2 poles). Additionally, the Gaussian filter excels as a baseline for oscillators and divergence analysis because its mathematical properties create predictable lag characteristics that remain consistent across different market conditions.

Implementation Examples

Create inputs from a slice or candles and compute Gaussian:

use vectorta::indicators::gaussian::{gaussian, GaussianInput, GaussianParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = GaussianParams { period: Some(14), poles: Some(4) };
let input = GaussianInput::from_slice(&prices, params);
let result = gaussian(&input)?;

// Using with Candles data structure (defaults: period=14, poles=4; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = GaussianInput::with_default_candles(&candles);
let result = gaussian(&input)?;

// Access values
for v in result.values { println!("Gaussian: {}", v); }

API Reference

Input Methods
// From price slice
GaussianInput::from_slice(&[f64], GaussianParams) -> GaussianInput

// From candles with custom source
GaussianInput::from_candles(&Candles, &str, GaussianParams) -> GaussianInput

// From candles with default params (close prices, period=14, poles=4)
GaussianInput::with_default_candles(&Candles) -> GaussianInput
Parameters Structure
pub struct GaussianParams {
    pub period: Option<usize>, // Default: 14
    pub poles: Option<usize>,  // Default: 4 (allowed: 1..=4)
}
Output Structure
pub struct GaussianOutput {
    pub values: Vec<f64>, // Filtered values
}
Validation, Warmup & NaNs
  • period ≥ 2. period == 1 is rejected: GaussianError::PeriodOneDegenerate; period < 2 yields GaussianError::DegeneratePeriod.
  • poles ∈ [1, 4]; otherwise GaussianError::InvalidPoles.
  • period ≤ data.len(); otherwise GaussianError::PeriodLongerThanData.
  • Must have at least period valid points after first finite input; otherwise GaussianError::NotEnoughValidData.
  • Leading indices up to first_valid + period are set to NaN (warmup); subsequent NaNs propagate.
Error Handling
use vectorta::indicators::gaussian::GaussianError;

match gaussian(&input) {
    Ok(output) => process_results(output.values),
    Err(GaussianError::NoData) => println!("No input data"),
    Err(GaussianError::PeriodOneDegenerate) => println!("period=1 is not allowed"),
    Err(GaussianError::DegeneratePeriod { period }) => println!("period {} must be ≥ 2", period),
    Err(GaussianError::InvalidPoles { poles }) => println!("poles must be 1..4; got {}", poles),
    Err(GaussianError::PeriodLongerThanData { period, data_len }) =>
        println!("period {} exceeds data length {}", period, data_len),
    Err(GaussianError::AllValuesNaN) => println!("All inputs are NaN"),
    Err(GaussianError::NotEnoughValidData { needed, valid }) =>
        println!("Need {} valid points after first finite; got {}", needed, valid),
}

Python Bindings

Basic Usage

Calculate Gaussian using NumPy arrays (explicit parameters required):

import numpy as np
from vectorta import gaussian

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=np.float64)

# Specify parameters explicitly (no implicit defaults in Python wrapper)
values = gaussian(prices, period=14, poles=4, kernel="auto")
Streaming
import numpy as np
from vectorta import GaussianStream

stream = GaussianStream(period=14, poles=4)
for x in [100.0, 101.0, 100.5, 102.0]:
    y = stream.update(float(x))
    print(y)
Batch Processing

Test multiple parameter combinations efficiently:

import numpy as np
from vectorta import gaussian_batch

prices = np.array([/* your data */], dtype=np.float64)
out = gaussian_batch(
    prices,
    period_range=(10, 30, 10),
    poles_range=(1, 4, 1),
    kernel="auto"
)

print(out["values"].shape)  # (num_combos, len(prices))
print(out["periods"])       # list of periods for each row
print(out["poles"])         # list of pole counts for each row
CUDA Acceleration

Available when built with Python + CUDA features.

import numpy as np
from vectorta import gaussian_cuda_batch_dev, gaussian_cuda_many_series_one_param_dev

# Option 1: One series, many parameters (parameter sweep)
prices_f32 = np.array([/* your data */], dtype=np.float32)
res = gaussian_cuda_batch_dev(
    data_f32=prices_f32,
    period_range=(10, 30, 1),
    poles_range=(1, 4, 1),
    device_id=0
)
# res is a device-resident array wrapper (DeviceArrayF32Py)

# Option 2: Many series, one parameter set (time-major: [T, N])
tm = np.array([/* T x N */], dtype=np.float32)
out = gaussian_cuda_many_series_one_param_dev(
    prices_tm_f32=tm,
    period=14,
    poles=4,
    device_id=0
)

JavaScript/WASM Bindings

Basic Usage

Calculate Gaussian in JavaScript/TypeScript:

import { gaussian_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const values = gaussian_js(prices, 14, 4);
console.log('Gaussian values:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { gaussian_alloc, gaussian_free, gaussian_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const len = prices.length;

const inPtr = gaussian_alloc(len);
const outPtr = gaussian_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(prices);

// Args: in_ptr, out_ptr, len, period, poles
gaussian_into(inPtr, outPtr, len, 14, 4);

const out = new Float64Array(memory.buffer, outPtr, len).slice();

gaussian_free(inPtr, len);
gaussian_free(outPtr, len);
Batch Processing

Test parameter combinations in one call:

import { gaussian_batch_js, gaussian_batch_metadata_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

const pStart = 10, pEnd = 30, pStep = 10;  // 10, 20, 30
const kStart = 1,  kEnd = 4,  kStep = 1;   // 1..4

const metadata = gaussian_batch_metadata_js(pStart, pEnd, pStep, kStart, kEnd, kStep);
const numCombos = metadata.length / 2; // [period, poles] pairs

const flat = gaussian_batch_js(prices, pStart, pEnd, pStep, kStart, kEnd, kStep);

// Reshape flat results into matrix
const matrix = [];
for (let i = 0; i < numCombos; i++) {
  const start = i * prices.length;
  const end = start + prices.length;
  matrix.push(flat.slice(start, end));
}

Performance Analysis

Comparison:
View:
Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

CUDA note

In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.

Related Indicators