Skip to content
Open RaamTrade

HistoricalVolatility — Historical Volatility

The annualised standard deviation of log returns, in percent — the realized-volatility number options desks quote. SPY around 15 is calm; above 30 is stress.

Signature

HistoricalVolatility(period, annualization, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Return window. Default 20, bounds 2–400.
annualization int Param or literal Yes Bars per year. 252 for daily; 252 × bars-per-session intraday. Bounds 1–100000.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

$$ r_i = \ln(C_i / C_{i-1}), \quad \text{HV} = \sigma(r, n),\sqrt{A} \times 100 $$

Warmup

period + 1 bars (one to establish the first return); 0 until then.

Reading the value

hv.value
hv.value > 30

Example

class CalmRegimeOnly(Strategy):
"""Trade the trend only while realized volatility is subdued."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
hv = HistoricalVolatility(20, 252, on=self.trade)
sma = SMA(50, on=self.trade)
if hv.value < 20 and self.trade.close > sma.value and self.trade.is_flat:
self.buy(self.trade, reason=f"HV {hv.value:.1f}")
if hv.value > 30 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Vol regime shift")

Notes

  • Standard close-to-close realized volatility; population standard deviation.
  • annualization is a parameter on purpose — hard-coding 252 would be silently wrong on every intraday chart. Set it to the session’s bars per year.
  • Proven by an assert-via-trade run on SPY daily (matched a strategy-side log-return reference to 0.05 on every bar; zero alarm trades).
  • ParkinsonVolatility and GarmanKlassVolatility (Tier 2) are the more efficient range-based estimators of the same quantity.