דלגו לתוכן
פתיחת RaamTrade

VolatilityStdDev — Annualized Volatility

תוכן זה אינו זמין עדיין בשפה שלך.

Annualized historical volatility: the standard deviation of log returns, scaled by √252.

VolatilityStdDev is the identifier a strategy writes; Annualized Volatility is the label the chart’s indicator picker shows. Single value — .value is the only accessor.

Signature

VolatilityStdDev(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Number of returns in the window. Default 20, bounds 2–200.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

Each bar contributes one log return:

$$ r_t = \ln!\left(\frac{\text{close}t}{\text{close}{t-1}}\right) $$

Over the last N returns, with $\bar{r}$ their mean:

$$ \text{VolatilityStdDev} = \sqrt{\frac{252}{N} \sum_{i=0}^{N-1} (r_i - \bar{r})^2} $$

Population standard deviation (divide-by-N), annualized by the 252-trading-day factor folded inside the root.

Warmup

period + 1 bars, not period.

The first bar has no previous close to compare against — the seeded source gates the return on prev_close > 0 — so it produces no return and only seeds the comparison. The window of N returns is therefore full one bar later than the period number suggests. Before that the indicator returns 0.

Reading the value

vol.value # a FRACTION: 0.24 means 24% annualized
vol.value * 100 # percent, for display and thresholds
vol.value_at(1)
vol > 0.30 # a high-volatility regime

Example

class VolatilityRegimeFilter(Strategy):
"""Trend-follow only while annualized volatility stays under a ceiling."""
period = Param(20, min=2, max=200)
ceiling = Param(40, min=5, max=200)
trade = Symbol()
sizing = FixedCapital()
def execute(self):
vol = VolatilityStdDev(self.period, on=self.trade)
sma = SMA(self.period, on=self.trade)
self.stop_loss(2.0)
vol_pct = vol.value * 100
if vol_pct < self.ceiling and self.trade.close > sma.value and self.trade.is_flat:
self.buy(self.trade, reason=f"Calm regime ({vol_pct:.1f}% annualized)")
if vol_pct > self.ceiling and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Volatility spike")

Notes

  • The value is a fraction, not a percentage. Everyone quotes volatility as “24%”, and this indicator reports 0.24. Comparing vol.value > 30 will never be true for any real instrument. Multiply by 100 for display and for percentage thresholds, as the example does.
  • The 252 factor assumes daily bars. It is hard-coded in the seeded source. On an hourly or 5-minute chart the arithmetic still runs, but “annualized” is then wrong by the square root of the bars-per-day ratio. Treat sub-daily readings as a relative signal, not a quotable percentage.
  • Log returns make it symmetric — a +10% move and a −10% move contribute equal magnitudes — and instrument-independent, which is what makes this the right volatility measure to compare across symbols. StdDev on raw price is not comparable that way.
  • It measures dispersion, not direction. A calm uptrend and a calm downtrend read the same; pair it with a trend indicator, as the example does.
  • Volatility clusters. A reading that has just spiked is more likely than not to stay elevated for several bars — which is what makes it usable as a regime filter rather than a trigger.