Moving Average Adaptive Q (MAAQ)

Parameters: period = 11 | fast_period = 2 | slow_period = 30

Overview

The Moving Average Adaptive Q, developed by Perry Kaufman in 1995, adjusts its smoothing factor dynamically based on the ratio of directional price movement to total volatility over a specified window. MAAQ calculates signal strength as the absolute change from the current price to the price n periods ago, then divides this by the sum of all individual price changes within that window to derive an efficiency ratio. This ratio scales between fast and slow smoothing constants (0.667 and 0.0645 by default), with the unique characteristic that the resulting adaptive factor is squared before application, creating a quadratic response that amplifies the difference between trending and ranging markets. When markets trend efficiently with minimal noise, MAAQ accelerates dramatically to track price closely, while in choppy sideways markets it slows down substantially to filter out whipsaws. Unlike its sibling KAMA which uses a linear smoothing constant, MAAQ's squared adaptive factor provides more aggressive filtering during consolidation and faster response during breakouts, making it particularly effective for trend following strategies that require clear distinction between signal and noise.

Defaults: period = 11, fast = 2, slow = 30.

Implementation Examples

Get started with MAAQ in just a few lines:

use vectorta::indicators::moving_averages::maaq::{maaq, MaaqInput, MaaqParams};
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 = MaaqParams { period: Some(11), fast_period: Some(2), slow_period: Some(30) };
let input = MaaqInput::from_slice(&prices, params);
let result = maaq(&input)?;

// Using with Candles data structure (defaults: period=11, fast=2, slow=30; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MaaqInput::with_default_candles(&candles);
let result = maaq(&input)?;

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

API Reference

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

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

// From candles with default params (close, period=11, fast=2, slow=30)
MaaqInput::with_default_candles(&Candles) -> MaaqInput
Parameters Structure
pub struct MaaqParams {
    pub period: Option<usize>,      // Default: 11
    pub fast_period: Option<usize>, // Default: 2
    pub slow_period: Option<usize>, // Default: 30
}
Output Structure
pub struct MaaqOutput {
    pub values: Vec<f64>, // Adaptive moving average values
}
Validation, Warmup & NaNs
  • period > 0, fast_period > 0, slow_period > 0; otherwise MaaqError::ZeroPeriods.
  • If period ≥ data.len() then MaaqError::InvalidPeriod.
  • After the first finite input at index first, there must be at least period valid points: else MaaqError::NotEnoughValidData.
  • All inputs NaN yields MaaqError::AllValuesNaN.
  • Batch: indices [0 .. first + period − 2] are NaN (ALMA parity). Streaming: returns raw inputs during warmup, then MAAQ values.
Error Handling
use vectorta::indicators::moving_averages::maaq::{maaq, MaaqError};

match maaq(&input) {
    Ok(output) => process(output.values),
    Err(MaaqError::ZeroPeriods { period, fast_p, slow_p }) => eprintln!("Zero periods: p={}, f={}, s={}", period, fast_p, slow_p),
    Err(MaaqError::InvalidPeriod { period, data_len }) => eprintln!("Invalid period: {} for len {}", period, data_len),
    Err(MaaqError::NotEnoughValidData { needed, valid }) => eprintln!("Need {} valid points, have {}", needed, valid),
    Err(MaaqError::AllValuesNaN) => eprintln!("All input values are NaN"),
}

Python Bindings

Basic Usage
import numpy as np
from vectorta import maaq

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

# Defaults: period=11, fast=2, slow=30
result = maaq(prices, period=11, fast_period=2, slow_period=30)
print(result)  # 1D array of MAAQ values
Batch Processing

Test multiple parameter combinations for optimization:

import numpy as np
from vectorta import maaq_batch

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

# Define parameter ranges (start, end, step)
period_range = (9, 20, 1)
fast_range = (1, 5, 1)
slow_range = (20, 50, 5)

results = maaq_batch(
    prices,
    period_range=period_range,
    fast_period_range=fast_range,
    slow_period_range=slow_range,
    kernel="auto"
)

print(results['values'].shape)  # (num_combos, len(prices))
print(results['periods'])
print(results['fast_periods'])
print(results['slow_periods'])
CUDA Acceleration

CUDA bindings are available for batch and multi-series operations when built with CUDA support.

from vectorta import (
    maaq_cuda_batch_dev,
    maaq_cuda_many_series_one_param_dev,
)
import numpy as np

# 1) One series, many parameters (parameter sweep)
prices = np.array([...], dtype=float)
period_range = (9, 20, 1)
fast_range = (1, 5, 1)
slow_range = (20, 50, 5)
dev_buf = maaq_cuda_batch_dev(prices, period_range, fast_range, slow_range, device_id=0)
# dev_buf is a device array wrapper (float32) with grid results

# 2) Many series, one parameter set (portfolio/time-major)
data_tm_f32 = np.array([...], dtype=np.float32)  # shape [T, N]
dev_buf2 = maaq_cuda_many_series_one_param_dev(
    data_tm_f32,
    period=11,
    fast_period=2,
    slow_period=30,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Calculate MAAQ in JavaScript/TypeScript:

import { maaq_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);

// Calculate MAAQ with specified parameters
const result = maaq_js(prices, 11, 2, 30);  // period=11, fast=2, slow=30

console.log('MAAQ values:', result);
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { maaq_alloc, maaq_free, maaq_into, memory } from 'vectorta-wasm';

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

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

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

// Args: in_ptr, out_ptr, len, period, fast_period, slow_period
maaq_into(inPtr, outPtr, length, 11, 2, 30);

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

maaq_free(inPtr, length);
maaq_free(outPtr, length);

console.log('MAAQ values:', maaqValues);
Batch Processing

Test parameter grids efficiently with unified batch output:

import { maaq_batch, maaq_batch_metadata_js } from 'vectorta-wasm';

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

// Define parameter sweep ranges (three axes)
const p = [9, 20, 1];
const f = [1, 5, 1];
const s = [20, 50, 5];

// Get metadata about parameter combinations: [p1,f1,s1, p2,f2,s2, ...]
const metadata = maaq_batch_metadata_js(p[0], p[1], p[2], f[0], f[1], f[2], s[0], s[1], s[2]);
const numCombos = metadata.length / 3;

// Calculate all combinations using unified batch API
const result = maaq_batch(prices, {
  period_range: p,
  fast_period_range: f,
  slow_period_range: s,
});

// result has { values, combos, rows, cols }
console.log(result.rows, result.cols);
// Access first combo row
const firstRow = result.values.slice(0, prices.length);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators