Volume Weighted Moving Average (VWMA)

Parameters: period = 20

Overview

The Volume Weighted Moving Average (VWMA) incorporates trading volume into moving average calculations by weighting each price point by its session volume across the lookback window. Unlike traditional moving averages that treat all periods equally, VWMA multiplies each price by its volume, sums these products, then divides by total volume over the period. This volume weighting ensures that prices accompanied by heavy trading activity exert greater influence on the average than those occurring during quiet periods. The resulting line tracks not just price movement but also the conviction behind that movement as measured by participation levels. When VWMA diverges significantly from simple or exponential moving averages, it reveals that volume distribution differs from price distribution, potentially signaling accumulation or distribution phases. Traders use VWMA crossovers and price interactions similarly to other moving averages but with added confidence from volume confirmation. The default 20-period setting aligns with standard moving average analysis while allowing the volume weighting to highlight institutional activity and meaningful price levels supported by actual trading interest.

Defaults: period = 20.

Implementation Examples

Compute VWMA from slices or Candles:

use vectorta::indicators::vwma::{vwma, VwmaInput, VwmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using price and volume data slices
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let volumes = vec![1200.0, 1600.0, 900.0, 2000.0, 1800.0, 1500.0];
let params = VwmaParams { period: Some(20) };
let input = VwmaInput::from_slice(&prices, &volumes, params);
let result = vwma(&input)?;

// Using Candles with default params (source="close", period=20)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VwmaInput::with_default_candles(&candles);
let result = vwma(&input)?;

// Access the VWMA values
for value in result.values {
    println!("VWMA: {}", value);
}

API Reference

Input Methods
// From price and volume slices
VwmaInput::from_slice(&[f64], &[f64], VwmaParams) -> VwmaInput

// From candles with custom source for prices (volume is taken from candles)
VwmaInput::from_candles(&Candles, &str, VwmaParams) -> VwmaInput

// From candles plus an explicit price slice (volume from candles)
VwmaInput::from_candles_plus_prices(&Candles, &[f64], VwmaParams) -> VwmaInput

// From candles with defaults (source="close", period=20)
VwmaInput::with_default_candles(&Candles) -> VwmaInput
Parameters Structure
pub struct VwmaParams {
    pub period: Option<usize>, // Default: 20
}
Output Structure
pub struct VwmaOutput {
    pub values: Vec<f64>, // VWMA values
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data_len; otherwise VwmaError::InvalidPeriod.
  • Price and volume lengths must match; otherwise VwmaError::PriceVolumeMismatch.
  • Searches for the first index where both price and volume are finite; if none, VwmaError::AllValuesNaN.
  • There must be at least period valid pairs from the first finite pair; otherwise VwmaError::NotEnoughValidData.
  • Warmup: indices before first + period − 1 are NaN. Streaming returns None until the window fills.
Error Handling
use vectorta::indicators::vwma::{vwma, VwmaError};

match vwma(&input) {
    Ok(output) => process_results(output.values),
    Err(VwmaError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for data length {}", period, data_len),
    Err(VwmaError::PriceVolumeMismatch { price_len, volume_len }) =>
        eprintln!("Mismatched lengths: prices={}, volumes={}", price_len, volume_len),
    Err(VwmaError::AllValuesNaN) =>
        eprintln!("All price-volume pairs are NaN"),
    Err(VwmaError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid pairs, only {} available", needed, valid),
}

Python Bindings

Basic Usage

Compute VWMA from NumPy arrays of prices and volumes:

import numpy as np
from vectorta import vwma

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=np.float64)
volumes = np.array([1200.0, 1600.0, 900.0, 2000.0, 1800.0, 1500.0], dtype=np.float64)

# period is required; kernel is optional: 'auto' | 'scalar' | 'avx2' | 'avx512'
values = vwma(prices, volumes, period=20, kernel="auto")
print(values)
Streaming

Incremental updates with (price, volume) tuples:

from vectorta import VwmaStream

stream = VwmaStream(period=20)
for (price, volume) in data_stream:
    vw = stream.update(price, volume)
    if vw is not None:
        print("VWMA:", vw)
Batch Parameter Optimization

Sweep multiple periods in one pass:

import numpy as np
from vectorta import vwma_batch

prices = np.asarray([...], dtype=np.float64)
volumes = np.asarray([...], dtype=np.float64)

# (start, end, step)
result = vwma_batch(prices, volumes, period_range=(10, 30, 5), kernel="auto")

print(result['values'].shape)  # (num_periods, len(prices))
print(result['periods'])       # list of tested periods
CUDA Acceleration

CUDA APIs are available behind optional features:

# Requires build with CUDA + Python features enabled
from vectorta import vwma_cuda_batch_dev, vwma_cuda_many_series_one_param_dev
import numpy as np

# 1) One series, many periods (device output wrapper)
prices_f32 = np.asarray([...], dtype=np.float32)
vols_f32 = np.asarray([...], dtype=np.float32)
dev = vwma_cuda_batch_dev(prices_f32, vols_f32, period_range=(10, 30, 1), device_id=0)

# 2) Many series (time-major), one period
data_tm_f32 = np.asarray([...], dtype=np.float32)  # shape [T, N]
vol_tm_f32 = np.asarray([...], dtype=np.float32)   # shape [T, N]
dev2 = vwma_cuda_many_series_one_param_dev(data_tm_f32, vol_tm_f32, period=20, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Calculate VWMA in JavaScript/TypeScript:

import { vwma_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const volumes = new Float64Array([1200, 1600, 900, 2000, 1800, 1500]);

// period = 20 by convention
const values = vwma_js(prices, volumes, 20);
console.log('VWMA values:', values);
Memory-Efficient Operations

Use zero-copy buffers with alloc/into:

import { vwma_alloc, vwma_free, vwma_into, memory } from 'vectorta-wasm';

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

const pPtr = vwma_alloc(len);
const vPtr = vwma_alloc(len);
const outPtr = vwma_alloc(len);

// Copy inputs into WASM memory
new Float64Array(memory.buffer, pPtr, len).set(prices);
new Float64Array(memory.buffer, vPtr, len).set(volumes);

// Compute into pre-allocated buffer
vwma_into(pPtr, vPtr, outPtr, len, 20);

// Read results
const out = new Float64Array(memory.buffer, outPtr, len).slice();

// Free buffers
vwma_free(pPtr, len);
vwma_free(vPtr, len);
vwma_free(outPtr, len);

console.log('VWMA values:', out);
Batch Processing

Sweep period values and reshape results:

import { vwma_batch_js, vwma_batch_metadata_js } from 'vectorta-wasm';

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

const start = 10, end = 30, step = 5; // 10, 15, 20, 25, 30

// Metadata returns tested periods
const periods = vwma_batch_metadata_js(start, end, step);
const numCombos = periods.length;

// Flat matrix: [row1..., row2..., ...]
const flat = vwma_batch_js(prices, volumes, start, end, step);

// Reshape into rows
const rows = [] as Float64Array[];
for (let i = 0; i < numCombos; i++) {
  const s = i * prices.length;
  rows.push(flat.slice(s, s + prices.length));
}

console.log('Periods:', periods);
console.log('VWMA[period=20] row:', rows[periods.indexOf(20)]);

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.02× slower than Tulip C in this benchmark.

Loading chart...

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

Related Indicators