Skip to content
Open RaamTrade

DEMA — Double EMA

Mulloy’s lag-reduced exponential average: twice the EMA minus the EMA of the EMA, which cancels most of the EMA’s lag while keeping its smoothness. Noisier than a plain EMA by construction.

Signature

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

Formula

$$ \text{DEMA} = 2,\text{EMA}(C, n) - \text{EMA}(\text{EMA}(C, n), n) $$

Warmup

Both EMAs seed from the first value and converge over roughly 3n bars; early values differ from long-history implementations by a geometrically decaying amount.

Reading the value

dema.value
dema.crossed_above(other)

Example

class DemaCross(Strategy):
"""Fast DEMA over slow DEMA."""
fast = Param(10, min=1, max=400)
slow = Param(50, min=1, max=400)
trade = Symbol()
sizing = FixedCapital()
def execute(self):
f = DEMA(self.fast, on=self.trade)
s = DEMA(self.slow, on=self.trade)
if f.crossed_above(s) and self.trade.is_flat:
self.buy(self.trade, reason="DEMA cross up")
if f.crossed_below(s) and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="DEMA cross down")

Notes

  • Patrick Mulloy, TASC (1994).
  • Proven by an assert-via-trade run on SPY daily (converged to an independent strategy-side EMA-chain replication within 0.05 after 40 bars; zero alarm trades).
  • TEMA extends the same idea one order further; HMA gets less lag still, differently.
  • The recursion seeds from the first bar rather than from a warm-up window, so early values are not comparable with TA-Lib DEMA(20), which seeds from an average of the first n; measured on a 300-bar tape the two agree to 1e-6 from bar 131 onward.