VolatilityCloseToCloseMovingBars: Close-to-Close Volatility (Moving Bars)
The same root mean square of log returns as VolatilityCloseToClose, taken over only the bars
whose close changed. On an illiquid instrument that prints the same close for hours, the plain
measure counts every one of those zero returns and reports a volatility far below what the
instrument actually does when it moves; this one leaves them out.
Signature
VolatilityCloseToCloseMovingBars(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
On each bar the indicator asks whether the close moved. A bar moved when it is not the first bar, the previous and the current close are both above zero, and the close differs from the previous one. A moving bar contributes its squared log return; every other bar contributes nothing and is not counted.
$$ \sigma_{mb} = 100,\sqrt{A \cdot \frac{\sum_{i \in M} \ln^2(C_i/C_{i-1})}{|M|}} $$
where M is the set of moving bars among the last period bars, |M| their count and A the
annualization. In words:
- Take the last
periodbars. - Keep only the bars that moved.
- Average their squared log returns. The average return is not subtracted: the measure
assumes a zero mean, exactly as
VolatilityCloseToClosedoes. - Multiply by
annualization, take the square root and express it as a percentage.
When no bar in the window moved, the value is 0.
Warmup
period + 1 bars; 0 when every bar in the window was unchanged.
Reading the value
mb.valuemb.value > ctc.valueExample
class ThinMarketVol(Strategy): """On a thin instrument, size from the volatility of the bars that actually moved.""" trade = Symbol() sizing = FixedCapital()
def execute(self): mb = VolatilityCloseToCloseMovingBars(20, 252, on=self.trade) sma = SMA(50, on=self.trade) if mb.value < 25 and self.trade.close > sma.value and self.trade.is_flat: self.buy(self.trade, reason=f"Effective vol {mb.value:.1f}") if mb.value > 40 and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="Vol of moving bars spiked")Notes
- A RaamTrade variant of the close-to-close measure, described fully above rather than by
reference to any published estimator. When every bar moves, it equals
VolatilityCloseToCloseover the same window. - Proven by an assert-via-trade run on SPY daily (non-negative and consistent with the plain estimator on every bar; zero alarm trades). On liquid daily data the two agree closely; the difference appears on thin or intraday series.