Z-Score
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
- Deviation — rolling deviation (stddev/MAD/MedAD).
- Standard Deviation — volatility measure used in z-score.
- MAC‑Z (ZVWAP + MACD) — uses Z‑score of VWAP.
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_typetunes the mean;devtypeselects stddev/MAD/MedAD robustness.
Implementation Examples
Compute Z‑Score from a slice or candles:
use vectorta::indicators::zscore::{zscore, ZscoreInput, ZscoreParams};
use vectorta::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 > 0andperiod ≤ len; otherwiseZscoreError::InvalidPeriod.- Requires at least
periodvalid points after the first finite value; otherwiseZscoreError::NotEnoughValidData. - Warmup: indices before
first + period − 1areNaN; first finite output at that index. - If the deviation at an index is
0.0orNaN, the output isNaNfor that index.
Error Handling ▼
use vectorta::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 vectorta 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 vectorta 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 vectorta 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 batch bindings available behind the cuda feature: zscore_cuda_batch_dev(data_f32, period_range, nbdev_range=(1.0,1.0,0.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 Export Code
use vectorta::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
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05