Predictive Moving Average (PMA)
Overview
The Predictive Moving Average applies John Ehlers' cascade of two seven point weighted moving averages to produce a prediction line that anticipates price movements while filtering market noise. The indicator computes a first weighted moving average using triangular weights over seven bars, then applies the same weighting scheme to those results, and finally calculates the prediction as twice the first smoothed value minus the double smoothed value. This extrapolation technique projects the smoothed trend forward by one period, creating a line that responds quickly to genuine trend changes while rejecting random fluctuations. A four point weighted trigger line of the prediction provides signal confirmation and helps identify momentum shifts. Traders watch for prediction line crossovers with the trigger to enter positions, with the prediction leading price by design to provide early warnings of directional changes. The fixed internal parameters eliminate curve fitting concerns while delivering consistent results across different instruments and timeframes, though the short smoothing window makes PMA most effective on intraday charts where responsiveness matters more than long term stability.
Defaults: PMA uses fixed internal windows and weights (no user parameters).
Implementation Examples
Get PMA prediction and trigger in a few lines:
use vectorta::indicators::pma::{pma, PmaInput, PmaParams};
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, 106.0, 107.0];
let input = PmaInput::from_slice(&prices, PmaParams {});
let result = pma(&input)?;
// result.predict and result.trigger match input length
println!("predict[7] = {}", result.predict[7]);
// Using with Candles (default source = "close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = PmaInput::with_default_candles(&candles);
let result = pma(&input)?; API Reference
Input Methods ▼
// From price slice
PmaInput::from_slice(&[f64], PmaParams) -> PmaInput
// From candles with custom source
PmaInput::from_candles(&Candles, &str, PmaParams) -> PmaInput
// From candles with default params (source = "close")
PmaInput::with_default_candles(&Candles) -> PmaInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct PmaParams; // No tunable fields; Default = PmaParams {} Output Structure ▼
pub struct PmaOutput {
pub predict: Vec<f64>, // predictive line
pub trigger: Vec<f64>, // weighted trigger of prediction
} Validation, Warmup & NaNs ▼
- Errors:
PmaError::EmptyData,PmaError::AllValuesNaN,PmaError::NotEnoughValidData { valid }. - Requires at least
7valid points after the first finite value; otherwiseNotEnoughValidData. - Warmup: indices before
first_valid_idx + 7 − 1areNaNfor both lines; prediction first becomes finite atfirst_valid_idx + 6. - Trigger becomes finite once 4 predictions exist (from
first_valid_idx + 9onward); earlier trigger values remainNaN.
Error Handling ▼
use vectorta::indicators::pma::PmaError;
match pma(&input) {
Ok(output) => {
use_output(output.predict, output.trigger);
}
Err(PmaError::EmptyData) => println!("Input data is empty"),
Err(PmaError::AllValuesNaN) => println!("All input values are NaN"),
Err(PmaError::NotEnoughValidData { valid }) =>
println!("Need at least 7 valid points after first finite; only {}", valid),
} Python Bindings
Basic Usage ▼
Calculate PMA prediction and trigger using NumPy arrays:
import numpy as np
from vectorta import pma, pma_batch, PmaStream
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5, 106.0, 107.0])
# Compute arrays (predict, trigger)
predict, trigger = pma(prices)
# Streaming usage
stream = PmaStream()
for price in prices:
pt = stream.update(price)
if pt is not None:
pred, trig = pt
# Batch API (single row; no parameter sweep)
res = pma_batch(prices)
print(res['values'].shape, res['rows'], res['cols']) CUDA Acceleration ▼
CUDA support for PMA is currently under development.
# Coming soon: CUDA-accelerated PMA calculations JavaScript/WASM Bindings
Basic Usage ▼
Calculate PMA in JavaScript/TypeScript:
import { pma_js } from 'vectorta-wasm';
// Price data as Float64Array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5, 106.0, 107.0]);
// Flat array: [predict..., trigger...]
const flat = pma_js(prices);
const len = prices.length;
const predict = flat.slice(0, len);
const trigger = flat.slice(len); Memory-Efficient Operations ▼
Use zero-copy operations with explicit buffers:
import { pma_alloc, pma_free, pma_into, memory } from 'vectorta-wasm';
const prices = new Float64Array(/* your data */);
const n = prices.length;
// Allocate WASM memory for input and outputs
const inPtr = pma_alloc(n);
const predPtr = pma_alloc(n);
const trigPtr = pma_alloc(n);
// Copy input data
new Float64Array(memory.buffer, inPtr, n).set(prices);
// Compute (writes directly into predict/trigger buffers)
await pma_into(inPtr, predPtr, trigPtr, n);
// Read results
const predict = new Float64Array(memory.buffer, predPtr, n).slice();
const trigger = new Float64Array(memory.buffer, trigPtr, n).slice();
// Free buffers
pma_free(inPtr, n);
pma_free(predPtr, n);
pma_free(trigPtr, n); Batch Processing ▼
Compute both lines with one call:
import { pma_batch } from 'vectorta-wasm';
const prices = new Float64Array(/* historical prices */);
const out = pma_batch(prices);
// out = { predict: Float64Array, trigger: Float64Array, rows: 1, cols: n } 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