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

UlcerIndex — Ulcer Index

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

Root-mean-square drawdown from the running high: volatility that counts only the downside. Two instruments with the same standard deviation can have very different Ulcer Indices, and the one with the higher reading is the one that actually hurt to hold.

Signature

UlcerIndex(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Window. Default 14, bounds 1–400.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

$$ \text{UI} = \sqrt{\frac{1}{n}\sum_i \left(100,\frac{C_i - \text{peak}_i}{\text{peak}_i}\right)^2} $$

with peak the running maximum inside the window.

Warmup

period bars; before that it measures over the bars available.

Reading the value

ui.value
ui < 3

Example

class UlcerGate(Strategy):
"""Hold only while downside volatility is contained."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
ui = UlcerIndex(14, on=self.trade)
sma = SMA(50, on=self.trade)
if ui.value < 3 and self.trade.close > sma.value and self.trade.is_flat:
self.buy(self.trade, reason=f"Ulcer {ui.value:.2f}")
if ui.value > 6 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Drawdown pain rising")

Notes

  • Peter Martin and Byron McCann, The Investor’s Guide to Fidelity Funds (1989).
  • Proven by an assert-via-trade run on SPY daily (matched a strategy-side RMS-drawdown reference to 1e-3 on every bar; zero alarm trades).
  • The peak is the window’s own running high, not an all-time high — a long decline eventually reads as calm once the old peak leaves the window.
  • Drawdown is measured against the running peak from the window’s first bar, where pandas_ta’s ui(14) measures against the trailing rolling maximum at each bar — a different construction, not a scale factor (ours reads about 0.74× the reference).