Ultimate Moving Average (UMA)
accelerator = 1 | min_length = 5 | max_length = 50 | smooth_length = 4 Overview
Ultimate Moving Average dynamically adjusts its effective lookback period based on current price position within volatility bands, creating an intelligent adaptive smoothing system. The indicator calculates a mean and standard deviation over its maximum window, then expands its effective length when price trades near the center to filter noise during consolidation, and contracts the length when price pushes toward band extremes to react quickly during breakouts. This volatility responsive behavior is further enhanced by a momentum component that uses Money Flow Index when volume data is available, or falls back to RSI otherwise, biasing the power weighted average toward recent values during strong directional moves. The adaptive mechanism allows UMA to automatically adjust between smooth trend following in quiet markets and responsive tracking during dynamic price action, eliminating the need for manual parameter optimization as conditions change. Traders value its ability to hug trends closely during momentum phases while remaining stable during choppy periods. Default settings use an accelerator of 1.0, minimum length of 5, maximum length of 50, and output smoothing of 4 periods, optimized for balanced performance across diverse market environments.
Implementation Examples
Compute UMA from price slices or candles (defaults: accelerator=1.0, min=5, max=50, smooth=4):
use vectorta::indicators::moving_averages::uma::{uma, UmaInput, UmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Price + optional volume slices
let prices = vec![100.0, 101.0, 103.0, 102.5, 104.0, 105.5];
let volume = vec![1200.0, 1500.0, 1700.0, 1600.0, 1800.0, 2100.0];
let params = UmaParams { accelerator: Some(1.0), min_length: Some(5), max_length: Some(50), smooth_length: Some(4) };
let input = UmaInput::from_slice(&prices, Some(&volume), params);
let out = uma(&input)?;
// Using Candles (volume automatically provided; source defaults to "close" via with_default_candles)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = UmaInput::with_default_candles(&candles);
let out = uma(&input)?;
// Access UMA values
for v in out.values { println!("UMA: {}", v); } API Reference
Input Methods ▼
// From slices (volume optional; enables MFI if provided)
UmaInput::from_slice(&[f64], Option<&[f64]>, UmaParams) -> UmaInput
// From candles with custom source (volume auto-wired)
UmaInput::from_candles(&Candles, &str, UmaParams) -> UmaInput
// From candles with defaults (close, accelerator=1.0, min=5, max=50, smooth=4)
UmaInput::with_default_candles(&Candles) -> UmaInput Parameters Structure ▼
pub struct UmaParams {
pub accelerator: Option<f64>, // Default: 1.0 (must be >= 1.0)
pub min_length: Option<usize>, // Default: 5 (must be >= 1)
pub max_length: Option<usize>, // Default: 50 (must be >= min_length and <= data_len)
pub smooth_length: Option<usize>, // Default: 4 (must be >= 1)
} Output Structure ▼
pub struct UmaOutput {
pub values: Vec<f64>, // UMA values (optionally WMA-smoothed)
} Validation, Warmup & NaNs ▼
accelerator >= 1.0,min_length >= 1,smooth_length >= 1.max_length >= 1andmin_length <= max_length; alsomax_length <= data_len.- Requires at least
max_lengthvalid points after the first finite value; elseUmaError::NotEnoughValidData. - Warmup: prefix indices up to
first + max_length - 1areNaN. Smoothing (smooth_length > 1) is applied after core UMA. - Volume optional: provided volume enables an MFI-based momentum; otherwise RSI fallback or a simplified volume-momentum is used.
Error Handling ▼
use vectorta::indicators::moving_averages::uma::{uma, UmaError};
match uma(&input) {
Ok(output) => consume(output.values),
Err(UmaError::EmptyInputData) => eprintln!("Input data is empty"),
Err(UmaError::AllValuesNaN) => eprintln!("All input values are NaN"),
Err(UmaError::InvalidMaxLength { max_length, data_len }) =>
eprintln!("max_length {} invalid for data length {}", max_length, data_len),
Err(UmaError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points after first finite, got {}", needed, valid),
Err(UmaError::InvalidAccelerator { accelerator }) =>
eprintln!("accelerator must be >= 1.0 (got {})", accelerator),
Err(UmaError::InvalidMinLength { min_length }) =>
eprintln!("min_length must be >= 1 (got {})", min_length),
Err(UmaError::InvalidSmoothLength { smooth_length }) =>
eprintln!("smooth_length must be >= 1 (got {})", smooth_length),
Err(UmaError::MinLengthGreaterThanMaxLength { min_length, max_length }) =>
eprintln!("min_length {} must be <= max_length {}", min_length, max_length),
Err(UmaError::DependencyError(e)) => eprintln!("Dependency error: {}", e),
} Python Bindings
Basic Usage ▼
Calculate UMA using NumPy arrays. Pass volume to enable MFI; otherwise RSI fallback is used:
import numpy as np
from vectorta import uma
prices = np.array([100, 101, 103, 102.5, 104, 105.5], dtype=np.float64)
volume = np.array([1200, 1500, 1700, 1600, 1800, 2100], dtype=np.float64)
# Defaults: accelerator=1.0, min_length=5, max_length=50, smooth_length=4
vals = uma(prices, accelerator=1.0, min_length=5, max_length=50, smooth_length=4,
volume=volume, kernel="auto")
print(vals) # NumPy array Streaming Real-time Updates ▼
from vectorta import UmaStream
stream = UmaStream(accelerator=1.0, min_length=5, max_length=50, smooth_length=4)
for (px, vol) in zip(price_feed, volume_feed):
val = stream.update_with_volume(px, vol)
if val is not None:
on_signal(val)
# Without volume
val = stream.update(106.0) Batch Parameter Optimization ▼
import numpy as np
from vectorta import uma_batch
prices = np.array([...], dtype=np.float64)
volume = np.array([...], dtype=np.float64)
res = uma_batch(
data=prices,
accelerator_range=(1.0, 2.0, 0.5),
min_length_range=(5, 15, 5),
max_length_range=(30, 60, 10),
smooth_length_range=(3, 6, 1),
volume=volume,
kernel="auto",
)
print(res["values"].shape) # (rows, len(prices))
print(res["accelerators"], res["min_lengths"], res["max_lengths"], res["smooth_lengths"]) CUDA Acceleration ▼
UMA includes CUDA exports when built with GPU support:
# GPU batch sweep on one series (F32 on device)
from vectorta import uma_cuda_batch_dev
res_dev = uma_cuda_batch_dev(
data_f32=prices.astype(np.float32),
accelerator_range=(1.0, 2.0, 0.5),
min_length_range=(5, 15, 5),
max_length_range=(30, 60, 10),
smooth_length_range=(3, 6, 1),
volume_f32=volume.astype(np.float32),
device_id=0,
)
# Many series (time-major) with one parameter set
from vectorta import uma_cuda_many_series_one_param_dev
tm = portfolio_prices_tm.astype(np.float32) # shape [T, N]
dev_out = uma_cuda_many_series_one_param_dev(
prices_tm_f32=tm,
accelerator=1.0,
min_length=5,
max_length=50,
smooth_length=4,
volume_tm_f32=portfolio_volume_tm.astype(np.float32),
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
Compute UMA (volume optional):
import { uma_js } from 'vectorta-wasm';
const prices = new Float64Array([/* ... */]);
const volume = new Float64Array([/* ... */]);
// args: data, accelerator, min_length, max_length, smooth_length, volume?
const result = uma_js(prices, 1.0, 5, 50, 4, volume);
console.log('UMA values:', result.values); Memory-Efficient Operations ▼
Use zero-copy helpers for large arrays:
import { uma_alloc, uma_free, uma_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* ... */]);
const len = prices.length;
const inPtr = uma_alloc(len);
const outPtr = uma_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(prices);
// in_ptr, out_ptr, len, accelerator, min_length, max_length, smooth_length
await uma_into(inPtr, outPtr, len, 1.0, 5, 50, 4);
const out = new Float64Array(memory.buffer, outPtr, len).slice();
uma_free(inPtr, len); uma_free(outPtr, len); Batch Processing ▼
Test parameter grids and inspect combos:
import { uma_batch_js } from 'vectorta-wasm';
const prices = new Float64Array([/* ... */]);
const volume = new Float64Array([/* ... */]);
// Each range: [start, end, step]
const out = uma_batch_js(
prices,
[1.0, 2.0, 0.5],
[5, 15, 5],
[30, 60, 10],
[3, 6, 1],
volume,
);
// out.values is flat [rows*len]; arrays for each parameter are provided
console.log(out.rows, out.cols);
console.log(out.accelerators, out.min_lengths, out.max_lengths, out.smooth_lengths); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05