Volatility Adjusted Moving Average (VAMA)
base_period = 113 | vol_period = 51 | smoothing = true | smooth_type = 3 (1–3) | smooth_period = 5 Overview
Volatility Adjusted Moving Average enhances a traditional exponential moving average by incorporating real time volatility measurements to create a more responsive trend following tool. The indicator begins with a base EMA calculation, then measures deviations from this EMA over a volatility window, tracking both the highest positive and lowest negative deviations during the period. It adds half the sum of these extreme deviations to the base EMA, effectively shifting the moving average toward areas where price has been ranging most actively. This volatility adjustment helps VAMA adapt its position to reflect actual price distribution rather than just average price, making it more relevant for support and resistance identification. The indicator optionally applies a final smoothing stage using SMA, EMA, or WMA to reduce jitter in the volatility adjusted output. Traders appreciate VAMA for its ability to automatically adjust to changing volatility regimes without sacrificing the trending characteristics of exponential averages. Default parameters use a 113 period base EMA, 51 period volatility window, and enabled WMA smoothing over 5 periods, optimized for intermediate to longer term trend analysis.
Defaults: base_period=113, vol_period=51, smoothing=true, smooth_type=3 (WMA), smooth_period=5.
Implementation Examples
Compute VAMA from a price slice or candles:
use vectorta::indicators::moving_averages::volatility_adjusted_ma::{vama, VamaInput, VamaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price slice
let prices = vec![100.0, 101.2, 102.4, 101.0, 103.5];
let params = VamaParams { // defaults: base=113, vol=51, smoothing=true, type=3 (WMA), period=5
base_period: Some(113),
vol_period: Some(51),
smoothing: Some(true),
smooth_type: Some(3),
smooth_period: Some(5),
};
let input = VamaInput::from_slice(&prices, params);
let result = vama(&input)?;
// Using Candles with default parameters (source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VamaInput::with_default_candles(&candles);
let result = vama(&input)?;
// Access values
for v in result.values { println!("{}", v); } API Reference
Input Methods ▼
// From price slice
VamaInput::from_slice(&[f64], VamaParams) -> VamaInput
// From candles with explicit source
VamaInput::from_candles(&Candles, &str, VamaParams) -> VamaInput
// Default candles (source="close") with default parameters
VamaInput::with_default_candles(&Candles) -> VamaInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct VamaParams {
pub base_period: Option<usize>, // Default: 113
pub vol_period: Option<usize>, // Default: 51
pub smoothing: Option<bool>, // Default: true
pub smooth_type: Option<usize>, // Default: 3 (1=SMA, 2=EMA, 3=WMA)
pub smooth_period: Option<usize>, // Default: 5
} Output Structure ▼
#[derive(Debug, Clone)]
pub struct VamaOutput {
pub values: Vec<f64>, // VAMA values
} Validation, Warmup & NaNs ▼
base_period > 0andvol_period > 0; both must be ≤ input length, otherwiseVamaError::InvalidPeriod.- If
smoothing=true,smooth_typemust be 1, 2, or 3; otherwiseVamaError::InvalidSmoothType. - Warmup: indices before
first_valid + max(base_period, vol_period) − 1areNaN. - If all input values are
NaN, returnsVamaError::AllValuesNaN. - If valid length after first finite value is less than
max(base_period, vol_period), returnsVamaError::NotEnoughValidData. vama_into_slicerequiresdst.len() == data.len()or returnsVamaError::InvalidPeriod.
Error Handling ▼
use vectorta::indicators::moving_averages::volatility_adjusted_ma::{vama, VamaError};
match vama(&input) {
Ok(out) => process(out.values),
Err(VamaError::EmptyInputData) => eprintln!("vama: empty input"),
Err(VamaError::AllValuesNaN) => eprintln!("vama: all values NaN"),
Err(VamaError::InvalidPeriod { period, data_len }) =>
eprintln!("vama: invalid period {} for len {}", period, data_len),
Err(VamaError::NotEnoughValidData { needed, valid }) =>
eprintln!("vama: need {} valid points, have {}", needed, valid),
Err(VamaError::InvalidSmoothType { smooth_type }) =>
eprintln!("vama: invalid smooth_type {} (use 1,2,3)", smooth_type),
Err(e) => eprintln!("vama error: {}", e),
} Python Bindings
Basic Usage ▼
Compute VAMA using NumPy arrays (defaults match Rust):
import numpy as np
from vectorta import vama, vama_batch
prices = np.array([100.0, 101.2, 102.4, 101.0, 103.5])
# Defaults: base=113, vol=51, smoothing=True, smooth_type=3 (WMA), smooth_period=5
vals = vama(prices)
# Custom parameters and kernel selection
vals = vama(
prices,
base_period=113,
vol_period=51,
smoothing=True,
smooth_type=3,
smooth_period=5,
kernel='auto'
) Streaming ▼
from vectorta import VamaStream
stream = VamaStream(base_period=113, vol_period=51, smoothing=True, smooth_type=3, smooth_period=5)
for px in live_prices:
y = stream.update(px)
if y is not None:
handle(y) Batch Processing ▼
import numpy as np
from vectorta import vama_batch
prices = np.array([...], dtype=np.float64)
out = vama_batch(
prices,
base_period_range=(100, 130, 10), # 100,110,120,130
vol_period_range=(40, 60, 10), # 40,50,60
kernel='auto'
)
print(out['values'].shape) # (num_combos, len(prices))
print(out['base_periods']) # tested base periods
print(out['vol_periods']) # tested vol periods CUDA Acceleration ▼
CUDA kernels are available when built with Python+CUDA features.
import numpy as np
from vectorta import vama_cuda_batch_dev, vama_cuda_many_series_one_param_dev
# One series, many parameter combinations (device result wrapper)
prices_f32 = np.asarray(prices, dtype=np.float32)
dev_arr = vama_cuda_batch_dev(
prices_f32,
base_period_range=(100,130,10),
vol_period_range=(40,60,10),
device_id=0
)
# Many series (time-major [T, N]), one parameter set
tm = np.asarray(portfolio_tm, dtype=np.float32) # shape [T, N]
dev_arr2 = vama_cuda_many_series_one_param_dev(
tm,
base_period=113,
vol_period=51,
device_id=0
) JavaScript/WASM Bindings
Basic Usage ▼
Compute VAMA in JS/TS:
import { vama_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 101.2, 102.4, 101.0, 103.5]);
const values = vama_js(prices, 113, 51, true, 3, 5);
console.log(values); Memory-Efficient Operations ▼
Use zero-copy buffers for large datasets:
import { vama_alloc, vama_free, vama_into, memory } from 'vectorta-wasm';
const prices = new Float64Array(/* your data */);
const n = prices.length;
const inPtr = vama_alloc(n);
const outPtr = vama_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
// in_ptr, out_ptr, len, base, vol, smoothing, smooth_type, smooth_period
vama_into(inPtr, outPtr, n, 113, 51, true, 3, 5);
const out = new Float64Array(memory.buffer, outPtr, n).slice();
vama_free(inPtr, n);
vama_free(outPtr, n); Batch Processing ▼
Sweep parameter ranges and get a flat matrix + metadata:
import { vama_batch as vama_batch_js } from 'vectorta-wasm';
const prices = new Float64Array(/* historical prices */);
const config = {
base_period_range: [100, 130, 10], // start, end, step
vol_period_range: [40, 60, 10]
};
// Returns a serialized object with values (flat), combos, rows, cols
const out = vama_batch_js(prices, config);
// out.values is flat array of length rows*cols
// reshape if needed
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)