Volatility Signature

Two-scale realized vol, noise variance estimation, and optimal sampling frequency

Realized volatility from high-frequency data is biased upward by microstructure noise (bid-ask bounce, discrete ticks, stale quotes). The volatility signature plot reveals this bias, and the two-scale realized volatility estimator (Zhang-Mykland-Ait-Sahalia) corrects for it.

API

Volatility signature plot

Compute realized vol at multiple sampling frequencies. At high frequencies the estimate is inflated by noise; it stabilizes as frequency decreases.

python
result = hz.volatility_signature(prices, freqs=[1, 2, 5, 10, 30, 60, 300])
# prices: list of (timestamp, price) tuples
# freqs: sampling intervals in seconds
# Returns: list of (frequency, realized_vol) tuples

for freq, vol in result:
    print(f"{freq:>4}s: {vol:.4f}")

# Output (typical):
#    1s: 0.2834    <-- inflated by noise
#    2s: 0.2401
#    5s: 0.2012
#   10s: 0.1856
#   30s: 0.1791
#   60s: 0.1782    <-- stabilizes here
#  300s: 0.1775

Two-scale realized volatility

The TSRV estimator combines a fast-scale and slow-scale estimator to cancel out the noise bias. Returns an unbiased vol estimate directly.

python
vol = hz.two_scale_realized_vol(prices)
# prices: list of (timestamp, price) tuples
# Returns: float -- noise-corrected annualized volatility

Optimal sampling frequency

python
freq = hz.optimal_sampling(prices)
# Returns: float -- optimal sampling interval in seconds

Practical usage

Corrected vol for position sizing

python
# Naive RV from 1-second bars is biased upward
naive_vol = hz.volatility_signature(tick_data, freqs=[1])[0][1]

# TSRV gives the corrected estimate
true_vol = hz.two_scale_realized_vol(tick_data)

print(f"Naive: {naive_vol:.4f}, Corrected: {true_vol:.4f}")
# Using naive vol would make you size positions too small (overestimating risk)

Noise-aware signal filtering

python
noise = hz.noise_variance(tick_data)
signal_move = current_price - fair_value

# Only trade if the move exceeds the noise level
if abs(signal_move) > 3 * noise ** 0.5:
    # This is a real move, not microstructure noise
    pass

When to use

  • High-frequency strategies: any strategy sampling prices faster than every 5 minutes needs noise correction.
  • Position sizing: using uncorrected vol overestimates risk and undersizes positions.
  • Signal filtering: distinguish real price moves from bid-ask bounce before acting on them.
  • Sampling rate selection: when designing a data pipeline, optimal_sampling() tells you the right frequency to avoid both noise contamination and information loss.

For daily or hourly data, microstructure noise is negligible and standard realized vol works fine.

Next