Z-Score

Parameters: period = 14 | ma_type = sma | nbdev = 1 | devtype = 0

Overview

The Z-Score normalizes price data by measuring how many standard deviations the current value sits from a rolling mean, creating a standardized oscillator useful for mean reversion strategies and outlier detection. The indicator calculates a moving average baseline using the specified method (SMA, EMA, or others), then measures dispersion through standard deviation, mean absolute deviation, or median absolute deviation. Dividing the difference between current price and the mean by the chosen deviation metric yields the Z-Score, typically ranging between -3 and +3 for normal market behavior. Values beyond ±2 suggest price has moved to statistical extremes relative to recent history, often signaling potential reversion opportunities. The nbdev parameter scales the deviation divisor, allowing traders to adjust sensitivity without changing the underlying period. This normalization makes Z-Score particularly valuable for comparing overbought and oversold conditions across different instruments or timeframes, as the standardized scale remains consistent regardless of absolute price levels. Mean reversion traders watch for extreme Z-Score readings to identify entry points, while trend followers may use sustained elevated readings to confirm strength. The flexible deviation type selection accommodates different market assumptions about return distributions and outlier handling.

Formula: zt = (xt − MAt) / Dt, where MA is the chosen moving average and D is the chosen deviation type (stddev/MAD/MedAD). Defaults (VectorTA): period=14, ma_type="sma", nbdev=1.0, devtype=0.

See also

Interpretation & Use

  • Zero-line: Above 0 = price above mean; below 0 = price below mean.
  • Extremes: |Z| ≥ 2 often marks statistically unusual moves; mean-reversion traders watch for fades.
  • Normalization: Compare standardized moves across symbols or regimes.
  • Choice of MA/Deviation: ma_type tunes the mean; devtype selects stddev/MAD/MedAD robustness.

Implementation Examples

Compute Z‑Score from a slice or candles:

use vector_ta::indicators::zscore::{zscore, ZscoreInput, ZscoreParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// From price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0];
let params = ZscoreParams {
    period: Some(14),
    ma_type: Some("sma".to_string()),
    nbdev: Some(1.0),
    devtype: Some(0),
};
let input = ZscoreInput::from_slice(&prices, params);
let result = zscore(&input)?;

// From candles with defaults (period=14, ma_type="sma", nbdev=1.0, devtype=0; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = ZscoreInput::with_default_candles(&candles);
let result = zscore(&input)?;

// Access Z-Score values
for v in result.values {
    println!("zscore: {}", v);
}

API Reference

Input Methods
// From price slice
ZscoreInput::from_slice(&[f64], ZscoreParams) -> ZscoreInput

// From candles with custom source
ZscoreInput::from_candles(&Candles, &str, ZscoreParams) -> ZscoreInput

// From candles with default params (close, period=14, ma_type="sma", nbdev=1.0, devtype=0)
ZscoreInput::with_default_candles(&Candles) -> ZscoreInput
Parameters Structure
pub struct ZscoreParams {
    pub period: Option<usize>,   // Default: 14
    pub ma_type: Option<String>, // Default: "sma"
    pub nbdev: Option<f64>,      // Default: 1.0
    pub devtype: Option<usize>,  // Default: 0 (stddev)
}
Output Structure
pub struct ZscoreOutput {
    pub values: Vec<f64>, // Z-Score values
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ len; otherwise ZscoreError::InvalidPeriod.
  • Requires at least period valid points after the first finite value; otherwise ZscoreError::NotEnoughValidData.
  • Warmup: indices before first + period − 1 are NaN; first finite output at that index.
  • If the deviation at an index is 0.0 or NaN, the output is NaN for that index.
Error Handling
use vector_ta::indicators::zscore::{zscore, ZscoreError};

match zscore(&input) {
    Ok(output) => process(output.values),
    Err(ZscoreError::AllValuesNaN) => eprintln!("All input values are NaN"),
    Err(ZscoreError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for data length {}", period, data_len),
    Err(ZscoreError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, only {}", needed, valid),
    Err(ZscoreError::DevError(e)) => eprintln!("Deviation error: {}", e),
    Err(ZscoreError::MaError(e)) => eprintln!("MA error: {}", e),
}

Python Bindings

Basic Usage

Function zscore(data, period=14, ma_type="sma", nbdev=1.0, devtype=0, kernel=None) -> np.ndarray

import numpy as np
# from vector_ta import zscore  # Depending on package exposure

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0], dtype=float)
zs = zscore(prices, period=14, ma_type='sma', nbdev=1.0, devtype=0)
print(zs)
Streaming
# from vector_ta import ZscoreStream

stream = ZscoreStream(period=14, ma_type='sma', nbdev=1.0, devtype=0)
for price in [100.0, 101.0, 102.0, 101.5, 103.0]:
    val = stream.update(price)
    if val is not None:
        print('zscore:', val)
Batch Sweep
# from vector_ta import zscore_batch
import numpy as np

prices = np.array([/* your data */], dtype=float)
out = zscore_batch(
    prices,
    period_range=(10, 20, 5),
    ma_type='sma',
    nbdev_range=(0.5, 2.0, 0.5),
    devtype_range=(0, 0, 0)
)

# out is a dict with 'values' shaped (rows, cols) and combo metadata
values = out['values']
periods = out['periods']
ma_types = out['ma_types']
nbdevs = out['nbdevs']
devtypes = out['devtypes']
CUDA

CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).

import numpy as np
from vector_ta import zscore_cuda_batch_dev, zscore_cuda_many_series_one_param_dev

data_f32 = np.asarray(load_data(), dtype=np.float32)

dev, meta = zscore_cuda_batch_dev(
    data_f32,
    period_range=(10, 20, 5),
    nbdev_range=(0.5, 2.0, 0.5),
    device_id=0,
)

# Many series (time-major), flattened for this API
data_tm = np.asarray(load_data_time_major_matrix(), dtype=np.float32)
rows, cols = data_tm.shape
data_tm = data_tm.ravel()

dev_tm = zscore_cuda_many_series_one_param_dev(
    data_tm_f32=data_tm,
    cols=cols,
    rows=rows,
    period=14,
    nbdev=1.0,
    device_id=0,
)

JavaScript / WASM

Basic
import { zscore_js } from 'vectorta-wasm';

const prices = new Float64Array([/* data */]);
// Args: data, period, ma_type, nbdev, devtype
const zs = zscore_js(prices, 14, 'sma', 1.0, 0);
Memory-Efficient Operations
import { zscore_alloc, zscore_free, zscore_into, memory } from 'vectorta-wasm';

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

const inPtr = zscore_alloc(n);
const outPtr = zscore_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);

// in_ptr, out_ptr, len, period, ma_type, nbdev, devtype
zscore_into(inPtr, outPtr, n, 14, 'sma', 1.0, 0);
const out = new Float64Array(memory.buffer, outPtr, n).slice();

zscore_free(inPtr, n);
zscore_free(outPtr, n);
Batch Processing
import { zscore_batch as zscore_batch_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const config = {
  period_range: [10, 20, 5],
  ma_type: 'sma',
  nbdev_range: [0.5, 2.0, 0.5],
  devtype_range: [0, 0, 0],
};

// Returns { values: f64[], combos: ZscoreParams[], rows, cols }
const res = zscore_batch_js(prices, config);
// Flattened row-major values: reshape using rows/cols as needed

CUDA Bindings (Rust)

use vector_ta::cuda::CudaZscore;
use vector_ta::indicators::zscore::ZscoreBatchRange;

let cuda = CudaZscore::new(0)?;

let data_f32: [f32] = /* ... */;
let sweep = ZscoreBatchRange::default();

let out = cuda.zscore_batch_dev(&data_f32, &sweep)?;
let _ = out;

Export Code

use vector_ta::indicators::zscore;

// Calculate ZSCORE with custom parameters
let result = zscore(&data, 14, sma, 1, 0);

// Or using the builder pattern
let result = indicators::zscore::new()
    .period(14)
    .ma_type(sma)
    .nbdev(1)
    .devtype(0)
    .calculate(&data);

This code snippet shows how to use the ZSCORE indicator with your current parameter settings.

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators