Historical Volatility Percentile

Parameters: 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.
  • length must fit the available data and annual_length must be greater than 0.
  • The indicator needs at least length + annual_length - 1 valid 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

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

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

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators