# Double Exponential Moving Average (DEMA): Reduced Lag and Overshoot

> **Domain:** D07 — Technical Indicators  
> **Family:** D07-F01 — Trend Smoothing  
> **Status:** Video-ready  
> **Last reviewed:** 2026-07-23

## Executive summary

The Double Exponential Moving Average (DEMA) is a lag-compensated combination
of an Exponential Moving Average (EMA) and an EMA of that EMA:

\[
E^{(1)}_t=\operatorname{EMA}(x)_t
\]

\[
E^{(2)}_t=\operatorname{EMA}(E^{(1)})_t
\]

\[
D_t=2E^{(1)}_t-E^{(2)}_t
\]

The name does **not** mean “apply twice the smoothing.” It means two nested EMA
layers are combined by extrapolating the first layer away from the slower
second layer:

\[
D_t=E^{(1)}_t+\left(E^{(1)}_t-E^{(2)}_t\right)
\]

Patrick G. Mulloy introduced the one-parameter form in 1994 as a descriptive
smoother intended to reduce the lag of a plain EMA. [TA-Lib's maintained
definition](https://ta-lib.org/functions/dema/) uses the same three-part
formula.

DEMA is more responsive because the correction term can push it beyond both
EMA states. That also means it is not a convex average: it can overshoot after
steps or reversals. Reduced steady-state ramp lag is not prediction and does
not imply lower phase delay at every frequency.

> **The main limitation is part of the formula, not an implementation bug.**
> DEMA's settled effective weights eventually become negative. Those
> subtractive older contributions help cancel ramp lag, but they also allow the
> output to cross beyond the current input. The default lab opens on that
> overshoot case so a visually favorable interval cannot hide the trade-off.

## Problem and financial relevance

Smoothing suppresses short-run variation, but ordinary moving averages respond
after the source changes. DEMA attempts to recover some of that delay while
retaining a recursive, low-memory calculation.

This matters when an analyst needs:

- a responsive trend overlay;
- a reusable smoother inside another indicator;
- causal streaming state rather than a centered future-looking curve;
- an explicit trade-off between lag, noise, and overshoot.

DEMA does not determine whether a market will rise or fall. A visually closer
fit to price does not establish trading usefulness.

## Learning objectives

After completing this topic, the reader can:

- calculate both nested EMA layers and the DEMA correction;
- explain why canonical warm-up lasts `2 × span − 1` observations;
- derive the newest-observation and general steady-state coefficients;
- explain reduced ramp lag without calling DEMA predictive;
- identify negative historical weights and overshoot risk;
- implement matching Python and TypeScript versions;
- test seed alignment, invalid input, invariants, and cross-language fixtures;
- compare DEMA fairly with EMA, TEMA, and finite-window averages.

## Prerequisites

- [SMA](../D07-F01-A01-sma/README.md) for the canonical seed
- [EMA](../D07-F01-A02-ema/README.md) for recursive exponential smoothing
- Ordered time series and basic algebra

## Definitions and terminology

| Term | Definition |
|---|---|
| `EMA1` | EMA of the validated source series. |
| `EMA2` | EMA of the available `EMA1` series using the same span and alpha. |
| Correction | `EMA1 − EMA2`, added once to `EMA1`. |
| DEMA | `2 × EMA1 − EMA2`. |
| Span | Positive integer `n` mapped to `alpha = 2 / (n + 1)`. |
| Warm-up | Source observations required before both SMA seeds exist. |
| Overshoot | DEMA lying beyond the current input or a component state. |
| Steady-state ramp lag | Offset behind an indefinitely extended linear input after transients decay. |

## Scope and variants

The canonical package uses the same finance span, alpha, and SMA seed for both
EMA layers.

| Variant | Definition | Best use | Main limitation |
|---|---|---|---|
| Canonical DEMA | Same span for two SMA-seeded recursive EMAs | Reproducible technical indicators | First output after `2n − 1` observations |
| First-value seeded | Both layers begin from their first input | Immediate streaming output | Early path differs materially |
| Direct-alpha DEMA | Caller supplies alpha | Filter design and continuous tuning | No universal period label |
| Adjusted-weight DEMA | Nested normalized finite-history EW calculations | Library-specific analysis | Not equal to recursive DEMA near start |
| Two-alpha DEMA | Different alpha per layer | Custom filter design | Materially different transfer function |

Excluded:

- TEMA and other higher-order combinations;
- Holt double exponential smoothing, which models level and trend differently;
- centered or future-looking filters;
- crossover rules, parameter optimization, and performance claims.

## Input data contract

The fields below define the **production envelope** that should surround the
numerical calculation. The delivered Python and TypeScript functions are
deliberately smaller batch kernels: they accept only an already ordered array
of numeric values plus `span`. They validate the span and numeric values, but
cannot validate timestamps or metadata that the caller did not supply.

The production envelope describes one homogeneous ordered numeric series:

| Field | Type | Meaning and constraints |
|---|---|---|
| `timestamp` | RFC 3339 or declared ordered key | Unique and strictly increasing. |
| `value` | finite number | Non-null observation in a stable unit. |
| `series_id` | non-empty string | Stable identity of the measured series. |
| `unit` | non-empty string | Currency, points, return, volume, or another declared unit. |
| `calendar` | non-empty string | Session or event-calendar identity. |
| `frequency` | non-empty string | Meaning of one update step. |
| `value_basis` | non-empty string | Field and adjustment policy. |
| `is_final` | boolean | Whether the source observation can still be revised. |

Invariants:

- values are not silently sorted, deduplicated, filled, or rounded;
- null, NaN, infinity, and booleans are invalid numeric inputs;
- a missing row does not advance either EMA;
- series identity, unit, calendar, frequency, and basis remain stable;
- corporate-action adjustment happens upstream;
- corrections require suffix replay from a trusted earlier checkpoint.

## Parameters and initialization

For span \(n\):

\[
\alpha=\frac{2}{n+1},\qquad 0<\alpha\le1
\]

`EMA1` is seeded from the first \(n\) source values:

\[
E^{(1)}_n=\frac{1}{n}\sum_{i=1}^{n}x_i
\]

`EMA2` is seeded from the first \(n\) available `EMA1` values:

\[
E^{(2)}_{2n-1}
=
\frac{1}{n}\sum_{j=n}^{2n-1}E^{(1)}_j
\]

Therefore, the first canonical DEMA is available at source observation:

\[
2n-1
\]

For `span = 1`, alpha equals one, both layers equal the current input, and DEMA
is the identity series.

## Output contract

The delivered `DEMAPoint` for each accepted numeric value contains:

| Field | Meaning |
|---|---|
| `value` | Validated source observation. |
| `ema1` | First EMA or null before its seed. |
| `ema2` | EMA of EMA1 or null before its seed. |
| `dema` | `2 × ema1 − ema2`, or null until both layers exist. |
| `status` | `warming_ema1`, `warming_ema2`, or `ready`. |

The compact result does **not** claim to emit span, alpha, seed policy,
provisionality, timestamps, source identity, or checkpoint provenance. A
production wrapper should attach those fields from the validated input
envelope and should withhold final publication when unresolved provisional
history remains.

## Mathematical formulation

Each layer uses the same recurrence:

\[
E^{(1)}_t=E^{(1)}_{t-1}+\alpha(x_t-E^{(1)}_{t-1})
\]

\[
E^{(2)}_t=E^{(2)}_{t-1}+\alpha(E^{(1)}_t-E^{(2)}_{t-1})
\]

The final combination is:

\[
D_t=2E^{(1)}_t-E^{(2)}_t
\]

### Correction intuition

\[
D_t=E^{(1)}_t+\underbrace{(E^{(1)}_t-E^{(2)}_t)}_{\text{lag correction}}
\]

When `EMA1` rises faster than `EMA2`, the correction is positive. When it falls
faster, the correction is negative.

![DEMA's second EMA estimates the lag correction](./visuals/static/dema-components.svg)

### Steady-state weights

Ignoring initialization transients, let \(q=1-\alpha\). A plain EMA gives lag
\(k\) coefficient:

\[
w^{EMA}_k=\alpha q^k
\]

The EMA-of-EMA coefficient is:

\[
w^{EMA2}_k=\alpha^2(k+1)q^k
\]

Therefore DEMA's coefficient is:

\[
w^{DEMA}_k
=
\alpha q^k\left[2-\alpha(k+1)\right]
\]

The newest value has coefficient:

\[
w^{DEMA}_0=\alpha(2-\alpha)
\]

which exceeds \(\alpha\) when \(0<\alpha<1\). This matches the contemporary
period-equivalence criticism recorded in the
[December 1994 letters archive](https://store.traders.com/stoccomv1253.html).

For sufficiently old lags, \(2-\alpha(k+1)\) becomes negative. The coefficients
still sum to one, but they are not all non-negative. DEMA is therefore an
affine filter, not a conventional convex moving average.

For \(0<\alpha<1\), the first strictly negative lag satisfies:

\[
k > \frac{2}{\alpha}-1
\]

Span 1 is the identity exception: \(q=0\), so older coefficients are zero.

For span 3, \(\alpha=0.5\). The first six settled coefficients are:

| Lag \(k\) | 0 | 1 | 2 | 3 | 4 | 5 |
|---:|---:|---:|---:|---:|---:|---:|
| \(w^{DEMA}_k\) | 0.75 | 0.25 | 0.0625 | 0 | -0.015625 | -0.015625 |

These are **filter coefficients**, not portfolio weights or trade sizes. They
describe the settled infinite-history filter; the SMA-seeded startup path has
separate initialization effects.

![Signed DEMA coefficients and the resulting step overshoot](./visuals/static/dema-signed-weights.svg)

The steady-state coefficient first moment is zero:

\[
\sum_{k=0}^{\infty}k\,w^{DEMA}_k=0
\]

That explains why an indefinitely extended linear ramp is tracked without a
constant steady-state offset. It does not eliminate startup transients,
turning-point lag, noise, or frequency-dependent delay.

## Algorithm

1. Validate span and derive alpha.
2. Validate each numeric observation; validate series metadata upstream before
   calling the compact kernel.
3. Seed `EMA1` with the SMA of the first `span` values.
4. Feed each available `EMA1` state into the `EMA2` layer.
5. Seed `EMA2` with the SMA of its first `span` inputs.
6. Once both states exist, calculate `2 × EMA1 − EMA2`.
7. Preserve full precision and aligned null warm-up output.
8. Persist both states and their provenance for restart.

After warm-up, time is \(O(1)\) and persistent state is \(O(1)\) per
observation. Exact initialization needs running sums or \(O(n)\) seed buffers.

See [canonical pseudocode](./examples/PSEUDOCODE.md) and the
[calculation flow](./visuals/mermaid/calculation-flow.md).

## Worked example

Use span 3, so:

\[
\alpha=\frac{2}{3+1}=\frac12
\]

Input:

\[
10,\ 13,\ 12,\ 15,\ 14,\ 18,\ 17,\ 20
\]

| Observation | Value | EMA1 | EMA2 | DEMA | Status |
|---:|---:|---:|---:|---:|---|
| 1 | 10 | null | null | null | Warming EMA1 |
| 2 | 13 | null | null | null | Warming EMA1 |
| 3 | 12 | 11.666667 | null | null | Warming EMA2 |
| 4 | 15 | 13.333333 | null | null | Warming EMA2 |
| 5 | 14 | 13.666667 | 12.888889 | 14.444444 | Ready |
| 6 | 18 | 15.833333 | 14.361111 | 17.305556 | Ready |
| 7 | 17 | 16.416667 | 15.388889 | 17.444444 | Ready |
| 8 | 20 | 18.208333 | 16.798611 | 19.618056 | Ready |

The first layer seed is:

\[
E^{(1)}_3=\frac{10+13+12}{3}=\frac{35}{3}
\]

Its next two states are:

\[
E^{(1)}_4=\frac{40}{3},\qquad E^{(1)}_5=\frac{41}{3}
\]

The second layer seed is:

\[
E^{(2)}_5
=
\frac{\frac{35}{3}+\frac{40}{3}+\frac{41}{3}}{3}
=
\frac{116}{9}
\]

So the first DEMA is:

\[
D_5
=
2\left(\frac{41}{3}\right)-\frac{116}{9}
=
\frac{130}{9}
=
14.444444\ldots
\]

At observation 7, DEMA is \(157/9=17.4444\ldots\), above the current value
17. That is a valid overshoot, not a convex-bound violation.

![Input, EMA1, and DEMA across the worked example](./visuals/static/dema-path.svg)

The [interactive DEMA lab](./visuals/animated/dema-playground.html) reveals
each causal state, changes span, and compares a worked path, step, ramp, and
reversal without exposing future observations. It opens on the synthetic step
at observation 8: input is 10, EMA1 is 8.75, EMA2 is 6.875, and DEMA is
10.625. This is the default comparison because it exposes the failure mode
before the learner sees a smoother-looking path.

## Implementation guide

- [Python implementation](./implementations/python/dema.py)
- [TypeScript implementation](./implementations/typescript/dema.ts)
- [Shared fixture](./datasets/dema-fixtures.json)
- [Python tests](./tests/test_dema.py)
- [TypeScript tests](./tests/dema.test.ts)

Both implementations return aligned component states, reject invalid spans and
numeric values, retain null warm-up values, and do not mutate the source. They
do not sort, timestamp, or enrich the series.

They also expose a settled-weight diagnostic. It is intentionally separate
from the recursive calculation so users do not mistake infinite-history
coefficients for the SMA-seeded warm-up weights.

## Testing and validation

The package checks:

- shared-fixture parity for `EMA1`, `EMA2`, and DEMA;
- exact rational arithmetic in the worked example;
- first output at `2 × span − 1`;
- period-one identity;
- constant-input preservation;
- deterministic step-response overshoot;
- translation and scale equivariance;
- invalid spans, nulls, booleans, NaN, and infinity;
- input immutability;
- steady-state unity gain and zero coefficient first moment;
- Mermaid, SVG, HTML syntax, controls, fixtures, and accessibility hooks.

These establish implementation fidelity. They do not validate a trading signal.

## State, corrections, and missing data

A production streaming checkpoint must include both full-precision EMA states, seed progress when
warming, observation count, last timestamp, span, alpha, seed policy, series
identity, unit, calendar, frequency, value basis, and provisionality.

The delivered implementation is a batch replay, not a checkpoint API.
Correcting one historical input changes `EMA1`, then `EMA2`, then every later
DEMA value in exact arithmetic. Replay from the beginning or from a verified
checkpoint before the correction.

A missing row is not zero and does not create a canonical update. Null input is
rejected rather than silently skipped. Irregular-time decay requires a separate
time-aware definition for both layers.

## Failure modes and limitations

- **Warm-up:** two SMA seeds require `2n − 1` observations.
- **Seed dependence:** library outputs can differ near the beginning.
- **Overshoot:** negative older coefficients can push DEMA beyond the input.
- **Noise sensitivity:** extra responsiveness may amplify short-lived moves.
- **Period ambiguity:** equal span labels are not equal-smoothness comparisons.
- **Turning points:** zero steady-state ramp lag does not remove reversal delay.
- **Irregular time:** fixed alpha treats each accepted row as one equal step.
- **Adjustment breaks:** mixed raw and adjusted prices create false movement.
- **Revision propagation:** historical changes alter the entire dependent suffix.
- **Precision drift:** rounding either recursive state compounds error.
- **No predictive proof:** responsiveness does not establish future returns.

## Historical-example decision

A dated market interval could show DEMA arithmetic on observed closes, but it
would not by itself prove why overshoot occurs or establish a causal market
event. This package therefore keeps the default overshoot case synthetic and
fully reproducible. A future historical insert should be added only with a
redistributable official series, exact retrieval provenance, declared price
basis, and provider observations separated from author-derived EMA and DEMA
values. It must include an overshoot interval and must not be framed as a
forecast or successful trade.

## Real-world use cases

DEMA can serve as:

- a responsive price or metric overlay;
- a trend baseline for monitoring systems;
- a component inside a carefully specified indicator;
- a compact streaming smoother when lag costs more than some overshoot;
- a teaching example in filter weight, phase, and initialization trade-offs.

Parameter selection should be justified against the measurement objective,
sampling frequency, noise, costs, and out-of-sample evaluation.

## Visual explanations

- [Calculation flow](./visuals/mermaid/calculation-flow.md)
- [Component and correction anatomy](./visuals/static/dema-components.svg)
- [Worked path](./visuals/static/dema-path.svg)
- [Signed weights and overshoot](./visuals/static/dema-signed-weights.svg)
- [Interactive causal playground](./visuals/animated/dema-playground.html)
- [Visual plan](./visuals/VISUAL-PLAN.md)
- [Visual QA record](./visuals/QA.md)

## Related catalog topics

- **SMA** supplies the seed used by both layers.
- **EMA** supplies the recursive primitive.
- **WMA** uses an explicit finite weight vector.
- **Wilder RMA** uses alpha `1 / span`.
- **TEMA** extends lag compensation to three nested EMA layers.
- **MACD** combines fast and slow moving averages into a spread.

## References

See [REFERENCES.md](./REFERENCES.md). Sourced definitions, independent
derivations, implementation choices, and non-claims are separated explicitly.
