# D07-F01-A06 — Triple Exponential Moving Average (TEMA)

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

## Executive summary

The Triple Exponential Moving Average (TEMA) combines three nested Exponential
Moving Average (EMA) layers:

\[
E^{(1)}=\operatorname{EMA}(x),\quad
E^{(2)}=\operatorname{EMA}(E^{(1)}),\quad
E^{(3)}=\operatorname{EMA}(E^{(2)})
\]

\[
T=3E^{(1)}-3E^{(2)}+E^{(3)}
\]

The maintained [TA-Lib definition](https://ta-lib.org/functions/tema/) uses
this construction and explicitly distinguishes TEMA from merely applying EMA
three times. Patrick G. Mulloy's 1994 articles introduced the DEMA/TEMA family
to reduce moving-average lag.

TEMA responds more strongly to recent changes than an equal-span EMA or DEMA.
That responsiveness is not free: three seeded layers need a longer warm-up,
and signed effective weights allow overshoot and ringing around abrupt changes.
It is a causal descriptive filter, not a forecast.

## Problem and financial relevance

An ordinary moving average suppresses short-run variation but trails sustained
movement. TEMA uses a third-order lag-correction combination while retaining a
recursive, constant-memory update after initialization.

This is useful for:

- responsive trend overlays and dashboards;
- causal smoothing inside another indicator;
- streaming calculations where future observations are unavailable;
- studying the lag, noise, and overshoot trade-off explicitly.

A closer fit to price or another metric does not establish predictive value.

## Learning objectives

After completing this topic, the reader can:

- compute and align all three EMA layers;
- explain the `3 × span − 2` canonical warm-up;
- derive and interpret `3EMA1 − 3EMA2 + EMA3`;
- explain why reduced low-frequency lag and overshoot share a cause;
- implement matching Python and TypeScript versions;
- test exact fixtures, invariants, validation, and cross-language parity;
- distinguish TEMA from EMA3, DEMA, TRIX, and the T3 moving average.

## Prerequisites

- [SMA](../D07-F01-A01-sma/README.md) for each canonical seed
- [EMA](../D07-F01-A02-ema/README.md) for recursive smoothing
- [DEMA](../D07-F01-A05-dema/README.md) for the preceding lag-correction idea
- Ordered time series and basic algebra

## Definitions and terminology

| Term | Definition |
|---|---|
| `EMA1` | EMA of the validated source series. |
| `EMA2` | EMA of available `EMA1` states using the same span. |
| `EMA3` | EMA of available `EMA2` states using the same span. |
| TEMA | `3 × EMA1 − 3 × EMA2 + EMA3`. |
| Span | Positive integer `n`, mapped to `alpha = 2 / (n + 1)`. |
| Warm-up | Source observations consumed before all three SMA seeds exist. |
| Overshoot | TEMA moving outside the current input or component range. |
| EMA3 | Only the third nested EMA; it is not TEMA. |

## Scope and variants

This package implements one explicit convention: the same span and
`alpha = 2 / (span + 1)` in all three layers, with an arithmetic-mean seed for
each layer. The three-pass assembly and default-compatible EMA seeding agree
with the maintained [TA-Lib C implementation](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_TEMA.c),
but this package's aligned nulls, accepted span range, and error handling are
its own declared interface. Vendor versions and compatibility modes can differ.

| Variant | Included? | Consequence |
|---|:---:|---|
| Same-span, SMA-seeded TEMA | Yes | First output at `3n − 2`; reproducible canonical path |
| First-value-seeded TEMA | No | Immediate but different startup path |
| Caller-supplied alpha | No | Useful in filter design, but no fixed span convention |
| EMA applied three times | No | Produces `EMA3`, a slower smoother, not TEMA |
| TRIX | No | Rate of change of a triple-smoothed EMA, not this level series |
| T3 moving average | No | Different multi-stage construction and coefficient |

The [TC2000 methodology](https://help.tc2000.com/m/69445/l/755007-triple-exponential-moving-average-tema)
also documents the `3EMA1 − 3EMA2 + EMA3` definition and warns that TEMA is not
TRIX.

## Input data contract

The calculator accepts `values` and `span`.

| Field | Type | Unit | Nullable | Meaning and constraints |
|---|---|---|:---:|---|
| `values` | ordered sequence of numbers | one stable unit | No | Finite observations in ascending event order |
| `span` | integer | observations | No | Positive; `alpha = 2 / (span + 1)` |

Operational rules:

- timestamps, if carried by the caller, must be unique and strictly increasing;
- a missing row creates no recursive update;
- null, `NaN`, and infinity are rejected rather than filled or skipped;
- zero is a valid value, not a missing-value marker;
- identity, currency/unit, frequency, calendar, and adjustment basis remain stable;
- prices are adjusted before smoothing when an adjusted series is intended;
- recursive state is never rounded;
- synthetic fixtures in this package have no external licensing dependency.

## Output contract

`calculate_tema_components` returns one aligned row per source observation.

| Field | Type | Meaning | Invariant |
|---|---|---|---|
| `value` | number | Original observation | Exactly preserves input order |
| `ema1` | number or null | First EMA layer | First available at observation `n` |
| `ema2` | number or null | Second EMA layer | First available at observation `2n − 1` |
| `ema3` | number or null | Third EMA layer | First available at observation `3n − 2` |
| `tema` | number or null | Final TEMA | Available exactly when `ema3` is available |
| `status` | enum | Current warm-up stage or `ready` | Matches component availability |

`calculate_tema` returns only the aligned TEMA series. Inputs are not mutated.
For span one, this package defines every layer and TEMA as the input. That is
the algebraic identity case; callers should not assume every vendor release
accepts the same minimum span.

## Mathematical formulation

For span \(n\):

\[
\alpha=\frac{2}{n+1},\qquad q=1-\alpha
\]

Each available layer uses:

\[
E_t=E_{t-1}+\alpha(y_t-E_{t-1})
\]

where \(y_t\) is the input to that layer. Each layer's first state is the
arithmetic mean of its first \(n\) available inputs.

The final combination can also be written:

\[
T=E^{(1)}+2(E^{(1)}-E^{(2)})-(E^{(2)}-E^{(3)})
\]

This reveals a two-step correction: extend EMA1 away from EMA2 twice, then
subtract the residual gap between EMA2 and EMA3.

### Symbol table

| Symbol | Meaning | Unit or range |
|---|---|---|
| \(x_t\) | Source observation at event index \(t\) | Source unit |
| \(n\) | Span | Positive integer |
| \(\alpha\) | Update coefficient | \(0 < \alpha \le 1\) |
| \(q\) | Decay factor `1 − alpha` | \(0 \le q < 1\) |
| \(E^{(j)}_t\) | EMA layer \(j\) | Source unit |
| \(T_t\) | TEMA output | Source unit |
| \(k\) | Historical lag | Non-negative integer |

### Warm-up alignment

EMA1 first exists at observation \(n\). EMA2 then needs \(n\) EMA1 states and
first exists at \(2n-1\). EMA3 needs \(n\) EMA2 states:

\[
\boxed{\text{first TEMA observation}=3n-2}
\]

TA-Lib's reference implementation likewise defines TEMA lookback as three EMA
lookbacks.

### Effective steady-state weights

After startup effects decay:

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

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

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

Therefore:

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

The newest-observation coefficient is:

\[
w^{TEMA}_0=\alpha(3-3\alpha+\alpha^2)=1-(1-\alpha)^3
\]

For \(0<\alpha<1\), it exceeds both EMA's \(\alpha\) and DEMA's
\(\alpha(2-\alpha)\). Some older coefficients are negative, so TEMA is not a
convex average. The coefficients sum to one, but signed weights permit
overshoot. The first two steady-state moments cancel, reducing low-order
polynomial lag after transients; this does not remove delay at every frequency
or at reversals.

For span 3, the settled coefficients at lags 0 through 7 are `0.875`,
`0.1875`, `0`, `−0.03125`, `−0.0234375`, `−0.01171875`, `−0.00390625`, and
`0`. “Negative” describes subtraction inside the filter; it is not a bearish
market label.

## Algorithm steps

1. Validate a positive integer span and every finite observation.
2. Map the span to alpha.
3. Seed EMA1 from the first `n` source values, then update recursively.
4. Feed only available EMA1 states into EMA2; seed and update it identically.
5. Feed only available EMA2 states into EMA3; seed and update it identically.
6. Once all layers exist, emit `EMA3 + 3 × (EMA1 − EMA2)`.
7. Preserve null warm-up positions and expose the current stage.

## Pseudocode

See [PSEUDOCODE.md](examples/PSEUDOCODE.md) for the standalone version.

```text
validate span and values
alpha = 2 / (span + 1)

for each source value:
    update or seed EMA1
    if EMA1 unavailable: emit warming_ema1; continue

    update or seed EMA2 from EMA1
    if EMA2 unavailable: emit warming_ema2; continue

    update or seed EMA3 from EMA2
    if EMA3 unavailable: emit warming_ema3; continue

    emit EMA3 + 3 * (EMA1 - EMA2), status ready
```

## Worked example

Use span 3, so \(\alpha=1/2\), and:

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

| Obs. | Input | EMA1 | EMA2 | EMA3 | TEMA | State |
|---:|---:|---:|---:|---:|---:|---|
| 1 | 10 | null | null | null | null | Warming EMA1 |
| 2 | 13 | null | null | null | null | Warming EMA1 |
| 3 | 12 | 11.6667 | null | null | null | Warming EMA2 |
| 4 | 15 | 13.3333 | null | null | null | Warming EMA2 |
| 5 | 14 | 13.6667 | 12.8889 | null | null | Warming EMA3 |
| 6 | 18 | 15.8333 | 14.3611 | null | null | Warming EMA3 |
| 7 | 17 | 16.4167 | 15.3889 | 14.2130 | 17.2963 | Ready |
| 8 | 20 | 18.2083 | 16.7986 | 15.5058 | 19.7350 | Ready |
| 9 | 19 | 18.6042 | 17.7014 | 16.6036 | 19.3119 | Ready |
| 10 | 22 | 20.3021 | 19.0017 | 17.8027 | 21.7037 | Ready |

At observation 7:

\[
E^{(1)}=\frac{197}{12},\quad
E^{(2)}=\frac{277}{18},\quad
E^{(3)}=\frac{1535}{108}
\]

\[
T=3\left(\frac{197}{12}\right)
-3\left(\frac{277}{18}\right)
+\frac{1535}{108}
=\frac{467}{27}\approx17.2963
\]

The output lies above the current input 17. That is expected overshoot, not an
arithmetic error. Exact source and expected values live in
[tema-fixtures.json](datasets/tema-fixtures.json) and
[worked-example.json](examples/worked-example.json).

## Visual explanations

- [Calculation flow](visuals/mermaid/calculation-flow.md)
- [Three-layer component anatomy](visuals/static/tema-components.svg)
- [Worked path comparison](visuals/static/tema-path.svg)
- [Effective signed weights](visuals/static/tema-effective-weights.svg)
- [Interactive causal playground](visuals/animated/tema-playground.html)
- [Visual design and accessibility plan](visuals/VISUAL-PLAN.md)

The playground reveals observations causally and lets the learner change span,
scenario, speed, and component visibility.

## Implementations

- [Python](implementations/python/tema.py)
- [TypeScript](implementations/typescript/tema.ts)

Both return aligned components and consume the same synthetic fixture. Both
also expose the same settled-weight helper so signed coefficients can be tested
directly without confusing them with the finite SMA-seeded startup.

### Complexity

- Time: \(O(N)\) for \(N\) observations
- Batch output memory: \(O(N)\)
- Streaming state after seeding: \(O(n)\) during seed accumulation and \(O(1)\)
  recursive state thereafter

### Corrections, restarts, and provenance

Each output depends on every later recursive state. If a historical observation
changes, replay all three layers from a verified checkpoint before the changed
row, or recompute the full suffix. A restart checkpoint must preserve:

- all three EMA states;
- seed buffers or seed progress for unfinished layers;
- span, alpha, last processed event identity, and source provenance;
- full-precision values.

Storing only displayed TEMA is insufficient to resume exactly.

## Testing strategy

Python and TypeScript tests verify:

- every fixture component and warm-up status;
- first output at `3n − 2`;
- span-one identity and constant-input preservation;
- deliberate step-response overshoot;
- shared settled coefficients, including negative lags and unity sum;
- short-series null output;
- translation and scale equivariance;
- rejection of booleans, nulls, non-finite values, and invalid spans;
- input immutability;
- exact article fractions;
- package links, SVG accessibility, Mermaid structure, and playground controls.

Passing these tests proves conformance to this contract, not market efficacy.

## Historical-example decision

No named security or market event is presented as proof that TEMA “worked.”
TEMA is a deterministic transform: applying it to a selected rally or reversal
would show arithmetic on that price path, not predictive or trading value. It
would also require a point-in-time symbol identity, venue calendar, price basis,
corporate-action treatment, and provider-retrieval record to make the input
reproducible. The controlled synthetic cases are therefore the stronger
evidence for warm-up, lag correction, negative weights, and overshoot.

If a later empirical lesson adds market data, it should label provider values
as observations and all EMA/TEMA values as author-derived arithmetic, then test
the signal out of sample after costs. Until that evidence exists, this package
defers the anecdote rather than presenting a selected chart as validation.

## Limitations and failure modes

- TEMA can amplify short-run noise and overshoot abrupt moves.
- Three layers delay the first canonical output.
- Equal span labels do not imply equal smoothness across SMA, EMA, DEMA, TEMA,
  or Hull Moving Average.
- Seed conventions materially affect early values.
- Missing data, mixed units, corporate-action mistakes, and rounded checkpoints
  contaminate every downstream state.
- Parameter selection on the evaluation sample invites overfitting.
- No crossover or slope rule is part of this algorithm.

## Practical interpretation

TEMA is suitable when causal responsiveness matters and signed-weight behavior
is understood and monitored. Evaluate it against an explicit task such as
tracking error, turning-point delay, or downstream stability. For trading use,
separate algorithm correctness from out-of-sample signal validation, costs,
liquidity, and execution.

## Related topics

- [SMA](../D07-F01-A01-sma/README.md)
- [EMA](../D07-F01-A02-ema/README.md)
- [DEMA](../D07-F01-A05-dema/README.md)
- Next in the catalog: [Hull Moving Average](../../../../MASTER-CATALOG.md)

## References

See [REFERENCES.md](REFERENCES.md) for provenance, source roles, derived claims,
implementation choices, and explicit non-claims.
