Standard Deviation (STDDEV)

Parameters: period = 5 | nbdev = 1

Overview

Standard Deviation measures the dispersion of price values around their mean over a rolling window. The indicator calculates how much individual prices deviate from the average by computing the square root of the average squared difference from the mean. Higher values indicate greater volatility and price dispersion, while lower values suggest prices are clustering tightly around the mean. Traders use standard deviation to gauge market volatility, size position risk appropriately, and identify periods of consolidation versus expansion. The indicator often serves as a core component in volatility based strategies like Bollinger Bands, where it defines the width of price channels. Default period is 5 with an nbdev multiplier of 1.0, matching common volatility measurement conventions in technical analysis.

Implementation Examples

Compute rolling standard deviation from slices or candles:

use vector_ta::indicators::stddev::{stddev, StdDevInput, StdDevParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice (defaults: period=5, nbdev=1.0)
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let input = StdDevInput::from_slice(&prices, StdDevParams::default());
let out = stddev(&input)?;

// From candles with defaults (source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = StdDevInput::with_default_candles(&candles);
let out = stddev(&input)?;

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

API Reference

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

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

// From candles with default params (close prices, period=5, nbdev=1.0)
StdDevInput::with_default_candles(&Candles) -> StdDevInput
Parameters Structure
pub struct StdDevParams {
    pub period: Option<usize>, // Default: 5
    pub nbdev: Option<f64>,    // Default: 1.0 (≥ 0)
}
Output Structure
pub struct StdDevOutput {
    pub values: Vec<f64>, // Rolling stddev × nbdev
}
Validation, Warmup & NaNs
  • period > 0, nbdev ≥ 0, nbdev must be finite; else StdDevError::InvalidPeriod / InvalidNbdev.
  • Finds first finite input index first; requires at least period valid points after first or StdDevError::NotEnoughValidData.
  • Warmup: indices < first + period − 1 are NaN. First finite output at warmup index.
  • Constant windows yield 0.0 (variance ≤ 0). Subsequent NaN inputs propagate to NaN outputs.
  • Population variance is used: σ² = E[x²] − (E[x])² with E[⋅] over the window (divide by n).
Error Handling
use vector_ta::indicators::stddev::{stddev, StdDevInput, StdDevParams, StdDevError};

match stddev(&StdDevInput::from_slice(&prices, StdDevParams::default())) {
    Ok(output) => handle(output.values),
    Err(StdDevError::EmptyInputData) => eprintln!("Input is empty"),
    Err(StdDevError::AllValuesNaN) => eprintln!("All input values are NaN"),
    Err(StdDevError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for length {}", period, data_len),
    Err(StdDevError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, have {}", needed, valid),
    Err(StdDevError::InvalidNbdev { nbdev }) =>
        eprintln!("nbdev must be non-negative and finite, got {}", nbdev),
    Err(StdDevError::MismatchedOutputLen { dst_len, expected_len }) =>
        eprintln!("Output len {} != expected {}", dst_len, expected_len),
    Err(StdDevError::InvalidKernel { msg }) =>
        eprintln!("Kernel error: {}", msg),
}

Python Bindings

Basic Usage

Calculate STDDEV in Python (population variance, nbdev scale):

import numpy as np
from vector_ta import stddev

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=np.float64)
values = stddev(prices, period=5, nbdev=1.0, kernel='auto')
print(values)
Streaming
from vector_ta import StdDevStream

stream = StdDevStream(period=20, nbdev=2.0)
for p in price_stream:
    s = stream.update(p)  # None until the window is full
    if s is not None:
        handle(s)
Batch Parameter Sweep
import numpy as np
from vector_ta import stddev_batch

prices = np.asarray(prices, dtype=np.float64)
res = stddev_batch(
    prices,
    period_range=(5, 30, 5),   # 5,10,15,20,25,30
    nbdev_range=(1.0, 3.0, 0.5),
    kernel='auto'
)

# res is a dict with keys: 'values' [rows x cols], 'periods', 'nbdevs'
values = res['values']
periods = res['periods']
nbdevs = res['nbdevs']
CUDA Acceleration

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 stddev_cuda_batch_dev, stddev_cuda_many_series_one_param_dev

# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)

dev = stddev_cuda_batch_dev(
    data_f32=data_f32,
    period_range=(5, 30, 5),
    nbdev_range=(0.5, 2.0, 0.5),
    device_id=0,
)

# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)
rows, cols = data_tm_f32.shape
data_tm_f32 = data_tm_f32.ravel()

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

JavaScript/WASM Bindings

Basic Usage

Calculate STDDEV in JavaScript/TypeScript:

import { stddev_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const values = stddev_js(prices, 20, 2.0);
console.log('STDDEV:', values);
Memory-Efficient Operations

Use zero-copy operations with preallocated WASM memory:

import { stddev_alloc, stddev_free, stddev_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const n = prices.length;
const inPtr = stddev_alloc(n);
const outPtr = stddev_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);

// Args: in_ptr, out_ptr, len, period, nbdev
stddev_into(inPtr, outPtr, n, 20, 2.0);
const out = new Float64Array(memory.buffer, outPtr, n).slice();

stddev_free(inPtr, n);
stddev_free(outPtr, n);
Batch Processing

Test parameter sweeps via a unified config:

import { stddev_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const config = {
  period_range: [5, 30, 5],   // start, end, step
  nbdev_range: [1.0, 3.0, 0.5]
};

// Returns a JSON-like object with values (flat), periods, nbdevs, rows, cols
const res = stddev_batch(prices, config);
// reshape if desired based on rows/cols

CUDA Bindings (Rust)

use vector_ta::cuda::CudaStddev;
use vector_ta::indicators::stddev::StdDevBatchRange;

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

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

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

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.03× faster than Tulip C in this benchmark.

Loading chart...

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

Related Indicators