Mean Absolute Deviation (MAD)

Parameters: period = 5

Overview

Mean Absolute Deviation (MAD) provides a robust measure of price volatility by calculating the average absolute distance between each price and the mean within a rolling window, offering superior outlier resistance compared to standard deviation. Unlike standard deviation which squares deviations and thus heavily weights extreme values, MAD treats all deviations equally by using absolute values, making it particularly valuable during abnormal market spikes or flash crashes where traditional volatility measures become distorted. The calculation involves first computing the arithmetic mean of prices in the window, then measuring each price's absolute deviation from that mean, and finally averaging these absolute deviations to produce the MAD value. When data follows a normal distribution, standard deviation equals approximately 1.2533 times the MAD, but in fat-tailed market distributions common in finance, MAD provides a more stable and reliable volatility estimate. Traders utilize MAD for comparing volatility across different assets when building diversified portfolios, identifying periods of consistent versus erratic price behavior, and setting more robust stop-loss levels that are less influenced by occasional price spikes. The indicator's simplicity and interpretability make it especially useful for risk assessment, where a lower MAD indicates steadier performance and higher values suggest increased uncertainty, with the rolling window implementation allowing real-time adaptation to changing market conditions.

Defaults (VectorTA): period=5.

Implementation Examples

Compute MAD in a few lines:

use vectorta::indicators::mean_ad::{mean_ad, MeanAdInput, MeanAdParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice (default: period=5)
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = MeanAdParams { period: Some(5) };
let input = MeanAdInput::from_slice(&prices, params);
let result = mean_ad(&input)?;

// From Candles with default params (source="close", period=5)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MeanAdInput::with_default_candles(&candles);
let result = mean_ad(&input)?;

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

API Reference

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

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

// From candles with default params (source = "close", period=5)
MeanAdInput::with_default_candles(&Candles) -> MeanAdInput
Parameters Structure
pub struct MeanAdParams {
    pub period: Option<usize>, // Default: 5
}
Output Structure
pub struct MeanAdOutput {
    pub values: Vec<f64>, // MAD values with NaN prefix during warmup
}
Validation, Warmup & NaNs
  • period > 0; otherwise MeanAdError::InvalidPeriod.
  • period ≤ data.len(); otherwise MeanAdError::InvalidPeriod.
  • There must be at least period valid points after the first finite value; else MeanAdError::NotEnoughValidData.
  • If all inputs are NaN, returns MeanAdError::AllValuesNaN.
  • Warmup: outputs are NaN up to index first + 2·period − 2; the first defined MAD appears there.
  • Implementation computes a rolling mean first, then a rolling mean of absolute residuals.
Error Handling
use vectorta::indicators::mean_ad::{mean_ad, MeanAdError};

match mean_ad(&input) {
    Ok(output) => process(output.values),
    Err(MeanAdError::EmptyData) => eprintln!("Input data is empty"),
    Err(MeanAdError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for length {}", period, data_len),
    Err(MeanAdError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, only {} found", needed, valid),
    Err(MeanAdError::AllValuesNaN) => eprintln!("All input values are NaN"),
}

Python Bindings

Basic Usage

Calculate MAD using NumPy arrays (default: period=5):

import numpy as np
from vectorta import mean_ad

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

# Default period (5)
arr = mean_ad(prices, period=5)

# Specify kernel for optimization ("auto", "avx2", "avx512", "scalar")
arr = mean_ad(prices, period=7, kernel="auto")

print(arr)
Streaming Real-time Updates

Use the streaming class to process ticks incrementally:

from vectorta import MeanAdStream

stream = MeanAdStream(period=5)

for price in price_feed:
    value = stream.update(price)  # None during warmup
    if value is not None:
        handle_value(value)
Batch Parameter Optimization

Sweep period values efficiently:

import numpy as np
from vectorta import mean_ad_batch

prices = np.array([...], dtype=float)

res = mean_ad_batch(
    prices,
    period_range=(5, 30, 5),
    kernel="auto"
)

print(res["values"].shape)  # (num_periods, len(prices))
print(res["periods"])       # tested periods
CUDA Acceleration

CUDA support for Mean Absolute Deviation is coming soon. The API will mirror other CUDA-enabled indicators.

JavaScript/WASM Bindings

Basic Usage

Calculate MAD in JavaScript/TypeScript:

import { mean_ad_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);

// period=5
const values = mean_ad_js(prices, 5);
console.log('MAD:', values);
Memory‑Efficient Operations

Use zero‑copy helpers for large arrays:

import { mean_ad_alloc, mean_ad_free, mean_ad_into, memory } from 'vectorta-wasm';

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

// Allocate WASM memory
const inPtr = mean_ad_alloc(n);
const outPtr = mean_ad_alloc(n);

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

// Compute directly into output buffer: (in_ptr, out_ptr, len, period)
mean_ad_into(inPtr, outPtr, n, 5);

// Read results
const out = new Float64Array(memory.buffer, outPtr, n).slice();

// Free WASM buffers
mean_ad_free(inPtr, n);
mean_ad_free(outPtr, n);
Batch Processing

Sweep periods efficiently with a config object:

import { mean_ad_batch } from 'vectorta-wasm';

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

// Define sweep config
const config = { period_range: [5, 30, 5] }; // start, end, step

// Calculate all combinations
const { values, periods, rows, cols } = mean_ad_batch(prices, config);

// values is a flat array of length rows*cols; reshape as needed
const matrix = [];
for (let i = 0; i < rows; i++) {
  const start = i * cols;
  matrix.push(values.slice(start, start + cols));
}

console.log('Periods tested:', periods);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators