Skip to content
Open RaamTrade

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:

  1. Take the last period bars.
  2. Keep only the bars that moved.
  3. Average their squared log returns. The average return is not subtracted: the measure assumes a zero mean, exactly as VolatilityCloseToClose does.
  4. 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.value
mb.value > ctc.value

Example

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 VolatilityCloseToClose over 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.