Historical Volatility Percentile
length = 21 | annual_length = 252 Overview
Historical Volatility Percentile starts by computing a conventional rolling historical-volatility series from returns. It then compares the current volatility value against a longer history of prior volatility readings and expresses that comparison as a percentile from 0 to 100. That makes it easier to judge whether current volatility is relatively compressed, elevated, or extreme compared with the instrument’s own recent behavior.
The indicator returns both the raw percentile series and a simple moving average of that percentile. The raw line is useful when you want the fastest possible read on volatility regime changes, while the smoothed line is more useful for filters and dashboards where you want fewer one-bar spikes. Candle input defaults to close and slice input can be used directly for precomputed series.
Defaults: Historical Volatility Percentile uses `length = 21` and `annual_length = 252`, with candle source `close`.
Implementation Examples
Compute percentile-ranked volatility from a close series or from candles.
use vector_ta::indicators::historical_volatility_percentile::{
historical_volatility_percentile,
HistoricalVolatilityPercentileInput,
HistoricalVolatilityPercentileParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let output = historical_volatility_percentile(
&HistoricalVolatilityPercentileInput::from_slice(
&close,
HistoricalVolatilityPercentileParams {
length: Some(21),
annual_length: Some(252),
},
),
)?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = historical_volatility_percentile(
&HistoricalVolatilityPercentileInput::with_default_candles(&candles),
)?;
println!("{:?}", output.hvp.last());
println!("{:?}", candle_output.hvp_sma.last()); API Reference
Input Methods ▼
// From candles
HistoricalVolatilityPercentileInput::from_candles(&Candles, &str, HistoricalVolatilityPercentileParams)
-> HistoricalVolatilityPercentileInput
// From a slice
HistoricalVolatilityPercentileInput::from_slice(&[f64], HistoricalVolatilityPercentileParams)
-> HistoricalVolatilityPercentileInput
// From candles with default params and source "close"
HistoricalVolatilityPercentileInput::with_default_candles(&Candles)
-> HistoricalVolatilityPercentileInput Parameters Structure ▼
pub struct HistoricalVolatilityPercentileParams {
pub length: Option<usize>, // default 21
pub annual_length: Option<usize>, // default 252
} Output Structure ▼
pub struct HistoricalVolatilityPercentileOutput {
pub hvp: Vec<f64>,
pub hvp_sma: Vec<f64>,
} Validation, Warmup & NaNs ▼
- Input must not be empty and must contain at least one valid source value.
lengthmust fit the available data andannual_lengthmust be greater than0.- The indicator needs at least
length + annual_length - 1valid points before percentile output is ready. - Batch mode validates both axes and rejects unsupported kernels through
InvalidKernelForBatch. - Streaming warmup is reported as
length + annual_length - 1.
Builder, Streaming & Batch APIs ▼
// Builder
HistoricalVolatilityPercentileBuilder::new()
.length(usize)
.annual_length(usize)
.kernel(Kernel)
.apply(&Candles)
HistoricalVolatilityPercentileBuilder::new()
.apply_slice(&[f64])
.into_stream()
// Stream
HistoricalVolatilityPercentileStream::try_new(HistoricalVolatilityPercentileParams)
HistoricalVolatilityPercentileStream::update(f64) -> Option<(f64, f64)>
HistoricalVolatilityPercentileStream::get_warmup_period() -> usize
// Batch
HistoricalVolatilityPercentileBatchBuilder::new()
.length_range(usize, usize, usize)
.annual_length_range(usize, usize, usize)
.kernel(Kernel)
.apply_slice(&[f64]) Error Handling ▼
pub enum HistoricalVolatilityPercentileError {
EmptyInputData,
AllValuesNaN,
InvalidLength { length: usize, data_len: usize },
InvalidAnnualLength { annual_length: usize },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
InvalidInput(&'static str),
} Python Bindings
Python exposes a scalar function, a stream class, and a batch sweep. The scalar and stream paths return the raw percentile and the smoothed percentile. Batch returns reshaped `hvp` and `hvp_sma` matrices plus the tested lengths and annual lengths.
import numpy as np
from vector_ta import (
historical_volatility_percentile,
historical_volatility_percentile_batch,
HistoricalVolatilityPercentileStream,
)
data = np.asarray(close_values, dtype=np.float64)
hvp, hvp_sma = historical_volatility_percentile(
data,
length=21,
annual_length=252,
kernel="auto",
)
stream = HistoricalVolatilityPercentileStream(length=21, annual_length=252)
print(stream.update(float(data[-1])))
batch = historical_volatility_percentile_batch(
data,
length_range=(14, 21, 7),
annual_length_range=(126, 252, 126),
kernel="auto",
)
print(batch["hvp"].shape)
print(batch["annual_lengths"]) JavaScript/WASM Bindings
The WASM layer exposes scalar, batch, and into-buffer helpers. The scalar binding returns an object with `hvp` and `hvp_sma`. The batch binding returns flattened `hvp` and `hvp_sma` arrays, the parameter combos, and the matrix dimensions.
import init, {
historical_volatility_percentile_js,
historical_volatility_percentile_batch_js,
} from "@vectoralpha/vector_ta";
await init();
const single = historical_volatility_percentile_js(close, 21, 252);
console.log(single.hvp);
console.log(single.hvp_sma);
const batch = historical_volatility_percentile_batch_js(close, {
length_range: [14, 21, 7],
annual_length_range: [126, 252, 126],
});
console.log(batch.rows, batch.cols);
console.log(batch.combos); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)