Volume Weighted MACD (VWMACD)
fast_period = 12 | slow_period = 26 | signal_period = 9 | fast_ma_type = sma | slow_ma_type = sma | signal_ma_type = ema Overview
Buff Dormeier created the Volume Weighted MACD in 2000 as a straightforward yet powerful enhancement to Gerald Appel's original MACD indicator by replacing exponential moving averages with volume-weighted moving averages. This modification makes the indicator more responsive to price movements that occur with significant volume, filtering out low-volume noise that often creates false signals in traditional MACD. The indicator calculates the difference between fast and slow volume-weighted moving averages to form the MACD line, then applies an exponential moving average to generate the signal line, keeping it unweighted since the MACD line already incorporates volume. When volume confirms price movements, the VWMACD responds more quickly than its traditional counterpart, providing earlier signals at potential turning points. The histogram visualizes momentum shifts by displaying the difference between the MACD and signal lines, expanding during strong trends and contracting at potential reversals. By weighting price changes according to their accompanying volume, VWMACD reveals whether institutional money supports price movements, making it particularly effective for identifying genuine breakouts versus low-volume drift that often precedes reversals.
Implementation Examples
Get started with VWMACD using price and volume data:
use vectorta::indicators::vwmacd::{vwmacd, VwmacdInput, VwmacdParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with slices (close and volume of equal length)
let close = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let volume = vec![1200.0, 1500.0, 1300.0, 2000.0, 1800.0, 1600.0];
let params = VwmacdParams {
fast_period: Some(12),
slow_period: Some(26),
signal_period: Some(9),
fast_ma_type: Some("sma".to_string()),
slow_ma_type: Some("sma".to_string()),
signal_ma_type: Some("ema".to_string()),
};
let input = VwmacdInput::from_slices(&close, &volume, params);
let out = vwmacd(&input)?; // -> VwmacdOutput { macd, signal, hist }
// Using with Candles (defaults: sources "close"/"volume", 12/26 VWMAs, 9 EMA signal)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VwmacdInput::with_default_candles(&candles);
let out = vwmacd(&input)?;
// Access the components
for i in 0..out.macd.len() {
println!("macd={}, signal={}, hist={}", out.macd[i], out.signal[i], out.hist[i]);
} API Reference
Input Methods ▼
// From close/volume slices (equal length)
VwmacdInput::from_slices(&[f64], &[f64], VwmacdParams) -> VwmacdInput
// From candles with custom sources
VwmacdInput::from_candles(&Candles, &str /*close_source*/, &str /*volume_source*/, VwmacdParams) -> VwmacdInput
// From candles with defaults (sources: "close"/"volume"; periods 12/26; signal 9 EMA)
VwmacdInput::with_default_candles(&Candles) -> VwmacdInput Parameters Structure ▼
pub struct VwmacdParams {
pub fast_period: Option<usize>, // Default: 12
pub slow_period: Option<usize>, // Default: 26
pub signal_period: Option<usize>, // Default: 9
pub fast_ma_type: Option<String>, // Default: "sma" (for fast VWMA)
pub slow_ma_type: Option<String>, // Default: "sma" (for slow VWMA)
pub signal_ma_type: Option<String>,// Default: "ema" (signal)
} Output Structure ▼
pub struct VwmacdOutput {
pub macd: Vec<f64>, // VWMACD line: VWMA_fast - VWMA_slow
pub signal: Vec<f64>, // Signal MA of MACD (default EMA 9)
pub hist: Vec<f64>, // Histogram: MACD - Signal
} Validation, Warmup & NaNs ▼
- Inputs must be same length; otherwise
VwmacdError::InvalidPeriod. VwmacdError::AllValuesNaNif all close or all volume values areNaN.VwmacdError::InvalidPeriodif any offast,slow,signalis0or greater than data length.VwmacdError::NotEnoughValidDataif valid samples after the first finite pair are fewer thanslow.- Warmup prefixes:
macd[.. first + max(fast, slow) - 1] = NaNandsignal/hist[.. macd_warmup + signal - 1] = NaN. - Where the VWMA denominator window sums to
0.0, outputs areNaN(no error).
Error Handling ▼
use vectorta::indicators::vwmacd::{vwmacd, VwmacdError};
match vwmacd(&input) {
Ok(out) => process(out),
Err(VwmacdError::AllValuesNaN) => eprintln!("All inputs are NaN"),
Err(VwmacdError::InvalidPeriod { fast, slow, signal, data_len }) =>
eprintln!("Invalid periods: fast={}, slow={}, signal={}, len={}", fast, slow, signal, data_len),
Err(VwmacdError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid samples after first finite pair, have {}", needed, valid),
Err(VwmacdError::MaError(msg)) => eprintln!("MA error: {}", msg),
} Python Bindings
Basic Usage ▼
Calculate VWMACD using NumPy arrays of close and volume:
import numpy as np
from vectorta import vwmacd, VwmacdStream
close = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])
volume = np.array([1200.0, 1500.0, 1300.0, 2000.0, 1800.0, 1600.0])
# Returns (macd, signal, hist) arrays
macd, signal, hist = vwmacd(
close, volume,
fast=12, slow=26, signal=9,
fast_ma_type="sma", slow_ma_type="sma", signal_ma_type="ema",
kernel="auto"
)
# Streaming API
stream = VwmacdStream(fast_period=12, slow_period=26, signal_period=9,
fast_ma_type="sma", slow_ma_type="sma", signal_ma_type="ema")
for c, v in zip(close, volume):
m, s, h = stream.update(float(c), float(v))
if m is not None:
pass # handle values
Batch Processing ▼
Test multiple parameter combinations:
import numpy as np
from vectorta import vwmacd_batch
close = np.array([...])
volume = np.array([...])
result = vwmacd_batch(
close, volume,
fast_range=(8, 16, 4),
slow_range=(20, 32, 6),
signal_range=(7, 11, 2),
fast_ma_type="sma",
slow_ma_type="sma",
signal_ma_type="ema",
kernel="auto"
)
# result is a dict with flattened arrays reshaped as (rows, cols):
# macd, signal, hist, plus parameter vectors
print(result["macd"].shape, result["signal"].shape, result["hist"].shape)
print(result["fast_periods"], result["slow_periods"], result["signal_periods"]) CUDA Acceleration ▼
CUDA support for VWMACD is coming soon. APIs will follow the pattern of other CUDA-enabled indicators.
JavaScript/WASM Bindings
Basic Usage ▼
Calculate VWMACD in JavaScript/TypeScript:
import { vwmacd_js } from 'vectorta-wasm';
const close = new Float64Array([/* close prices */]);
const volume = new Float64Array([/* volumes */]);
// Returns a flattened Float64Array: [macd..., signal..., hist...] (length = 3 * N)
const flat = vwmacd_js(close, volume, 12, 26, 9, 'sma', 'sma', 'ema');
// Helper to slice components
function split(flat: Float64Array) {
const n = flat.length / 3;
return {
macd: flat.slice(0, n),
signal: flat.slice(n, 2*n),
hist: flat.slice(2*n),
};
}
const { macd, signal, hist } = split(flat);
console.log(macd[macd.length-1], signal[signal.length-1], hist[hist.length-1]); Memory-Efficient Operations ▼
Use zero-copy into pre-allocated buffers:
import { vwmacd_alloc, vwmacd_free, vwmacd_into, memory } from 'vectorta-wasm';
const n = close.length;
// Allocate WASM memory for inputs and outputs
const closePtr = vwmacd_alloc(n);
const volumePtr = vwmacd_alloc(n);
const macdPtr = vwmacd_alloc(n);
const signalPtr = vwmacd_alloc(n);
const histPtr = vwmacd_alloc(n);
// Copy inputs into WASM memory
new Float64Array(memory.buffer, closePtr, n).set(close);
new Float64Array(memory.buffer, volumePtr, n).set(volume);
// Compute directly into WASM memory
vwmacd_into(closePtr, volumePtr, macdPtr, signalPtr, histPtr, n, 12, 26, 9, 'sma', 'sma', 'ema');
// Read results
const macd = new Float64Array(memory.buffer, macdPtr, n).slice();
const signal = new Float64Array(memory.buffer, signalPtr, n).slice();
const hist = new Float64Array(memory.buffer, histPtr, n).slice();
// Free allocations
vwmacd_free(closePtr, n);
vwmacd_free(volumePtr, n);
vwmacd_free(macdPtr, n);
vwmacd_free(signalPtr, n);
vwmacd_free(histPtr, n); Batch Processing ▼
Unified batch API for values and combos:
import { vwmacd_batch } from 'vectorta-wasm';
const cfg = {
fast_range: [8, 16, 4],
slow_range: [20, 32, 6],
signal_range: [7, 11, 2],
fast_ma_type: 'sma',
slow_ma_type: 'sma',
signal_ma_type: 'ema',
};
const { values, combos, rows, cols } = vwmacd_batch(close, volume, cfg);
// Row helper
const rowSeries = (row: number) => {
const start = row * cols;
return {
macd: Float64Array.from(values.slice(start, start + cols)),
signal: Float64Array.from(values.slice(start + rows*cols, start + rows*cols + cols)),
hist: Float64Array.from(values.slice(start + 2*rows*cols, start + 2*rows*cols + cols)),
};
};
console.log('First combo:', combos[0]);
console.log('MACD series for first combo:', rowSeries(0).macd); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05