T3
Tillson T3 — a six-fold cascaded EMA recombined with a volume factor
vto give a smooth, low-lag trend line.
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 required; v = 0.7 (Python default) |
| Warmup period | 6·period − 5 |
| Interpretation | Smooth trend line with less lag than a same-period EMA. |
Formula
T3 is the generalised DEMA (GD) applied three times. Tim Tillson's expansion of GD(GD(GD(price))) over six chained EMAs — e1 … e6, each of the same period, where e2 = EMA(e1), e3 = EMA(e2), … — is:
v2 = v², v3 = v³
c1 = −v3
c2 = 3·v2 + 3·v3
c3 = −6·v2 − 3·v − 3·v3
c4 = 1 + 3·v + v3 + 3·v2
T3 = c1·e6 + c2·e5 + c3·e4 + c4·e3The four coefficients always sum to 1, so a constant price series maps to itself. The volume factor v controls the lag/overshoot trade-off: v = 0 collapses T3 to the plain triple-cascaded EMA e3; the conventional v = 0.7 adds a corrective hump that sharpens turns.
Parameters
| Name | Type | Default | Valid range | Description |
|---|---|---|---|---|
period | usize | none | >= 1 | Length of every EMA in the cascade. period = 0 errors with Error::PeriodZero. |
v | f64 | 0.7 (Python) | [0.0, 1.0] | Volume factor. Non-finite or out-of-range values error with Error::InvalidPeriod. |
The Python binding defaults v to 0.7 via #[pyo3(signature = (period, v=0.7))]; period is always explicit. The Node and WASM constructors take both arguments explicitly.
Inputs / Outputs
From crates/wickra-core/src/indicators/t3.rs:
use wickra::{Indicator, T3};
// T3: Input = f64, Output = f64
const _: fn(&mut T3, f64) -> Option<f64> = <T3 as Indicator>::update;A single f64 close in, an Option<f64> out. Python maps this to float | None / numpy.ndarray (NaN warmup); Node to number | null / Array<number> (NaN warmup).
Warmup
T3::new(period, v).warmup_period() == 6·period − 5. Each stage of the SMA-seeded EMA cascade adds period − 1 bars of delay: e1 seeds at input period, e2 at 2·period − 1, …, e6 at 6·period − 5. T3 emits its first value once e6 is ready, since the output formula needs e3 through e6.
Edge cases
- Constant series. Because
c1 + c2 + c3 + c4 = 1for anyv, a flat input series produces a flat output equal to the constant (coefficients_sum_to_oneandconstant_series_yields_the_constantpin this). v = 0. The coefficients becomec1 = c2 = c3 = 0,c4 = 1, soT3is exactly the third stage of the EMA cascade (zero_volume_factor_collapses_to_triple_cascaded_emapins this).- NaN / infinity inputs. Non-finite inputs are silently dropped — the cascade is not advanced — and the previous valid value is returned.
- Reset.
t3.reset()clears all six EMAs and the cached value.
Examples
Rust
use wickra::{BatchExt, Indicator, T3};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let prices: Vec<f64> = (1..=40).map(f64::from).collect();
let mut t3 = T3::new(3, 0.7)?;
let out = t3.batch(&prices);
println!("warmup_period = {}", t3.warmup_period());
println!("first ready index = {:?}", out.iter().position(Option::is_some));
Ok(())
}Output:
warmup_period = 13
first ready index = Some(12)T3(3, 0.7) warms up after 6·3 − 5 = 13 inputs, so the first non-None output sits at index 12. On a pure ramp the output then tracks the input trend with a smooth, near-constant offset.
Python
import numpy as np
import wickra as ta
t3 = ta.T3(5) # v defaults to 0.7
prices = np.linspace(100.0, 140.0, 60)
out = t3.batch(prices)
print("warmup_period =", t3.warmup_period())
print("ready values:", np.count_nonzero(~np.isnan(out)))Output:
warmup_period = 25
ready values: 36Node
const ta = require('wickra');
const t3 = new ta.T3(5, 0.7);
const prices = Array.from({ length: 60 }, (_, i) => 100 + i);
console.log('warmupPeriod:', t3.warmupPeriod());
console.log('last:', t3.batch(prices).at(-1));Interpretation
T3 is a "best of both" trend line — close to Tema in lag reduction but visibly smoother, because the six-EMA cascade filters noise the three-EMA Tema lets through. Use it as a single trend filter or as the slow leg of a crossover where you want a clean line. Raise v toward 1 for sharper turns (more overshoot), lower it toward 0 for maximum smoothness (v = 0 is just a triple EMA).
Common pitfalls
- Treating
vas optional outside Python. Only the Python binding defaultsvto0.7; the Rust, Node and WASM constructors require it. - Underestimating warmup.
6·period − 5grows fast — aT3(20)needs115bars before its first value.
References
Tim Tillson, "Better Moving Averages", Technical Analysis of Stocks & Commodities (1998). The six-EMA expansion and coefficient formulas here match Tillson's published derivation and TA-Lib's T3.
See also
- Indicator-Tema — the three-EMA relative.
- Indicator-Dema — the two-EMA relative.
- Indicator-Zlema — low-lag average via de-lagging.
- Indicators-Overview — the full taxonomy.