VerticalHorizontalFilter — Vertical Horizontal Filter
The window’s net range divided by the total path travelled: high means one directional move,
low means the same ground covered many times. Structurally the same question as
EfficiencyRatio, asked with the range instead of the endpoints.
Signature
VerticalHorizontalFilter(period, on=<source>)| Argument | Type | Required | Notes |
|---|---|---|---|
period |
int Param or literal | Yes | Window. Default 28, bounds 2–400. |
on= |
symbol, OHLCV field, or indicator | No | Source. Defaults to the trade symbol’s close. |
Formula
$$ \text{VHF} = \frac{\max(C, n) - \min(C, n)}{\sum_i |C_i - C_{i-1}|} $$
Warmup
period + 1 bars; 0 until then.
Reading the value
vhf.valuevhf > 0.35Example
class VhfGate(Strategy): """Trend rules only above a VHF threshold.""" trade = Symbol() sizing = FixedCapital()
def execute(self): vhf = VerticalHorizontalFilter(28, on=self.trade) sma = SMA(50, on=self.trade) if vhf.value > 0.35 and self.trade.close > sma.value and self.trade.is_flat: self.buy(self.trade, reason=f"VHF {vhf.value:.2f}") if vhf.value < 0.25 and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="Gone horizontal")Notes
- Adam White, Futures (1991).
- Proven by an assert-via-trade run on SPY daily (matched a strategy-side range/path reference to 1e-4 on every bar; zero alarm trades).
- Bounded 0–1. Higher means more vertical (trending); the thresholds are instrument-specific.