Arnaud Legoux Moving Average (ALMA)

Parameters: period = 9 | offset = 0.85 (0–1) | sigma = 6

Overview

ALMA creates a low lag moving average by applying a Gaussian distribution curve to weight price data, with most emphasis placed near the end of the lookback window rather than equally across all periods. Arnaud Legoux and Dimitris Kouzis-Loukas developed this approach to combine the responsiveness of shorter averages with the smoothness of longer ones. The offset parameter shifts the Gaussian peak within the window, where 0.85 places maximum weight at 85% toward recent prices, while sigma controls the curve's width to balance between sharp response and noise reduction. Unlike simple or exponential averages that apply fixed weighting schemes, ALMA dynamically adjusts its emphasis based on the Gaussian curve, reducing lag during trends while maintaining stability during consolidations. Traders use ALMA as a superior trend filter that reacts quickly to genuine price moves yet resists whipsaws from market noise, making it particularly effective for identifying entry points in trending markets.

Implementation Examples

Compute ALMA from price slices or candles in a few lines:

use vectorta::indicators::moving_averages::alma::{alma, AlmaInput, AlmaParams};
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 = AlmaParams { period: Some(9), offset: Some(0.85), sigma: Some(6.0) };
let input = AlmaInput::from_slice(&prices, params);
let result = alma(&input)?;

// Using with Candles (defaults: period=9, offset=0.85, sigma=6.0; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = AlmaInput::with_default_candles(&candles);
let result = alma(&input)?;

// Access the ALMA values
for value in result.values {
    println!("ALMA: {}", value);
}

API Reference

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

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

// From candles with default params (close prices; 9/0.85/6.0)
AlmaInput::with_default_candles(&Candles) -> AlmaInput
Parameters Structure
pub struct AlmaParams {
    pub period: Option<usize>, // Default: 9
    pub offset: Option<f64>,   // Default: 0.85 (in [0.0, 1.0])
    pub sigma: Option<f64>,    // Default: 6.0 (> 0.0)
}
Output Structure
pub struct AlmaOutput {
    pub values: Vec<f64>, // ALMA values (Gaussian-weighted average)
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data.len().
  • offset ∈ [0.0, 1.0] and finite; otherwise AlmaError::InvalidOffset.
  • sigma > 0.0; otherwise AlmaError::InvalidSigma.
  • There must be at least period valid points after the first finite value; else AlmaError::NotEnoughValidData.
  • Warmup: NaN prefix length is first_valid + period - 1; first output at index first_valid + period - 1.
  • Streaming: AlmaStream::update returns None until warm; subsequent NaN inputs propagate to outputs.
Error Handling
use vectorta::indicators::moving_averages::alma::{alma, AlmaError};

match alma(&input) {
    Ok(output) => process_results(output.values),
    Err(AlmaError::EmptyInputData) =>
        println!("Input data is empty"),
    Err(AlmaError::AllValuesNaN) =>
        println!("All input values are NaN"),
    Err(AlmaError::InvalidPeriod { period, data_len }) =>
        println!("Invalid period {} for data length {}", period, data_len),
    Err(AlmaError::NotEnoughValidData { needed, valid }) =>
        println!("Need {} data points, only {} valid", needed, valid),
    Err(AlmaError::InvalidSigma { sigma }) =>
        println!("Invalid sigma: {} (must be > 0)", sigma),
    Err(AlmaError::InvalidOffset { offset }) =>
        println!("Invalid offset: {} (must be in [0,1])", offset),
}

Python Bindings

Basic Usage

Calculate ALMA using NumPy arrays (defaults: period=9, offset=0.85, sigma=6.0):

import numpy as np
from vectorta import alma

# Prepare price data as NumPy array
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Calculate ALMA with explicit parameters
result = alma(prices, period=9, offset=0.85, sigma=6.0, kernel="auto")

# Result is a NumPy array matching input length
print("ALMA values:", result)
Streaming Real-time Updates

Process real-time price updates efficiently with ALMA:

from vectorta import AlmaStream

# Initialize streaming ALMA calculator
stream = AlmaStream(period=9, offset=0.85, sigma=6.0)

# Process real-time price updates
for price in price_feed:
    alma_value = stream.update(price)

    if alma_value is not None:
        # ALMA value is ready (None during warmup period)
        print("Current ALMA:", alma_value)
Batch Parameter Optimization

Test multiple parameter combinations and retrieve per‑combo results:

import numpy as np
from vectorta import alma_batch

prices = np.array([...])  # Your historical prices

results = alma_batch(
    prices,
    period_range=(5, 20, 5),      # 5, 10, 15, 20
    offset_range=(0.80, 0.90, 0.05),
    sigma_range=(4.0, 8.0, 2.0),
    kernel="auto"
)

print("Values shape:", results['values'].shape)  # (num_combinations, len(prices))
print("Periods tested:", results['periods'])
print("Offsets tested:", results['offsets'])
print("Sigmas tested:", results['sigmas'])
CUDA Acceleration

CUDA-backed Python bindings for ALMA are available when vectorta is built with CUDA support.

import numpy as np
from vectorta import alma_cuda_batch_dev, alma_cuda_many_series_one_param_dev

# Option 1: One series, many parameters (FP32 input)
prices_f32 = np.array([...], dtype=np.float32)
dev_array = alma_cuda_batch_dev(
    prices_f32,
    period_range=(5, 20, 1),
    offset_range=(0.80, 0.90, 0.01),
    sigma_range=(4.0, 8.0, 0.5),
    device_id=0
)
# dev_array exposes CUDA array interface for downstream libraries

# Option 2: Many series, one parameter set (time-major FP32 input)
data_tm_f32 = np.array([...], dtype=np.float32)  # shape: [time_steps, num_assets]
dev_array = alma_cuda_many_series_one_param_dev(
    data_tm_f32,
    period=9,
    offset=0.85,
    sigma=6.0,
    device_id=0
)

JavaScript/WASM Bindings

Basic Usage

Calculate ALMA in JavaScript/TypeScript:

import { alma_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// Calculate ALMA with specified parameters
const result = alma_js(prices, 9, 0.85, 6.0);

// Result is a Float64Array
console.log('ALMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { alma_alloc, alma_free, alma_into, memory } from 'vectorta-wasm';

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

// Allocate WASM memory for input and output
const inPtr = alma_alloc(length);
const outPtr = alma_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate ALMA directly into allocated memory
// Args: in_ptr, out_ptr, len, period, offset, sigma
alma_into(inPtr, outPtr, length, 9, 0.85, 6.0);

// Read results from WASM memory (slice() to copy out)
const almaValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
alma_free(inPtr, length);
alma_free(outPtr, length);

console.log('ALMA values:', almaValues);
Batch Processing

Test multiple parameter combinations using a unified batch API:

import { alma_batch_unified_js } from 'vectorta-wasm';

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

// Define parameter sweep ranges via config object
const config = {
  period_range: [5, 20, 5],     // 5, 10, 15, 20
  offset_range: [0.80, 0.90, 0.05],
  sigma_range: [4.0, 8.0, 2.0]
};

// Calculate all combinations; returns { values, combos, rows, cols }
const out = alma_batch_unified_js(prices, config);
console.log('Rows x Cols:', out.rows, out.cols);
console.log('First combo params:', out.combos[0]);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators