Jurik Moving Average (JMA)

Parameters: period = 7 | phase = 0 (-100–100) | power = 6 (1–8)

Overview

The Jurik Moving Average achieves the seemingly impossible combination of minimal lag and exceptional smoothness through proprietary adaptive algorithms that dynamically adjust filtering based on market volatility and price acceleration patterns. JMA tracks price with surgical precision during trending moves while automatically increasing smoothing during choppy consolidations, eliminating the whipsaw trades that plague fixed period averages. The phase parameter allows traders to shift the average forward or backward in time, with positive phase values creating a leading indicator that anticipates price movements and negative values adding stability through additional smoothing. Meanwhile, the power parameter controls how aggressively the filter responds to price changes, with higher values creating snappier response to breakouts while lower values maintain steadier signals. Professional traders consider JMA the gold standard for adaptive moving averages because it maintains consistent behavior across all market conditions, from quiet overnight sessions to volatile news events, without requiring parameter adjustments. The indicator excels particularly in algorithmic trading systems where its smooth output reduces false signals while its minimal lag ensures timely entries and exits, making it ideal for both trend following and mean reversion strategies that demand precision timing.

Implementation Examples

Get started with JMA using slices or candles:

use vectorta::indicators::jma::{jma, JmaInput, JmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = JmaParams { period: Some(7), phase: Some(50.0), power: Some(2) };
let input = JmaInput::from_slice(&prices, params);
let out = jma(&input)?;

// From candles (defaults: period=7, phase=50, power=2, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = JmaInput::with_default_candles(&candles);
let out = jma(&input)?;

for v in out.values { println!("JMA: {}", v); }

API Reference

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

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

// From candles with default params (close, period=7, phase=50, power=2)
JmaInput::with_default_candles(&Candles) -> JmaInput
Parameters Structure
pub struct JmaParams {
    pub period: Option<usize>, // Default: 7
    pub phase: Option<f64>,    // Default: 50.0 (recommended range [-100, 100])
    pub power: Option<u32>,    // Default: 2
}
Output Structure
pub struct JmaOutput {
    pub values: Vec<f64>, // JMA values
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ len(data) or JmaError::InvalidPeriod.
  • Requires at least period valid points after the first finite value, else JmaError::NotEnoughValidData.
  • All-NaN input yields JmaError::AllValuesNaN. Output buffer mismatches yield JmaError::InvalidOutputBuffer.
  • phase must be finite; non-finite produces JmaError::InvalidPhase. Values outside [-100,100] are saturated internally.
  • Warmup: indices before the first finite input are NaN; the first valid output equals that price.
  • Streaming: update returns None until the first finite input; subsequent NaNs propagate to the output.
Error Handling
use vectorta::indicators::jma::{jma, JmaError};

match jma(&input) {
    Ok(output) => process(output.values),
    Err(JmaError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(JmaError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period {} for data length {}", period, data_len)
    }
    Err(JmaError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {} valid points, found {}", needed, valid)
    }
    Err(JmaError::InvalidPhase { phase }) => eprintln!("invalid phase: {}", phase),
    Err(JmaError::InvalidOutputBuffer { expected, actual }) => {
        eprintln!("output buffer size mismatch: expected {}, actual {}", expected, actual)
    }
}

Python Bindings

Basic Usage

Calculate JMA with NumPy arrays; optionally select a kernel:

import numpy as np
from vectorta import jma

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

# Defaults: period=7, phase=50.0, power=2
vals = jma(prices, period=7, phase=50.0, power=2)

# Specify kernel ("auto", "scalar", "avx2", "avx512") if supported
vals = jma(prices, period=7, phase=50.0, power=2, kernel="auto")

print(vals)
Streaming Real-time Updates
from vectorta import JmaStream

stream = JmaStream(period=7, phase=50.0, power=2)
for price in price_feed:
    value = stream.update(price)
    if value is not None:
        handle(value)
Batch Parameter Optimization
import numpy as np
from vectorta import jma_batch

prices = np.array([...], dtype=float)
results = jma_batch(
    prices,
    period_range=(5, 20, 5),
    phase_range=(0.0, 100.0, 50.0),
    power_range=(1, 3, 1),
    kernel="auto"
)

print(results["values"].shape)  # (num_combinations, len(prices))
print(results["periods"])      # np.array of periods
print(results["phases"])       # np.array of phases
print(results["powers"])       # np.array of powers
CUDA Acceleration

CUDA-backed JMA APIs are available when built with Python+CUDA features.

from vectorta import jma_cuda_batch_dev, jma_cuda_many_series_one_param_dev
import numpy as np

# 1) One series, many parameter combinations
prices_f32 = np.array([...], dtype=np.float32)
out_dev = jma_cuda_batch_dev(
    prices_f32,
    period_range=(5, 20, 1),
    phase_range=(0.0, 100.0, 10.0),
    power_range=(1, 3, 1),
    device_id=0
)

# 2) Many series (time-major), one parameter set
tm_prices = np.array([...], dtype=np.float32)  # shape [T, N]
grid_dev = jma_cuda_many_series_one_param_dev(
    prices_tm_f32=tm_prices,
    period=7,
    phase=50.0,
    power=2,
    device_id=0
)

JavaScript/WASM Bindings

Basic Usage

Calculate JMA in JavaScript/TypeScript:

import { jma_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// period, phase, power
const result = jma_js(prices, 7, 50.0, 2);
console.log('JMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for large arrays:

import { jma_alloc, jma_free, jma_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* data */]);
const len = prices.length;
const inPtr = jma_alloc(len);
const outPtr = jma_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(prices);
jma_into(inPtr, outPtr, len, 7, 50.0, 2);
const values = new Float64Array(memory.buffer, outPtr, len).slice();

jma_free(inPtr, len);
jma_free(outPtr, len);
Batch Processing

Test multiple parameter combinations efficiently:

import { jma_batch_js, jma_batch_metadata_js } from 'vectorta-wasm';

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

// Define sweep ranges
const ps = 5, pe = 20, pstep = 5;      // period
const f0 = 0.0, f1 = 100.0, fstep = 50.0; // phase
const qs = 1, qe = 3, qstep = 1;       // power

// Metadata encodes [period, phase, power, period, phase, power, ...]
const meta = jma_batch_metadata_js(ps, pe, pstep, f0, f1, fstep, qs, qe, qstep);
const combos = meta.length / 3;

// Flat results: concat rows for each combo
const flat = jma_batch_js(prices, ps, pe, pstep, f0, f1, fstep, qs, qe, qstep);
const cols = prices.length;
const matrix = [] as Float64Array[];
for (let i = 0; i < combos; i++) {
  const start = i * cols;
  matrix.push(flat.slice(start, start + cols));
}

// Access first combo's params
const firstPeriod = meta[0];
const firstPhase = meta[1];
const firstPower = meta[2];

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators