Tilson T3
period = 5 | volume_factor = 0 Overview
The Tilson T3 Moving Average employs a sophisticated multi-stage exponential smoothing algorithm designed to minimize lag while maintaining smooth output. Developed by Tim Tilson, this indicator applies six cascaded exponential moving averages with a volume factor coefficient that controls the balance between responsiveness and smoothness. The volume factor typically ranges from 0 to 1, where values near zero produce behavior similar to a double exponential moving average, while higher values increase smoothing at the cost of slightly more lag. The T3 excels in trending markets by hugging price action more closely than traditional moving averages while filtering out minor fluctuations that generate false signals. Traders favor it for its ability to identify trend changes earlier than simple or exponential averages without the excessive whipsaws of more reactive indicators. Default parameters are a 5 period lookback with a volume factor of 0.7, providing an effective balance for most trading timeframes.
Implementation Examples
Get started with Tilson T3 in Rust:
use vectorta::indicators::moving_averages::tilson::{tilson, TilsonInput, TilsonParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = TilsonParams { period: Some(5), volume_factor: Some(0.0) }; // defaults
let input = TilsonInput::from_slice(&prices, params);
let result = tilson(&input)?; // result.values: Vec<f64>
// Using Candles with defaults (source="close", period=5, volume_factor=0.0)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = TilsonInput::with_default_candles(&candles);
let result = tilson(&input)?;
for v in result.values { println!("T3: {}", v); } API Reference
Input Methods ▼
// From price slice
TilsonInput::from_slice(&[f64], TilsonParams) -> TilsonInput
// From candles with custom source
TilsonInput::from_candles(&Candles, &str, TilsonParams) -> TilsonInput
// From candles with defaults (close, period=5, volume_factor=0.0)
TilsonInput::with_default_candles(&Candles) -> TilsonInput Parameters Structure ▼
pub struct TilsonParams {
pub period: Option<usize>, // Default: 5
pub volume_factor: Option<f64>, // Default: 0.0 (must be finite)
} Output Structure ▼
pub struct TilsonOutput {
pub values: Vec<f64>, // T3 values (row length = input length)
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len; otherwiseTilsonError::InvalidPeriod.- After the first finite input, need at least
6×(period−1)+1valid points; otherwiseTilsonError::NotEnoughValidData. volume_factormust be finite (notNaNor infinite); otherwiseTilsonError::InvalidVolumeFactor.- Indices before
first_valid + 6×(period−1)areNaN; subsequent outputs follow the cascade. - Streaming returns
Noneuntil warmup is satisfied; then yields a value per update.
Error Handling ▼
use vectorta::indicators::moving_averages::tilson::{tilson, TilsonError};
match tilson(&input) {
Ok(output) => use_values(output.values),
Err(TilsonError::EmptyInputData) => eprintln!("Input data is empty"),
Err(TilsonError::AllValuesNaN) => eprintln!("All input values are NaN"),
Err(TilsonError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for data length {}", period, data_len),
Err(TilsonError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {} available", needed, valid),
Err(TilsonError::InvalidVolumeFactor { v_factor }) =>
eprintln!("Volume factor must be finite, got {}", v_factor),
} Python Bindings
Basic Usage ▼
Calculate T3 using NumPy arrays (defaults: period=5, volume_factor=0.0):
import numpy as np
from vectorta import tilson
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=np.float64)
# Defaults
result = tilson(prices, period=5)
# Custom parameters and kernel
result = tilson(prices, period=7, volume_factor=0.5, kernel="avx2")
print("T3 values:", result) Streaming Real-time Updates ▼
Stream values via TilsonStream:
from vectorta import TilsonStream
stream = TilsonStream(period=5, volume_factor=0.0)
for price in price_feed:
t3 = stream.update(price)
if t3 is not None:
handle(t3) Batch Parameter Optimization ▼
Test multiple parameter combinations efficiently:
import numpy as np
from vectorta import tilson_batch
prices = np.array([...], dtype=np.float64)
results = tilson_batch(
prices,
period_range=(3, 10, 1),
volume_factor_range=(0.0, 1.0, 0.2),
kernel="auto",
)
print(results['values'].shape) # (num_combinations, len(prices))
print(results['periods']) # periods per row
print(results['volume_factors']) # volume factors per row CUDA Acceleration ▼
CUDA APIs are available for batch and many‑series cases:
# Requires CUDA-enabled build of vectorta
from vectorta import tilson_cuda_batch_dev, tilson_cuda_many_series_one_param_dev
import numpy as np
# One series, many parameter combinations (F32 input recommended)
prices_f32 = np.array([...], dtype=np.float32)
out_dev = tilson_cuda_batch_dev(
prices_f32,
period_range=(3, 10, 1),
volume_factor_range=(0.0, 1.0, 0.2),
device_id=0,
)
# Many series (time-major), one parameter set
tm_f32 = np.array([...], dtype=np.float32) # shape [T, N]
out_dev = tilson_cuda_many_series_one_param_dev(
tm_f32,
period=5,
volume_factor=0.0,
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
Calculate T3 in JavaScript/TypeScript:
import { tilson_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const t3 = tilson_js(prices, 5, 0.0);
console.log('T3:', t3); Memory-Efficient Operations ▼
Use zero-copy operations for larger datasets:
import { tilson_alloc, tilson_free, tilson_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const len = prices.length;
const inPtr = tilson_alloc(len);
const outPtr = tilson_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(prices);
// Args: in_ptr, out_ptr, len, period, volume_factor
tilson_into(inPtr, outPtr, len, 5, 0.0);
const out = new Float64Array(memory.buffer, outPtr, len).slice();
tilson_free(inPtr, len);
tilson_free(outPtr, len); Batch Processing ▼
Test multiple parameter combinations efficiently:
import { tilson_batch_js, tilson_batch_metadata_js } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Define parameter ranges
const pStart = 3, pEnd = 10, pStep = 1;
const vStart = 0.0, vEnd = 1.0, vStep = 0.2;
// Metadata: [periods..., volume_factors...]
const meta = tilson_batch_metadata_js(pStart, pEnd, pStep, vStart, vEnd, vStep);
const numCombos = meta.length / 2;
// Compute all combinations
const flat = tilson_batch_js(prices, pStart, pEnd, pStep, vStart, vEnd, vStep);
// Reshape into rows (one row per (period, v_factor))
const rows: Float64Array[] = [];
for (let i = 0; i < numCombos; i++) {
const start = i * prices.length;
rows.push(flat.slice(start, start + prices.length));
} Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
CUDA note
In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.
Related Indicators
Arnaud Legoux Moving Average
Moving average indicator
Compound Ratio Moving Average (CoRa Wave)
Moving average indicator
Centered Weighted Moving Average
Moving average indicator
Double Exponential Moving Average
Moving average indicator
Ehlers Distance Coefficient Filter
Moving average indicator
Ehlers Error-Correcting EMA (ECEMA)
Moving average indicator