Rolling Variance (VAR)
period = 14 | nbdev = 1 Overview
Rolling Variance quantifies the dispersion of price values around their mean over a sliding window, providing the mathematical foundation for volatility measurement in technical analysis. The indicator calculates variance using the sum of squared deviations from the mean, offering a precise statistical measure of how much prices fluctuate rather than simply trending in one direction. Unlike standard deviation which returns values in the same units as price, variance returns squared units, making it particularly useful for volatility modeling and options pricing applications. The implementation employs efficient windowed calculations using running sums and sums of squares for batch processing, while maintaining a ring buffer for constant time streaming updates. An optional nbdev scaling factor allows traders to adjust the sensitivity of variance readings to match different risk management frameworks. Higher variance values indicate greater price dispersion and uncertainty, while lower readings suggest tighter clustering and more predictable behavior. Default parameters use a 14 period window with an nbdev multiplier of 1.0, matching conventional volatility measurement standards.
Implementation Examples
Get started with VAR in just a few lines:
use vector_ta::indicators::var::{var, VarInput, VarParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = VarParams { period: Some(14), nbdev: Some(1.0) };
let input = VarInput::from_slice(&prices, params);
let result = var(&input)?;
// Using with Candles and default parameters (period=14, nbdev=1.0; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = VarInput::with_default_candles(&candles);
let result = var(&input)?;
// Access the VAR values
for value in result.values {
println!("VAR: {}", value);
} API Reference
Input Methods ▼
// From price slice
VarInput::from_slice(&[f64], VarParams) -> VarInput
// From candles with custom source
VarInput::from_candles(&Candles, &str, VarParams) -> VarInput
// From candles with default params (close prices, period=14, nbdev=1.0)
VarInput::with_default_candles(&Candles) -> VarInput Parameters Structure ▼
pub struct VarParams {
pub period: Option<usize>, // Default: 14
pub nbdev: Option<f64>, // Default: 1.0 (output scaled by nbdev²)
} Output Structure ▼
pub struct VarOutput {
pub values: Vec<f64>, // Rolling variance values (scaled)
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len, otherwiseVarError::InvalidPeriod.- At least
periodvalid points after the first finite value; elseVarError::NotEnoughValidData. nbdevmust be finite (not NaN/∞); elseVarError::InvalidNbdev.- All inputs NaN →
VarError::AllValuesNaN. - Warmup: indices before
first + period − 1areNaN(also invar_into_sliceand batch). - Streaming:
VarStream::updatereturnsNoneuntil the buffer fillsperiodsamples.
Error Handling ▼
use vector_ta::indicators::var::{var, VarError};
match var(&input) {
Ok(output) => process_results(output.values),
Err(VarError::AllValuesNaN) =>
eprintln!("All input values are NaN"),
Err(VarError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for data length {}", period, data_len),
Err(VarError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {} available", needed, valid),
Err(VarError::InvalidNbdev { nbdev }) =>
eprintln!("nbdev must be finite, got {}", nbdev),
} Python Bindings
Basic Usage ▼
Calculate VAR using NumPy arrays (defaults: period=14, nbdev=1.0):
import numpy as np
from vector_ta import var
# Prepare price data as NumPy array
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])
# Calculate VAR with defaults (14, 1.0)
result = var(prices)
# Or specify custom parameters
result = var(prices, period=20, nbdev=1.5)
# Specify kernel for performance optimization
result = var(prices, period=20, nbdev=1.5, kernel="avx2")
print(f"VAR values: {result}") Streaming Real-time Updates ▼
Process real-time price updates efficiently:
from vector_ta import VarStream
# Initialize streaming VAR calculator
stream = VarStream(period=14, nbdev=1.0)
# Process real-time price updates
for price in price_feed:
var_value = stream.update(price)
if var_value is not None:
print(f"Current VAR: {var_value}") Batch Parameter Optimization ▼
Test multiple parameter combinations for optimization:
import numpy as np
from vector_ta import var_batch
prices = np.array([...])
# Define parameter ranges
period_range = (10, 30, 5) # 10, 15, 20, 25, 30
nbdev_range = (0.5, 2.0, 0.5) # 0.5, 1.0, 1.5, 2.0
results = var_batch(
prices,
period_range=period_range,
nbdev_range=nbdev_range,
kernel="auto"
)
print(results.keys()) # values, periods, nbdevs
print(results['values'].shape)
print(results['periods'])
print(results['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 var_cuda_batch_dev, var_cuda_many_series_one_param_dev
# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)
dev = var_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 = var_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 ▼
Compute VAR in the browser or Node via WASM:
import { var_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const period = 14;
const nbdev = 1.0;
const values = var_js(prices, period, nbdev);
console.log('VAR values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for better performance with large datasets:
import { var_alloc, var_free, var_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const length = prices.length;
// Allocate WASM memory for input and output
const inPtr = var_alloc(length);
const outPtr = var_alloc(length);
// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);
// Calculate VAR directly into allocated memory
// Args: in_ptr, out_ptr, len, period, nbdev
var_into(inPtr, outPtr, length, 14, 1.0);
// Read results
const varValues = new Float64Array(memory.buffer, outPtr, length).slice();
// Free allocated memory when done
var_free(inPtr, length);
var_free(outPtr, length);
console.log('VAR values:', varValues); Batch Processing ▼
Test multiple parameter combinations efficiently:
import { var_batch_js } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Define parameter sweep ranges
const config = {
period_range: [10, 30, 5], // 10, 15, 20, 25, 30
nbdev_range: [0.5, 2.0, 0.5] // 0.5, 1.0, 1.5, 2.0
};
const out = var_batch_js(prices, config);
// out.values is a flat array of length out.rows * out.cols
// out.combos is an array of VarParams objects with period/nbdev
// Reshape by rows/cols
const matrix: number[][] = [];
for (let r = 0; r < out.rows; r++) {
const start = r * out.cols;
matrix.push(out.values.slice(start, start + out.cols));
}
// Access specific parameter combination
const combo = out.combos[0]; // { period: 10, nbdev: 0.5 }
const firstRow = matrix[0]; CUDA Bindings (Rust)
use vector_ta::cuda::CudaVar;
use vector_ta::indicators::var::VarBatchRange;
let cuda = CudaVar::new(0)?;
let data_f32: [f32] = /* ... */;
let sweep = VarBatchRange::default();
let out = cuda.var_batch_dev(&data_f32, &sweep)?;
let _ = out; Performance Analysis
Across sizes, Rust CPU runs about 1.36× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28