Files
QuanTAlib/lib/trends/t3/T3.md
T
2025-12-18 13:51:06 -08:00

4.4 KiB

T3: Tillson T3 Moving Average

What It Does

The T3 Moving Average is a hyper-smooth, low-lag indicator developed by Tim Tillson. It uses a unique "volume factor" to control how aggressively the moving average tracks the price. Unlike standard moving averages that simply smooth data, T3 applies multiple layers of smoothing (specifically, a generalized DEMA) to create a curve that is exceptionally smooth yet responsive to significant price moves.

Historical Context

Tim Tillson introduced the T3 in his article "Smoothing Techniques for More Accurate Signals" in Technical Analysis of Stocks & Commodities (January 1998). His goal was to improve upon the lag characteristics of traditional moving averages and the overshoot problems of DEMA (Double Exponential Moving Average).

How It Works

The Core Idea

T3 is essentially a "moving average of a moving average of a moving average..." but using a generalized DEMA (GD) instead of a simple EMA.

  • GD (Generalized DEMA): A mix of EMA and DEMA controlled by a volume factor v.
  • T3: Applying the GD filter six times in sequence (GD(GD(GD(GD(GD(GD(Price))))))).

The "Volume Factor" (v) determines how much "DEMA" (fast, overshooting) vs "EMA" (slow, lagging) is mixed in.

  • v=0: T3 behaves like a triple EMA (very smooth, some lag).
  • v=1: T3 behaves like a DEMA (very fast, prone to overshoot).
  • v=0.7: The standard default, offering a balance.

Mathematical Foundation

  1. Generalized DEMA (GD):

    GD(x, v) = EMA(x) \times (1 + v) - EMA(EMA(x)) \times v
  2. T3 Sequence:

    e1 = GD(Price) e2 = GD(e1) e3 = GD(e2) ... T3 = e6

Implementation Details

Our implementation uses the recursive GD formula for O(1) updates.

  • Complexity: O(1) per update (6 GD calculations).
  • Stability: Requires a warmup period to stabilize all 6 internal layers.

Configuration

Parameter Default Purpose Adjustment Guidelines
Period 14 Smoothing period Standard lookback.
Volume Factor (v) 0.7 Responsiveness 0.7 is standard. Lower (0.1-0.5) = smoother/slower. Higher (0.8-1.0) = faster/responsive.

Performance Profile

Operation Complexity Description
Streaming update O(1) 6 layers of GD calculation
Bar correction O(1) Efficient state rollback
Batch processing O(N) Single pass through data
Memory footprint O(1) Stores state for 6 internal layers

Interpretation

Trading Signals

Trend Identification

  • Smoothness: T3 is famous for filtering out "noise" better than almost any other MA. If T3 is rising, the trend is likely real, not just a blip.
  • Crossovers: Price crossing T3 is a significant event due to the indicator's smoothness.

When It Works Best

  • Noisy Markets: T3 shines in markets with lots of wicks and erratic movement, where standard EMAs would get chopped up.

When It Struggles

  • Lag: Despite its clever math, applying a filter 6 times introduces lag. It will turn after the market turns, not with it.

Architecture Notes

This implementation makes specific trade-offs:

Choice: 6 Layers

  • Implementation: We implement the standard "T3" which implies 6 layers of smoothing.
  • Rationale: While "T2" or "T4" are possible, "T3" (6 layers) is the industry standard definition.

References

  • Tillson, Tim. "Smoothing Techniques for More Accurate Signals." Technical Analysis of Stocks & Commodities, V. 16:1 (33-37), 1998.

C# Usage

Streaming Updates (Single Instance)

using QuanTAlib;

var t3 = new T3(period: 14, vFactor: 0.7);

// Process each new bar
TValue result = t3.Update(new TValue(timestamp, closePrice));
Console.WriteLine($"T3: {result.Value:F2}");

// Check if buffer is full
if (t3.IsHot)
{
    // Indicator is fully initialized
}

Batch Processing (Historical Data)

// TSeries API
TSeries prices = ...;
TSeries t3Values = T3.Batch(prices, period: 14, vFactor: 0.7);

// Span API (High Performance)
double[] prices = new double[1000];
double[] output = new double[1000];
T3.Calculate(prices.AsSpan(), output.AsSpan(), period: 14, vFactor: 0.7);

Bar Correction (isNew Parameter)

var t3 = new T3(14);

// New bar
t3.Update(new TValue(time, 100), isNew: true);

// Intra-bar update
t3.Update(new TValue(time, 101), isNew: false); // Replaces 100 with 101