Skip to content
Open RaamTrade

VolatilityCloseToClose — Close-to-Close Volatility

The plain close-to-close estimator with a zero-mean assumption: the root mean square of log returns, annualised. This is the baseline every other estimator in the family is judged against, and it is the only one that sees overnight gaps.

Signature

VolatilityCloseToClose(period, annualization, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Window. Default 20, bounds 1–400.
annualization int Param or literal Yes Bars per year. 252 daily. Bounds 1–100000.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

$$ \sigma = \sqrt{A \cdot \frac{1}{n}\sum_i \ln^2!\left(\frac{C_i}{C_{i-1}}\right)} \times 100 $$

Warmup

period + 1 bars.

Reading the value

ctc.value

Example

class VolCompare(Strategy):
"""Gap risk shows up as close-to-close volatility above the range estimator."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
ctc = VolatilityCloseToClose(20, 252, on=self.trade)
pv = ParkinsonVolatility(20, 252, on=self.trade)
if ctc.value < pv.value * 1.2 and self.trade.is_flat:
self.buy(self.trade, reason="No unusual gap risk")
if ctc.value > pv.value * 2 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Gap risk")

Notes

  • Zero-mean, not de-meaned. It squares raw log returns rather than deviations from their average, which is the estimator most volatility literature means by “close-to-close”. HistoricalVolatility uses the sample standard deviation instead; on a strongly trending window the two differ visibly.
  • Proven by an assert-via-trade run on SPY daily (matched a strategy-side log-return reference to 0.01 on every bar; zero alarm trades).