Median Absolute Deviation (MEDIUM_AD)
period = 5 Overview
Median Absolute Deviation (MEDIUM_AD) provides the most robust statistical measure of price volatility by calculating the median of absolute deviations from the median price within a rolling window, achieving a 50% breakdown point that remains reliable even when up to half the data points are outliers. Unlike Mean Absolute Deviation which uses arithmetic averages, MEDIUM_AD employs medians at both stages of calculation, first finding the median price in the window, then computing each price's absolute deviation from that median, and finally taking the median of those deviations, creating double protection against extreme values. In normally distributed markets, standard deviation equals approximately 1.4826 times the MEDIUM_AD (the consistency constant), but during market crises with fat-tailed distributions, MEDIUM_AD provides dramatically more stable readings by completely ignoring outliers beyond the middle 50% of data. Traders apply MEDIUM_AD for fraud detection in transaction monitoring where unusual price movements need identification without false positives from legitimate spikes, for portfolio risk management where episodic market shocks shouldn't distort long-term volatility estimates, and for adaptive stop-loss systems that remain stable during flash crashes. The indicator excels in asymmetric market conditions where separate upper and lower MAD calculations can capture different volatility characteristics for upward versus downward price movements, making it particularly valuable for options traders analyzing skewed risk profiles.
Defaults: period = 5.
Implementation Examples
Compute robust dispersion in a few lines:
use vectorta::indicators::medium_ad::{medium_ad, MediumAdInput, MediumAdParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price/series slice
let prices = vec![100.0, 101.0, 98.0, 99.5, 102.0, 101.5];
let params = MediumAdParams { period: Some(5) }; // default = 5
let input = MediumAdInput::from_slice(&prices, params);
let result = medium_ad(&input)?;
// Using Candles (defaults: source="close", period=5 via with_default_candles)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MediumAdInput::with_default_candles(&candles);
let result = medium_ad(&input)?;
// Access MAD values
for v in result.values {
println!("MAD: {}", v);
} API Reference
Input Methods ▼
// From slice
MediumAdInput::from_slice(&[f64], MediumAdParams) -> MediumAdInput
// From candles with explicit source
MediumAdInput::from_candles(&Candles, &str, MediumAdParams) -> MediumAdInput
// From candles with defaults (source = "close", period = 5)
MediumAdInput::with_default_candles(&Candles) -> MediumAdInput Parameters Structure ▼
pub struct MediumAdParams {
pub period: Option<usize>, // Default: 5
} Output Structure ▼
pub struct MediumAdOutput {
pub values: Vec<f64>, // Per-index MAD (>= 0)
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ data.len()elseMediumAdError::InvalidPeriod.- If all inputs are
NaN→MediumAdError::AllValuesNaN; empty slice →MediumAdError::EmptyData. - Require at least
periodvalid points after the first finite value; otherwiseMediumAdError::NotEnoughValidData. - Warmup: indices
[0 .. first_valid + period − 1)areNaN. - Windows containing any
NaNproduceNaNat that index. - Special case:
period=1→ output is0.0for finite inputs;NaNwhere inputs areNaN. - Streaming: returns
Noneuntil the window fills and contains noNaN; subsequentNaNs delay outputs.
Error Handling ▼
use vectorta::indicators::medium_ad::{medium_ad, MediumAdError};
match medium_ad(&input) {
Ok(output) => process(output.values),
Err(MediumAdError::EmptyData) => eprintln!("Input data is empty"),
Err(MediumAdError::AllValuesNaN) => eprintln!("All input values are NaN"),
Err(MediumAdError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period={} for data_len={}", period, data_len),
Err(MediumAdError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {}", needed, valid),
} Python Bindings
Basic Usage ▼
Calculate MEDIUM_AD using NumPy arrays:
import numpy as np
from vectorta import medium_ad
prices = np.array([100.0, 101.0, 98.0, 99.5, 102.0, 101.5], dtype=float)
# Default period=5
values = medium_ad(prices, period=5)
# Specify kernel for performance ("auto", "avx2", "avx512", "scalar")
values = medium_ad(prices, period=7, kernel="auto")
print(values) Streaming Real-time Updates ▼
Process real-time updates efficiently:
from vectorta import MediumAdStream
stream = MediumAdStream(period=5)
for price in price_feed:
mad = stream.update(price)
if mad is not None:
print(f"MAD: {mad}") Batch Parameter Sweep ▼
Evaluate multiple periods at once:
import numpy as np
from vectorta import medium_ad_batch
prices = np.array([...], dtype=float)
results = medium_ad_batch(
prices,
period_range=(3, 20, 1), # start, end, step
kernel="auto"
)
print(results['values'].shape) # (rows, cols) = (num_periods, len(prices))
print(results['periods']) # periods tested CUDA Acceleration ▼
CUDA support for MEDIUM_AD is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated MEDIUM_AD calculations
# Expected APIs will mirror existing CUDA-enabled indicators. JavaScript/WASM Bindings
Basic Usage ▼
Calculate MEDIUM_AD in JavaScript/TypeScript:
import { medium_ad_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 101, 98, 99.5, 102, 101.5]);
const values = medium_ad_js(prices, 5); // period=5
console.log('MAD values:', values); Memory-Efficient Operations ▼
Use zero-copy buffers for large datasets:
import { medium_ad_alloc, medium_ad_free, medium_ad_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const n = prices.length;
const inPtr = medium_ad_alloc(n);
const outPtr = medium_ad_alloc(n);
// Copy input into WASM memory
new Float64Array(memory.buffer, inPtr, n).set(prices);
// Compute MAD directly into output buffer
medium_ad_into(inPtr, outPtr, n, 5);
// Read results back (slice to copy)
const mad = new Float64Array(memory.buffer, outPtr, n).slice();
medium_ad_free(inPtr, n);
medium_ad_free(outPtr, n);
console.log('MAD:', mad); Batch Processing ▼
Sweep periods and analyze many outputs:
import { medium_ad_batch, medium_ad_batch_into, medium_ad_alloc, medium_ad_free, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Option A: Unified batch returning an object
const result = medium_ad_batch(prices, { period_range: [3, 20, 1] });
// result: { values: Float64Array, combos: Array<{ period: number | null }>, rows: number, cols: number }
console.log(result.rows, result.cols);
// Option B: Zero-copy into a pre-allocated buffer
const rows = 20 - 3 + 1; // for start=3, end=20, step=1
const outPtr = medium_ad_alloc(rows * prices.length);
const inPtr = medium_ad_alloc(prices.length);
new Float64Array(memory.buffer, inPtr, prices.length).set(prices);
const numRows = medium_ad_batch_into(inPtr, outPtr, prices.length, 3, 20, 1);
const flat = new Float64Array(memory.buffer, outPtr, numRows * prices.length).slice();
medium_ad_free(inPtr, prices.length);
medium_ad_free(outPtr, rows * prices.length); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Accumulation/Distribution
Technical analysis indicator
Accumulation/Distribution Oscillator
Technical analysis indicator
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
High-Low Correlation
Technical analysis indicator
Deviation
Technical analysis indicator