Volume Adjusted Moving Average

Parameters: length = 13 | vi_factor = 0.67 | strict = true | sample_period = 0

Overview

Volume Adjusted Moving Average dynamically modulates its effective lookback period based on trading volume intensity, creating a context aware smoothing system that responds to the strength behind price movements. The indicator compares each bar's volume against an average volume threshold, using a volume increment factor to adjust the effective window length inversely with volume activity. During high volume periods when institutional participation or significant news drives trading, the shortened window makes the average more responsive to capture the move's momentum. Conversely, during low volume consolidations when price movements lack conviction, the extended window provides greater smoothing to filter meaningless fluctuations. This volume sensitivity helps traders distinguish between price changes backed by substantial activity versus those occurring on thin participation. The strict mode setting determines whether the indicator accumulates full volume increments before producing values or uses available bars up to the length parameter. Default settings use a 13 period base length with a volume increment factor of 0.67, strict accumulation enabled, and cumulative volume averaging over all historical bars for normalization.

Implementation Examples

Calculate the Volume Adjusted MA from slices or candles:

use vectorta::indicators::moving_averages::volume_adjusted_ma::{
    VolumeAdjustedMa, VolumeAdjustedMaInput, VolumeAdjustedMaParams
};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From price and volume slices
let prices = vec![100.0, 101.5, 103.0, 102.5, 104.0];
let volumes = vec![2000.0, 1800.0, 2500.0, 1700.0, 3000.0];
let params = VolumeAdjustedMaParams { length: Some(13), vi_factor: Some(0.67), strict: Some(true), sample_period: Some(0) };
let input = VolumeAdjustedMaInput::from_slices(&prices, &volumes, params);
let out = VolumeAdjustedMa(&input)?;

// From Candles with defaults (source = "close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VolumeAdjustedMaInput::with_default_candles(&candles);
let out = VolumeAdjustedMa(&input)?;

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

API Reference

Input Methods
// From price and volume slices
VolumeAdjustedMaInput::from_slices(&[f64], &[f64], VolumeAdjustedMaParams) -> VolumeAdjustedMaInput

// From candles with custom source (e.g., "close")
VolumeAdjustedMaInput::from_candles(&Candles, &str, VolumeAdjustedMaParams) -> VolumeAdjustedMaInput

// From candles with defaults (close, length=13, vi_factor=0.67, strict=true, sample_period=0)
VolumeAdjustedMaInput::with_default_candles(&Candles) -> VolumeAdjustedMaInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct VolumeAdjustedMaParams {
    pub length: Option<usize>,       // Default: 13 (must be > 0)
    pub vi_factor: Option<f64>,      // Default: 0.67 (must be > 0 and finite)
    pub strict: Option<bool>,        // Default: true
    pub sample_period: Option<usize> // Default: 0 (0 = cumulative average)
}
Output Structure
pub struct VolumeAdjustedMaOutput {
    pub values: Vec<f64>, // VAMA values
}
Validation, Warmup & NaNs
  • Price and volume slices must be non‑empty and equal length; otherwise DataLengthMismatch or Empty… errors.
  • length > 0 and length ≤ data_len; else InvalidPeriod. Need at least length valid points after first finite; else NotEnoughValidData.
  • vi_factor must be finite and > 0; else InvalidViFactor.
  • Warmup: indices [0 .. first_finite + length − 2] are NaN. With sample_period > 0, early indices before the first full window also yield NaN.
  • Streaming: update(price, volume) returns NaN during warmup; thereafter follows the batch semantics.
Error Handling
use vectorta::indicators::moving_averages::volume_adjusted_ma::VolumeAdjustedMaError;

match VolumeAdjustedMa(&input) {
    Ok(out) => process(out.values),
    Err(VolumeAdjustedMaError::EmptyInputData) => eprintln!("price slice is empty"),
    Err(VolumeAdjustedMaError::EmptyVolumeData) => eprintln!("volume slice is empty"),
    Err(VolumeAdjustedMaError::AllValuesNaN) => eprintln!("all price values are NaN"),
    Err(VolumeAdjustedMaError::InvalidPeriod { period, data_len }) => eprintln!("invalid length {} for data_len {}", period, data_len),
    Err(VolumeAdjustedMaError::NotEnoughValidData { needed, valid }) => eprintln!("need {} valid points, have {}", needed, valid),
    Err(VolumeAdjustedMaError::InvalidViFactor { vi_factor }) => eprintln!("invalid vi_factor {}", vi_factor),
    Err(VolumeAdjustedMaError::DataLengthMismatch { price_len, volume_len }) => eprintln!("price={} volume={}", price_len, volume_len),
}

Python Bindings

Basic Usage

Compute from NumPy arrays of price and volume:

import numpy as np
from vectorta import VolumeAdjustedMa

prices = np.array([100.0, 101.5, 103.0, 102.5, 104.0])
volumes = np.array([2000.0, 1800.0, 2500.0, 1700.0, 3000.0])

# Defaults: length=13, vi_factor=0.67, strict=True, sample_period=0
result = VolumeAdjustedMa(prices, volumes)

# Custom parameters + kernel selection ("auto", "avx2", "avx512" if available)
result = VolumeAdjustedMa(prices, volumes, length=20, vi_factor=0.6, strict=True, sample_period=0, kernel="auto")

print(result)
Streaming Real-time Updates
from vectorta import VolumeAdjustedMaStream

stream = VolumeAdjustedMaStream(length=13, vi_factor=0.67, strict=True, sample_period=0)
for (price, volume) in price_volume_feed:
    vama = stream.update(price, volume)
    # During warmup, vama may be NaN
    handle(vama)
Batch Parameter Optimization
import numpy as np
from vectorta import VolumeAdjustedMa_batch

prices = np.array([...], dtype=float)
volumes = np.array([...], dtype=float)

length_range = (10, 30, 5)
vi_factor_range = (0.5, 0.8, 0.1)
sample_period_range = (0, 0, 0)  # keep cumulative avg

results = VolumeAdjustedMa_batch(
    prices,
    volumes,
    length_range=length_range,
    vi_factor_range=vi_factor_range,
    sample_period_range=sample_period_range,
    strict=True,
    kernel="auto",
)

# Results is a dict: values (2D), lengths, vi_factors, sample_periods, stricts
print(results['values'].shape)
CUDA Acceleration

CUDA helpers are available for batch and multi‑series flows (when built with CUDA):

import numpy as np
from vectorta import (
    volume_adjusted_ma_cuda_batch_dev,
    volume_adjusted_ma_cuda_many_series_one_param_dev,
)

# One series, many parameter combos
prices_f32 = np.asarray(prices, dtype=np.float32)
volumes_f32 = np.asarray(volumes, dtype=np.float32)
out_dev = volume_adjusted_ma_cuda_batch_dev(
    prices_f32,
    volumes_f32,
    length_range=(10, 30, 5),
    vi_factor_range=(0.5, 0.8, 0.1),
    sample_period_range=(0, 0, 0),
    strict=True,
    device_id=0,
)

# Many series (time-major), one parameter set
tm_prices = np.random.rand(1024, 64).astype(np.float32)
tm_vols = np.random.rand(1024, 64).astype(np.float32)
out_dev = volume_adjusted_ma_cuda_many_series_one_param_dev(
    tm_prices, tm_vols,
    length=13, vi_factor=0.67, strict=True, sample_period=0,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Compute in JS/TS using WASM exports:

import {
  volume_adjusted_ma_js,
  volume_adjusted_ma_unified_js,
} from 'vectorta-wasm';

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

// Direct call with explicit params
const values = volume_adjusted_ma_js(prices, volumes, 13, 0.67, true, 0);

// Unified variant (optional parameters), returns {{ values }} object
const out = volume_adjusted_ma_unified_js(prices, volumes, 13, 0.67, true, 0);
const arr = (await out).values;
Memory-Efficient Operations
import { volume_adjusted_ma_alloc, volume_adjusted_ma_free, volume_adjusted_ma_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* ... */]);
const volumes = new Float64Array([/* ... */]);
const len = prices.length;

const pPtr = volume_adjusted_ma_alloc(len);
const vPtr = volume_adjusted_ma_alloc(len);
const oPtr = volume_adjusted_ma_alloc(len);

new Float64Array(memory.buffer, pPtr, len).set(prices);
new Float64Array(memory.buffer, vPtr, len).set(volumes);

// in_ptr_price, in_ptr_volume, out_ptr, len, length, vi_factor, strict, sample_period
await volume_adjusted_ma_into(pPtr, vPtr, oPtr, len, 13, 0.67, true, 0);
const out = new Float64Array(memory.buffer, oPtr, len).slice();

volume_adjusted_ma_free(pPtr, len);
volume_adjusted_ma_free(vPtr, len);
volume_adjusted_ma_free(oPtr, len);
Batch Processing
import { volume_adjusted_ma_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* ... */]);
const volumes = new Float64Array([/* ... */]);
const cfg = {
  length_range: [10, 30, 5],
  vi_factor_range: [0.5, 0.8, 0.1],
  sample_period_range: [0, 0, 0],
  strict: true,
};

// Returns { values: Float64Array, combos: VolumeAdjustedMaParams[], rows, cols }
const res = volume_adjusted_ma_batch(prices, volumes, cfg);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators