MAC-Z VWAP (MACZ)

Parameters: fast_length = 12 | slow_length = 25 | signal_length = 9 | lengthz = 20 | length_stdev = 25 | a = 1 (-2–2) | b = 1 (-2–2) | use_lag = | gamma = 0.02

Overview

MAC-Z VWAP combines the trend-following characteristics of MACD with the mean reversion properties of Z-score to create a composite momentum oscillator that derives signals from both volume and price action. The indicator calculates ZVWAP by taking the difference between price and VWAP divided by the standard deviation over a specified window, then combines this with MACD normalized by the population standard deviation to create a volatility-adjusted momentum reading. This dual approach leverages the counter-trend component of the Z-score to adjust and improve the trend component of MACD, resulting in more accurate signals that describe actual market behavior without assumptions. The optional Laguerre filter, developed by John Ehlers for signal processing applications, can smooth the MACZ values using fourth-order polynomials that emphasize recent data while minimizing noise and lag. Weighted coefficients a and b allow traders to adjust the relative influence of the ZVWAP mean reversion component versus the normalized MACD momentum component, enabling customization for different market conditions. The final output histogram represents MACZ minus its signal line SMA, oscillating around zero where positive values suggest bullish momentum and negative values indicate bearish pressure, with the indicator remaining comparable across different timeframes and securities due to its normalization approach.

Defaults: fast=12, slow=25, signal=9, lengthz=20, length_stdev=25, a=1.0, b=1.0, use_lag=false, gamma=0.02.

Implementation Examples

Compute MACZ from slices or candles:

use vectorta::indicators::macz::{macz, MaczInput, MaczParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Price slice
let prices = vec![100.0, 101.0, 102.5, 101.7, 103.2];
let input = MaczInput::from_slice(&prices, MaczParams::default());
let out = macz(&input)?;

// Candles (uses close + volume by default)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MaczInput::with_default_candles(&candles); // source="close", defaults applied
let out = macz(&input)?;

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

API Reference

Input Methods
// From price slice
MaczInput::from_slice(&[f64], MaczParams) -> MaczInput
MaczInput::from_slice_with_volume(&[f64], &[f64], MaczParams) -> MaczInput

// From candles (uses provided source and volume)
MaczInput::from_candles(&Candles, &str, MaczParams) -> MaczInput
MaczInput::from_candles_with_volume(&Candles, &str, &[f64], MaczParams) -> MaczInput

// Defaults
MaczInput::with_default_candles(&Candles) -> MaczInput      // source="close", defaults
MaczInput::with_default_slice(&[f64]) -> MaczInput
// Back-compat alias
MaczInput::with_default_candles_auto_volume(&Candles) -> MaczInput
Parameters Structure
pub struct MaczParams {
    pub fast_length: Option<usize>,   // Default: 12 (> 0)
    pub slow_length: Option<usize>,   // Default: 25 (> 0)
    pub signal_length: Option<usize>, // Default: 9  (> 0)
    pub lengthz: Option<usize>,       // Default: 20 (> 0)
    pub length_stdev: Option<usize>,  // Default: 25 (> 0)
    pub a: Option<f64>,               // Default: 1.0 (−2.0..=2.0)
    pub b: Option<f64>,               // Default: 1.0 (−2.0..=2.0)
    pub use_lag: Option<bool>,        // Default: false
    pub gamma: Option<f64>,           // Default: 0.02 (0 ≤ gamma < 1)
}
Output Structure
pub struct MaczOutput {
    pub values: Vec<f64>, // Histogram: MACZ − SMA(MACZ, signal_length)
}
Validation, Warmup & NaNs
  • fast_length, slow_length, signal_length, lengthz, length_stdev must be > 0; otherwise MaczError::InvalidPeriod.
  • Must have at least max(fast, slow, lengthz, length_stdev) valid points after the first finite value; else MaczError::NotEnoughValidData.
  • a and b must be within [−2.0, 2.0] (inclusive) or MaczError::InvalidA/InvalidB.
  • gamma must satisfy 0 ≤ gamma < 1 or MaczError::InvalidGamma.
  • Warmup: histogram starts at warm_hist = first_non_nan + max(slow,lengthz,length_stdev) + signal_length − 1; indices before are NaN.
  • VWAP uses candle volume when provided; without volume, VWAP ≡ SMA(price, lengthz). Volume length must match price length.
  • Strict windows: any NaN inside fast/slow/lengthz/length_stdev windows yields NaN for that step.
  • Streaming: update() returns None during warmup; can return Some(NaN) until the signal window is fully populated.
Error Handling
use vectorta::indicators::macz::{macz, MaczError};

match macz(&input) {
    Ok(output) => consume(output.values),
    Err(MaczError::EmptyInputData) => eprintln!("No data provided"),
    Err(MaczError::AllValuesNaN) => eprintln!("All values are NaN"),
    Err(MaczError::InvalidPeriod { period, data_len }) => {
        eprintln!("Invalid period: {period}, data length: {data_len}");
    }
    Err(MaczError::NotEnoughValidData { needed, valid }) => {
        eprintln!("Need {needed} valid points, have {valid}");
    }
    Err(MaczError::InvalidGamma { gamma }) => eprintln!("Invalid gamma: {gamma}"),
    Err(MaczError::InvalidA { a }) => eprintln!("A out of range: {a}"),
    Err(MaczError::InvalidB { b }) => eprintln!("B out of range: {b}"),
    Err(MaczError::VolumeRequired) => eprintln!("Volume data required"),
    Err(MaczError::InvalidParameter { msg }) => eprintln!("Invalid parameter: {msg}"),
}

Python Bindings

Basic Usage

Compute MACZ using NumPy arrays; volume is optional:

import numpy as np
from vectorta import macz

prices = np.array([100.0, 101.0, 102.5, 101.7, 103.2])

# Defaults (fast=12, slow=25, sig=9, lengthz=20, length_stdev=25, a=1.0, b=1.0, use_lag=False, gamma=0.02)
values = macz(prices)

# With parameters and kernel selection
values = macz(
    prices,
    fast_length=12,
    slow_length=25,
    signal_length=9,
    lengthz=20,
    length_stdev=25,
    a=1.0,
    b=1.0,
    use_lag=False,
    gamma=0.02,
    kernel="auto",  # or "scalar"
)

print(values)  # NumPy array of histogram values
Streaming Real‑time Updates
from vectorta import MaczStream

stream = MaczStream(
    fast_length=12, slow_length=25, signal_length=9,
    lengthz=20, length_stdev=25, a=1.0, b=1.0,
    use_lag=False, gamma=0.02,
)

for price, volume in feed():
    hist = stream.update(price, volume)
    if hist is not None:
        process(hist)
Batch Parameter Optimization
import numpy as np
from vectorta import macz_batch

prices = np.array([...])

results = macz_batch(
    prices,
    fast_length_range=(8, 16, 2),
    slow_length_range=(20, 30, 5),
    signal_length_range=(5, 9, 2),
    lengthz_range=(16, 24, 4),
    length_stdev_range=(20, 30, 5),
    a_range=(0.5, 1.5, 0.5),
    b_range=(0.5, 1.5, 0.5),
    use_lag_range=(False, False, False),
    gamma_range=(0.02, 0.02, 0.0),
    kernel="auto",
)

print(results.keys())  # values, fast_lengths, slow_lengths, signal_lengths
CUDA Acceleration

CUDA support for MACZ is currently under development.

# Coming soon: CUDA-accelerated MACZ calculations

JavaScript/WASM Bindings

Basic Usage

Calculate MACZ in JavaScript/TypeScript:

import { macz_js } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const hist = macz_js(prices, 12, 25, 9, 20, 25, 1.0, 1.0, false, 0.02);
console.log('MACZ histogram:', hist);
Memory‑Efficient Operations

Use zero‑copy operations for large datasets:

import { macz_alloc, macz_free, macz_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* data */]);
const n = prices.length;
const inPtr = macz_alloc(n);
const outPtr = macz_alloc(n);

new Float64Array(memory.buffer, inPtr, n).set(prices);
// Args: in_ptr, out_ptr, len, fast, slow, sig, lengthz, length_stdev, a, b, use_lag, gamma
macz_into(inPtr, outPtr, n, 12, 25, 9, 20, 25, 1.0, 1.0, false, 0.02);
const hist = new Float64Array(memory.buffer, outPtr, n).slice();

macz_free(inPtr, n);
macz_free(outPtr, n);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators