Files
wickra/docs/wiki/indicators/moving-averages/Indicator-Tema.md
T
kingchenc d2f99efd78 F13c: restructure the indicator catalogue into eight families
The original taxonomy was four classical families plus a statistics group,
with the F1-F12 expansion slotted in as sub-categories. This regroups the
whole 71-indicator catalogue into eight top-level families, each with at
least five members:

  Moving Averages (12), Momentum Oscillators (13), Trend & Directional (9),
  Price Oscillators (5), Volatility & Bands (12), Trailing Stops (5),
  Volume (9), Price Statistics (7).

- Wiki: docs/wiki/indicators/ reorganised into eight family folders; all 71
  indicator pages moved with `git mv`. Every internal cross-link is
  normalised to `../<family>/Indicator-X.md`, each page's `Family` field is
  set to its new family, and two pre-existing `../Indicator-Chaining.md`
  links (should have been `../../`) are corrected. A link check confirms
  every relative wiki link resolves.
- Indicators-Overview.md fully rewritten around the eight families;
  Home.md indicator reference and the README family table follow suit.
- Warmup-Periods.md gains the eight F13 indicators; CHANGELOG records the
  46-indicator expansion (25 -> 71) and the eight-family taxonomy.
- Tests: Node indicators.test.js and Python test_new_indicators.py cover
  all eight new indicators (Node 91/91, Python 117/117 green).

cargo fmt + clippy (core/wickra/data/wasm/node) clean; 508 core tests,
25 data tests and 74 doctests green.
2026-05-22 21:21:56 +02:00

208 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TEMA
> Triple Exponential Moving Average — Mulloy's
> `3·EMA1 3·EMA2 + EMA3` (where each EMA is fed from the previous one),
> the second-order lag-reduction sibling of DEMA.
## Quick reference
| Field | Value |
|-------|-------|
| Family | Moving Averages |
| Input type | `f64` (single close) |
| Output type | `f64` |
| Output range | unbounded; tracks the input price scale |
| Default parameters | `period` is required (no default in either binding) |
| Warmup period | `3·period 2` |
| Interpretation | Even less lag than `Dema`, at the cost of more noise sensitivity. |
## Formula
Let `EMA1 = EMA(price, period)`, `EMA2 = EMA(EMA1, period)`,
`EMA3 = EMA(EMA2, period)`. Then:
```
TEMA_t = 3 * EMA1_t - 3 * EMA2_t + EMA3_t
```
All three EMAs share the same `period`, hence the same
`α = 2 / (period + 1)`. The coefficients `(3, 3, 1)` are the
second-order finite-difference correction that removes both the
first-order and second-order EMA lag terms — they come from expanding
`(1 L)^{-3}` where `L` is the lag operator.
## Parameters
| Name | Type | Default | Valid range | Description |
|----------|---------|---------|-------------|-------------|
| `period` | `usize` | none | `>= 1` | Period shared by all three internal EMAs. `period = 0` errors with `Error::PeriodZero`. |
(Python class `wickra.TEMA(period)` has no `#[pyo3(signature)]` default;
pass `period` explicitly.)
## Inputs / Outputs
From `crates/wickra-core/src/indicators/tema.rs`:
```rust
impl Indicator for Tema {
type Input = f64;
type Output = f64;
// update(&mut self, input: f64) -> Option<f64>
}
```
Python `update` returns `float | None`, `batch` returns a 1-D
`numpy.ndarray` (`float64`, `NaN` for warmup). Node `update` returns
`number | null`, `batch` returns `Array<number>` with `NaN`
placeholders.
## Warmup
`Tema::new(period).warmup_period() == 3 * period - 2`. Each stacked EMA
adds `period 1` more inputs to the warmup count:
- `ema1` emits first at input `period`.
- `ema2`, fed from `ema1`, emits first at input `period + (period 1) = 2·period 1`.
- `ema3`, fed from `ema2`, emits first at input `(2·period 1) + (period 1) = 3·period 2`.
For `Tema::new(14)` this gives `40` (matches the table in
[Warmup Periods](../../Warmup-Periods.md)); for `Tema::new(5)` (the example
below) it gives `13`. The implementation uses `?` short-circuit on every
stage, so each inner EMA is only fed once the previous one emits.
## Edge cases
- **Constant series.** Feeding `[42.0; n]` produces `Some(42.0)` once all
three EMAs have converged: `3·42 3·42 + 42 = 42`. The unit test
`constant_series_yields_constant_tema` pins this with `Tema::new(5)`
over 80 constants.
- **NaN / infinity inputs.** Inherited from the inner `Ema`: non-finite
inputs are silently dropped at the `ema1` boundary and never reach the
`3·EMA1 3·EMA2 + EMA3` arithmetic.
- **Reset.** `tema.reset()` resets all three internal EMAs; the next
`update` starts a full `3·period 2` warmup countdown.
## Examples
### Rust
```rust
use wickra::{BatchExt, Indicator, Tema};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut tema = Tema::new(5)?;
let prices: Vec<f64> = (1..=20).map(f64::from).collect();
let out: Vec<Option<f64>> = tema.batch(&prices);
println!("warmup_period = {}", tema.warmup_period());
println!("{:?}", out);
Ok(())
}
```
Output:
```
warmup_period = 13
[None, None, None, None, None, None, None, None, None, None, None, None, Some(13.0), Some(14.0), Some(15.000000000000002), Some(16.000000000000004), Some(17.000000000000007), Some(18.000000000000007), Some(19.000000000000007), Some(20.0)]
```
The first `Some` lands at index 12 (the 13th input), matching
`3·5 2 = 13`. On the linear ramp `1, 2, …, 20`, TEMA tracks the input
ramp essentially exactly because both first- and second-order lag have
been cancelled; the floating-point tail
(`15.000000000000002`, `16.000000000000004`, …) is ordinary IEEE-754
drift from the recursive subtractions.
### Python
```python
import numpy as np
import wickra as ta
tema = ta.TEMA(5)
out = tema.batch(np.arange(1.0, 21.0))
print("warmup_period =", tema.warmup_period())
print(out)
```
Output:
```
warmup_period = 13
[nan nan nan nan nan nan nan nan nan nan nan nan 13. 14. 15. 16. 17. 18.
19. 20.]
```
### Node
```javascript
const ta = require('wickra');
const tema = new ta.TEMA(5);
const prices = Array.from({ length: 20 }, (_, i) => i + 1);
console.log(tema.batch(prices));
console.log('warmupPeriod:', tema.warmupPeriod());
```
Output:
```
[
NaN, NaN,
NaN, NaN,
NaN, NaN,
NaN, NaN,
NaN, NaN,
NaN, NaN,
13, 14,
15.000000000000002, 16.000000000000004,
17.000000000000007, 18.000000000000007,
19.000000000000007, 20
]
warmupPeriod: 13
```
## Interpretation
`Tema` removes more lag than `Dema` and noticeably more than `Ema`.
On a clean trending series the line stays glued to price; on a noisy
or sideways series the same lag-cancellation amplifies the noise — TEMA
overshoots and reverses faster than DEMA, and very much faster than EMA.
The signals are the same crossover patterns: price-vs-TEMA and
fast-TEMA-vs-slow-TEMA. The `(3, 3, 1)` coefficient pattern is also
what makes `Trix` (also in this family) work — `Trix` is the percentage
change of `EMA3`, the triple-smoothed series.
Prefer `Tema` when `Dema` still feels too laggy and your data is clean
enough to tolerate the extra noise sensitivity. Prefer `Hma` if you want
a similar lag profile but with a built-in smoothing step (WMA chain
instead of EMA chain), which behaves more gracefully on noisy data.
## Common pitfalls
- **Forgetting the `3·period 2` warmup.** `Tema::new(50)` will not
emit until input 148. That is a significant chunk of any short-term
backtest. If you are running a side-by-side panel of indicators with
different warmups, filter rows on `~np.isnan(...)` (Python) /
`is_some()` (Rust) per indicator rather than picking one global
warmup cutoff.
- **Using TEMA for noisy intraday data without a smoothing step.** The
same lag-cancellation that makes TEMA attractive on clean data turns
into whipsaws on tick-by-tick feeds. Either raise `period` materially
or switch to `Hma`, which has a final WMA smoothing pass built in.
## References
Patrick G. Mulloy, *"Smoothing Data with Less Lag"*, **Technical Analysis
of Stocks & Commodities**, February 1994 (TEMA). The coefficient pattern
`(3, 3, 1)` for cancelling first- and second-order EMA lag is derived
in the same article.
## See also
- [Indicator-Ema.md](../moving-averages/Indicator-Ema.md) — the building block.
- [Indicator-Dema.md](../moving-averages/Indicator-Dema.md) — second-order's sibling.
- [Indicator-Hma.md](../moving-averages/Indicator-Hma.md) — similar lag profile, built on WMAs.
- [Indicators-Overview.md](../../Indicators-Overview.md) — the full taxonomy.