Historical Volatility Rank

Parameters: hv_length = 10 | rank_length = 364 | annualization_days = 365 | bar_days = 1

Overview

Historical Volatility Rank begins with a rolling historical-volatility calculation and then asks where the current volatility value sits inside a longer ranking window. Instead of using percentile counts, it normalizes the current reading between the rolling minimum and maximum historical-volatility values observed over that rank window. The result is a 0 to 100 regime score that is straightforward to use in filters and dashboards.

The indicator returns both the rank line and the underlying historical-volatility line. The rank is the regime indicator, while the raw volatility series remains useful when you need the absolute annualized reading. Candle input uses close by default, and both annualization days and bar-day spacing are configurable so daily, weekly, or custom spacing can be modeled explicitly.

Defaults: Historical Volatility Rank uses `hv_length = 10`, `rank_length = 52 * 7`, `annualization_days = 365.0`, and `bar_days = 1.0`.

Implementation Examples

Compute the rank line and the underlying annualized volatility series from closes or candles.

use vector_ta::indicators::historical_volatility_rank::{
    historical_volatility_rank,
    HistoricalVolatilityRankInput,
    HistoricalVolatilityRankParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = historical_volatility_rank(
    &HistoricalVolatilityRankInput::from_slice(
        &close,
        HistoricalVolatilityRankParams {
            hv_length: Some(10),
            rank_length: Some(52 * 7),
            annualization_days: Some(365.0),
            bar_days: Some(1.0),
        },
    ),
)?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = historical_volatility_rank(
    &HistoricalVolatilityRankInput::with_default_candles(&candles),
)?;

println!("{:?}", output.hvr.last());
println!("{:?}", candle_output.hv.last());

API Reference

Input Methods
// From candles
HistoricalVolatilityRankInput::from_candles(&Candles, HistoricalVolatilityRankParams)
    -> HistoricalVolatilityRankInput

// From a slice
HistoricalVolatilityRankInput::from_slice(&[f64], HistoricalVolatilityRankParams)
    -> HistoricalVolatilityRankInput

// From candles with default parameters
HistoricalVolatilityRankInput::with_default_candles(&Candles)
    -> HistoricalVolatilityRankInput
Parameters Structure
pub struct HistoricalVolatilityRankParams {
    pub hv_length: Option<usize>,            // default 10
    pub rank_length: Option<usize>,          // default 52 * 7
    pub annualization_days: Option<f64>,     // default 365.0
    pub bar_days: Option<f64>,               // default 1.0
}
Output Structure
pub struct HistoricalVolatilityRankOutput {
    pub hvr: Vec<f64>,
    pub hv: Vec<f64>,
}
Validation, Warmup & NaNs
  • Input must not be empty and must contain valid positive closes for volatility computation.
  • hv_length and rank_length must be greater than 0.
  • annualization_days and bar_days must be finite and positive.
  • Streaming exposes two warmups: get_hv_warmup_period() for the volatility line and get_hvr_warmup_period() for the rank line.
  • Batch mode validates all axes and rejects unsupported kernels through InvalidKernelForBatch.
Builder, Streaming & Batch APIs
// Builder
HistoricalVolatilityRankBuilder::new()
    .hv_length(usize)
    .rank_length(usize)
    .annualization_days(f64)
    .bar_days(f64)
    .kernel(Kernel)
    .apply_slice(&[f64])

HistoricalVolatilityRankBuilder::new()
    .apply_candles(&Candles)
    .into_stream()

// Stream
HistoricalVolatilityRankStream::try_new(HistoricalVolatilityRankParams)
HistoricalVolatilityRankStream::update(f64) -> Option<(f64, f64)>
HistoricalVolatilityRankStream::get_hv_warmup_period() -> usize
HistoricalVolatilityRankStream::get_hvr_warmup_period() -> usize

// Batch
HistoricalVolatilityRankBatchBuilder::new()
    .hv_length_range(usize, usize, usize)
    .rank_length_range(usize, usize, usize)
    .annualization_days_range(f64, f64, f64)
    .bar_days_range(f64, f64, f64)
    .kernel(Kernel)
    .apply_slice(&[f64])
Error Handling
pub enum HistoricalVolatilityRankError {
    EmptyInputData,
    AllValuesNaN,
    InvalidHvLength { hv_length: usize, data_len: usize },
    InvalidRankLength { rank_length: usize },
    NotEnoughValidData { needed: usize, valid: usize },
    InvalidAnnualizationDays { annualization_days: f64 },
    InvalidBarDays { bar_days: f64 },
    OutputLengthMismatch { expected: usize, got: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
    BatchOutputLengthMismatch { dst_len: usize, expected_len: usize },
    InvalidInput { msg: String },
}

Python Bindings

Python exposes a scalar function, a stream class, and a batch sweep. The scalar and stream paths return the rank and the underlying historical-volatility reading together. Batch returns reshaped `hvr` and `hv` matrices plus the tested HV lengths, rank lengths, annualization days, and bar-day settings.

import numpy as np
from vector_ta import (
    historical_volatility_rank,
    historical_volatility_rank_batch,
    HistoricalVolatilityRankStream,
)

data = np.asarray(close_values, dtype=np.float64)

hvr, hv = historical_volatility_rank(
    data,
    hv_length=10,
    rank_length=52 * 7,
    annualization_days=365.0,
    bar_days=1.0,
    kernel="auto",
)

stream = HistoricalVolatilityRankStream(
    hv_length=10,
    rank_length=52 * 7,
    annualization_days=365.0,
    bar_days=1.0,
)
print(stream.update(float(data[-1])))

batch = historical_volatility_rank_batch(
    data,
    hv_length_range=(10, 14, 4),
    rank_length_range=(126, 252, 126),
    annualization_days_range=(252.0, 365.0, 113.0),
    bar_days_range=(1.0, 1.0, 0.0),
    kernel="auto",
)

print(batch["hvr"].shape)
print(batch["rank_lengths"])

JavaScript/WASM Bindings

The WASM layer exposes scalar, batch, and into-buffer helpers. The scalar binding returns an object with `hvr` and `hv`. The batch binding returns flattened `hvr` and `hv` arrays plus the tested parameter combos and the matrix dimensions.

import init, {
  historical_volatility_rank_js,
  historical_volatility_rank_batch_js,
} from "@vectoralpha/vector_ta";

await init();

const single = historical_volatility_rank_js(close, 10, 52 * 7, 365.0, 1.0);
console.log(single.hvr);
console.log(single.hv);

const batch = historical_volatility_rank_batch_js(close, {
  hv_length_range: [10, 14, 4],
  rank_length_range: [126, 252, 126],
  annualization_days_range: [252.0, 365.0, 113.0],
  bar_days_range: [1.0, 1.0, 0.0],
});

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