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.valuedema.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).
TEMAextends the same idea one order further;HMAgets 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.