Jurik Moving Average (JMA)
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 > 0andperiod ≤ len(data)orJmaError::InvalidPeriod.- Requires at least
periodvalid points after the first finite value, elseJmaError::NotEnoughValidData. - All-NaN input yields
JmaError::AllValuesNaN. Output buffer mismatches yieldJmaError::InvalidOutputBuffer. phasemust be finite; non-finite producesJmaError::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:
updatereturnsNoneuntil the first finite input; subsequentNaNs 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
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