util::volatility
Four estimators, two window conventions
Section titled “Four estimators, two window conventions”The volatility utilities return standard deviations of returns, without annualization. Supply positive price levels ordered oldest first. Range estimators consume parallel OHLC arrays; daily volatility consumes timestamped closes and returns timestamped estimates. The implementation and reference tests define the conventions below.
| Function | Inputs | Smoothing | Output and warm-up |
|---|---|---|---|
get_daily_vol | Timestamps and close | Adjusted, unbiased EWM standard deviation; lookback is a span | Omits bars without an eligible earlier close; first returned estimate is NaN |
get_parkinson_vol | High, low | Rolling window bars | Input length; first window - 1 values NaN |
get_garman_class_vol | Open, high, low, close | Rolling window bars | Input length; first window - 1 values NaN |
get_yang_zhang_vol | Open, high, low, close | Rolling window bars plus previous close | Input length; first window values NaN |
Use daily volatility for a timestamped target such as a labeling barrier. Use
range estimates to describe dispersion within OHLC bars. Parkinson and
Garman-Klass omit overnight gaps; Yang-Zhang includes a previous-close-to-open
term. The public name get_garman_class_vol retains the historical spelling,
although the estimator is Garman-Klass.
Daily returns and timestamp alignment
Section titled “Daily returns and timestamp alignment”AFML §3.3, Snippet 3.1 selects the last close strictly before day: , where . An exact timestamp match one day ago is excluded. Consequently, regularly spaced daily observations produce two-day returns after omitting the first two rows. This is the implemented search convention, not an ordinary adjacent daily percentage change.
With , returns receive weights . The weighted mean is and the unbiased variance is
One observation cannot estimate this variance. Span 1 also leaves only one positive weight, so every returned estimate is NaN. Span 0 or fewer than two input bars returns an empty sequence.
import mathfrom datetime import datetime, timedeltafrom openquant import volatility
stamps = [(datetime(2024, 1, 1) + timedelta(days=i)).strftime("%Y-%m-%d %H:%M:%S") for i in range(5)]close = [100.0, 100.0, 110.0, 120.0, 121.0]result = volatility.get_daily_vol(stamps, close, lookback=3)# Eligible returns are 110/100-1, 120/100-1, and 121/110-1.assert [stamp for stamp, _ in result] == stamps[2:]assert math.isnan(result[0][1])assert math.isclose(result[1][1], math.sqrt(0.005), abs_tol=1e-12)for stamp, value in result: print(stamp, f"{value:.6f}")2024-01-03 00:00:00 nan2024-01-04 00:00:00 0.0707112024-01-05 00:00:00 0.059761Always join the returned timestamps back to the input. Positional assignment would shift the target because the output omits initial rows.
Range formulas
Section titled “Range formulas”For a trailing window of bars, the functions return the square roots of:
Yang-Zhang uses , , and :
Both variance terms subtract their window means and use divisor ; the
Rogers-Satchell term uses divisor . These are the paper’s conventions tested
in test_yang_zhang_matches_paper, rather than the older mlfinlab expression
using and uncentered sums.
import mathfrom openquant import volatility
# Synthetic identical bars: no overnight or open-to-close movement.open_ = close = [100.0] * 5high, low = [101.0] * 5, [99.0] * 5p = volatility.get_parkinson_vol(high, low, window=3)g = volatility.get_garman_class_vol(open_, high, low, close, window=3)y = volatility.get_yang_zhang_vol(open_, high, low, close, window=3)expected_p = math.log(101 / 99) / math.sqrt(4 * math.log(2))assert math.isclose(p[-1], expected_p, abs_tol=1e-12)for name, values in [("Parkinson", p), ("Garman-Klass", g), ("Yang-Zhang", y)]: print(name, "warm-up", sum(math.isnan(v) for v in values), "tail", f"{values[-1]:.6f}")Parkinson warm-up 2 tail 0.012012Garman-Klass warm-up 2 tail 0.014143Yang-Zhang warm-up 3 tail 0.013404The differences reflect the formulas, not evidence that one estimator forecasts better. All bars here are synthetic and contain no market information.
Rust range example
Section titled “Rust range example”use openquant::util::volatility::get_parkinson_vol;
let values = get_parkinson_vol(&[101.0; 5], &[99.0; 5], 3)?;assert!(values[..2].iter().all(|v| v.is_nan()));let expected = (101.0_f64 / 99.0).ln() / (4.0 * 2.0_f64.ln()).sqrt();assert!((values[4] - expected).abs() < 1e-12);Failure modes and interpretation
Section titled “Failure modes and interpretation”- Parallel array lengths must match; mismatches raise Python
ValueErroror RustInputError. Python daily-volatility timestamps must parse asYYYY-MM-DD HH:MM:SS(fractional seconds accepted). - Supply increasing timestamps yourself. Daily volatility does not sort or validate price positivity. The range estimators check lengths, not OHLC consistency; nonpositive or inconsistent prices can produce nonfinite results.
- Range windows containing NaNs remain NaN until the affected observations leave the required window. A close used by Yang-Zhang also affects the next bar’s overnight return.
- Parkinson and Garman-Klass return all NaNs for window 0; Yang-Zhang does so for windows below 2. Windows longer than the history never finish warm-up.
- Outputs are not percentages or annualized values. Calendar gaps also mean the daily estimator’s actual return horizon can exceed one day. Establish the sampling convention before scaling values or comparing estimators.
Sources and related modules
Section titled “Sources and related modules”- López de Prado (2018), Advances in Financial Machine Learning, §3.3, Snippet 3.1: daily volatility target.
- Rust implementation and Python contract: formulas, validation and output alignment.
- Volatility reference tests: range estimator references, centered Yang-Zhang variance and pure-drift case.
- Labeling consumes volatility targets; EWMA explains adjusted means; data prepares OHLCV inputs.
API reference
Section titled “API reference”Python
Section titled “Python”Signatures, parameters, return values and errors for every function: openquant.volatility.
volatility.get_daily_vol— Daily volatility: exponentially weighted standard deviation of daily returns.volatility.get_parkinson_vol— Parkinson high-low volatility over a rolling window.volatility.get_garman_class_vol— Garman-Klass volatility over a rolling window.volatility.get_yang_zhang_vol— Yang-Zhang (2000) volatility over a rolling window.
Rustdoc for this commit, with every public item of openquant::util::volatility (after a crates.io release the same pages are on docs.rs):