MESA Adaptive Moving Average (MAMA)
fast_limit = 0.5 (0.3–0.7) | slow_limit = 0.05 (0.03–0.1) Overview
The MESA Adaptive Moving Average, developed by John Ehlers, adapts to price movement based on the rate of change of phase as measured by the Hilbert Transform Discriminator, which imparts a 90 degree phase shift to frequency components to track dominant market cycles. Rather than using fixed smoothing periods, MAMA calculates an adaptive alpha value bounded between fast and slow limits (typically 0.5 and 0.05) based on the phase rate of change, where shorter cycles produce faster adaptation and longer cycles result in slower smoothing. The indicator features a fast attack average and slow decay average that rapidly ratchets behind price changes in a distinctive staircase pattern, then holds the average value until the next ratchet occurs, eliminating the curved lag typical of traditional moving averages. MAMA produces two outputs: the primary MAMA line and the Following Adaptive Moving Average (FAMA), which uses half the alpha value of MAMA to create a slower companion line synchronized in time but with reduced vertical movement. Crossovers between MAMA and FAMA generate reliable trading signals with minimal whipsaws, as the two lines rarely cross unless a major directional change occurs, while both lines serve as dynamic support and resistance levels for pullback entries. The Hilbert Transform discriminator computes the dominant cycle by averaging phase differentials until they sum to 360 degrees, enabling MAMA to automatically adjust its responsiveness to match the prevailing market rhythm.
Implementation Examples
Compute MAMA and FAMA from prices or candles:
use vectorta::indicators::moving_averages::mama::{mama, MamaInput, MamaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price slice
let prices = vec![100.0, 101.5, 103.0, 102.2, 104.8];
let params = MamaParams { fast_limit: Some(0.5), slow_limit: Some(0.05) };
let input = MamaInput::from_slice(&prices, params);
let result = mama(&input)?; // MamaOutput { mama_values, fama_values }
// Using Candles with defaults (source = "close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MamaInput::with_default_candles(&candles);
let result = mama(&input)?;
// Access paired series
for (m, f) in result.mama_values.iter().zip(&result.fama_values) {
println!("MAMA={}, FAMA={}", m, f);
} API Reference
Input Methods ▼
// From a price slice
MamaInput::from_slice(&[f64], MamaParams) -> MamaInput
// From candles with custom source
MamaInput::from_candles(&Candles, &str, MamaParams) -> MamaInput
// From candles with defaults (close, fast_limit=0.5, slow_limit=0.05)
MamaInput::with_default_candles(&Candles) -> MamaInput Parameters Structure ▼
pub struct MamaParams {
pub fast_limit: Option<f64>, // Default: 0.5
pub slow_limit: Option<f64>, // Default: 0.05
} Output Structure ▼
pub struct MamaOutput {
pub mama_values: Vec<f64>, // primary adaptive average
pub fama_values: Vec<f64>, // following (slower) average of MAMA
} Validation, Warmup & NaNs ▼
- Requires at least
10points; elseMamaError::NotEnoughData. fast_limit > 0andslow_limit > 0; non‑finite values error.- Auto kernel routes to
Kernel::Scalarfor this indicator. - Warmup: first
10outputs areNaNto align with streaming; subsequent values are finite if inputs are. - Streaming returns
Noneuntil its internal buffer is filled (≈10 samples), then yields(MAMA, FAMA).
Error Handling ▼
use vectorta::indicators::moving_averages::mama::{mama, MamaError};
match mama(&input) {
Ok(out) => handle(out),
Err(MamaError::NotEnoughData { needed, found }) =>
eprintln!("need {needed} points, found {found}"),
Err(MamaError::InvalidFastLimit { fast_limit }) =>
eprintln!("invalid fast_limit: {fast_limit}"),
Err(MamaError::InvalidSlowLimit { slow_limit }) =>
eprintln!("invalid slow_limit: {slow_limit}"),
} Python Bindings
Basic Usage ▼
Calculate MAMA and FAMA from NumPy arrays (defaults 0.5 / 0.05):
import numpy as np
from vectorta import mama
prices = np.array([100.0, 101.5, 103.0, 102.2, 104.8])
# Returns (mama, fama) as NumPy arrays
m, f = mama(prices, fast_limit=0.5, slow_limit=0.05, kernel="auto")
print(m.shape, f.shape) Streaming Real-time Updates ▼
Use the streaming API to get the latest (MAMA, FAMA) as data arrives:
from vectorta import MamaStream
stream = MamaStream(0.5, 0.05)
for price in price_feed:
pair = stream.update(price)
if pair is not None:
mama, fama = pair
use(mama, fama) Batch Parameter Optimization ▼
Test multiple fast_limit/slow_limit combinations efficiently:
import numpy as np
from vectorta import mama_batch
prices = np.array([...], dtype=float)
res = mama_batch(
prices,
fast_limit_range=(0.30, 0.70, 0.10),
slow_limit_range=(0.03, 0.10, 0.01),
kernel="auto"
)
print(res["mama"].shape, res["fama"].shape) # (rows, cols) CUDA Acceleration ▼
CUDA support is available in this project when built with the cuda feature:
# Requires building vectorta with Python + CUDA features enabled
from vectorta import mama_cuda_batch_dev, mama_cuda_many_series_one_param_dev
# Option 1: One series, parameter sweep (device arrays returned)
m_dev, f_dev = mama_cuda_batch_dev(
data=prices.astype('float64'),
fast_limit_range=(0.30, 0.70, 0.10),
slow_limit_range=(0.03, 0.10, 0.01),
device_id=0
)
# Option 2: Many series, one parameter set (time-major [T, N] f32)
portfolio_tm_f32 = some_data_tm.astype('float32')
m_dev, f_dev = mama_cuda_many_series_one_param_dev(
portfolio_tm_f32, 0.5, 0.05, device_id=0
) JavaScript/WASM Bindings
Basic Usage ▼
Calculate MAMA in the browser or Node.js:
import { mama_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 101.5, 103, 102.2, 104.8]);
// Returns an object: { values: Float64Array([mama..., fama...]), rows: 2, cols: len }
const res = mama_js(prices, 0.5, 0.05) as { values: Float64Array; rows: number; cols: number };
const { values, cols } = res;
const mama = values.slice(0, cols);
const fama = values.slice(cols);
console.log(mama, fama); Memory-Efficient Operations ▼
Use zero-copy compute directly into preallocated memory:
import { mama_alloc, mama_free, mama_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const n = prices.length;
const inPtr = mama_alloc(n);
const outMPtr = mama_alloc(n);
const outFPtr = mama_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
mama_into(inPtr, outMPtr, outFPtr, n, 0.5, 0.05);
const mama = new Float64Array(memory.buffer, outMPtr, n).slice();
const fama = new Float64Array(memory.buffer, outFPtr, n).slice();
mama_free(inPtr, n);
mama_free(outMPtr, n);
mama_free(outFPtr, n); Batch Processing ▼
Sweep parameter grids and reshape results:
import { mama_batch_js, mama_batch_metadata_js } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
const meta = mama_batch_metadata_js(0.30, 0.70, 0.10, 0.03, 0.10, 0.01);
const numCombos = meta.length / 2;
const out = mama_batch_js(prices, 0.30, 0.70, 0.10, 0.03, 0.10, 0.01) as {
mama: Float64Array;
fama: Float64Array;
rows: number;
cols: number;
};
// Split into rows
const matrixM = [] as number[][];
for (let i = 0; i < out.rows; i++) {
const start = i * out.cols;
matrixM.push(Array.from(out.mama.slice(start, start + out.cols)));
} Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Arnaud Legoux Moving Average
Moving average indicator
Compound Ratio Moving Average (CoRa Wave)
Moving average indicator
Centered Weighted Moving Average
Moving average indicator
Double Exponential Moving Average
Moving average indicator
Ehlers Distance Coefficient Filter
Moving average indicator
Ehlers Error-Correcting EMA (ECEMA)
Moving average indicator