Triple Exponential Moving Average (TEMA)
period = 9 Overview
The Triple Exponential Moving Average (TEMA) achieves superior lag reduction through a clever mathematical combination of three exponential moving averages. Rather than simply stacking EMAs, TEMA applies the formula 3×EMA1 - 3×EMA2 + EMA3, where EMA1 operates on price, EMA2 smooths EMA1, and EMA3 smooths EMA2. This weighted combination effectively cancels much of the inherent smoothing lag while maintaining the noise reduction benefits of exponential averaging. The result tracks price movements more closely than a single EMA of the same period, responding faster to genuine trend changes while still filtering random fluctuations. Traders favor TEMA for entries and exits in trending markets where traditional moving averages lag too far behind price action. The indicator remains smooth enough to avoid excessive whipsaw signals yet responsive enough to capture trend inflection points with minimal delay. The default 9-period setting provides a practical balance, though shorter periods increase aggressiveness and longer periods prioritize smoothness over responsiveness.
Implementation Examples
Use TEMA with slices or candles:
use vectorta::indicators::moving_averages::tema::{tema, TemaInput, TemaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = TemaParams { period: Some(9) };
let input = TemaInput::from_slice(&prices, params);
let out = tema(&input)?;
// From candles (defaults: period=9, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = TemaInput::with_default_candles(&candles);
let out = tema(&input)?;
for v in out.values { println!("TEMA: {}", v); } API Reference
Input Methods ▼
// From price slice
TemaInput::from_slice(&[f64], TemaParams) -> TemaInput
// From candles with custom source
TemaInput::from_candles(&Candles, &str, TemaParams) -> TemaInput
// From candles with default params (close, period=9)
TemaInput::with_default_candles(&Candles) -> TemaInput Parameters Structure ▼
pub struct TemaParams {
pub period: Option<usize>, // Default: 9
} Output Structure ▼
pub struct TemaOutput {
pub values: Vec<f64>, // TEMA values
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ len(data)orTemaError::InvalidPeriod.- Requires at least
periodvalid points after the first finite value; elseTemaError::NotEnoughValidData. - All-NaN input yields
TemaError::AllValuesNaN; empty slice yieldsTemaError::EmptyInputData. - Warmup: outputs are
NaNuntilfirst + (period−1)×3. Forperiod=1, values equal price from first finite onward.
Error Handling ▼
use vectorta::indicators::moving_averages::tema::{tema, TemaError, TemaInput, TemaParams};
let input = TemaInput::from_slice(&prices, TemaParams { period: Some(9) });
match tema(&input) {
Ok(output) => process(output.values),
Err(TemaError::EmptyInputData) => eprintln!("Input data is empty"),
Err(TemaError::AllValuesNaN) => eprintln!("All values are NaN"),
Err(TemaError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for data length {}", period, data_len),
Err(TemaError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points after first finite; have {}", needed, valid),
} Python Bindings
Basic Usage ▼
Compute TEMA with NumPy arrays:
import numpy as np
from vectorta import tema
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0], dtype=np.float64)
# period (>=1), optional kernel: 'auto' | 'scalar' | 'avx2' | 'avx512'
values = tema(prices, 9, kernel='auto')
print(values.shape) Streaming Real-time Updates ▼
Process real-time TEMA values:
from vectorta import TemaStream
stream = TemaStream(period=9)
for price in price_feed:
val = stream.update(price)
if val is not None:
handle(val) Batch Parameter Optimization ▼
Test multiple period values and get structured outputs:
import numpy as np
from vectorta import tema_batch
prices = np.array([...], dtype=np.float64)
results = tema_batch(
prices,
period_range=(5, 30, 5),
kernel='auto'
)
# results is a dict with 2D array and metadata
print(results['values'].shape) # (num_combinations, len(prices))
print(results['periods']) # np.array of periods tested CUDA Acceleration ▼
CUDA-backed TEMA APIs are available when built with Python+CUDA features.
from vectorta import tema_cuda_batch_dev, tema_cuda_many_series_one_param_dev
import numpy as np
# 1) One series, many period values (parameter sweep)
prices_f32 = np.array([...], dtype=np.float32)
dev_out = tema_cuda_batch_dev(prices_f32, period_range=(5, 30, 1), device_id=0)
# 2) Many series (time-major), one parameter set
tm = np.array([...], dtype=np.float32) # shape [T, N]
grid_dev = tema_cuda_many_series_one_param_dev(prices_tm_f32=tm, period=9, device_id=0) JavaScript/WASM Bindings
Basic Usage ▼
Calculate TEMA in JavaScript/TypeScript:
import { tema_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 102, 101.5, 103, 105]);
const period = 9;
const values = tema_js(prices, period);
console.log('TEMA values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for large arrays:
import { tema_alloc, tema_free, tema_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* data */]);
const len = prices.length;
const inPtr = tema_alloc(len);
const outPtr = tema_alloc(len);
new Float64Array(memory.buffer, inPtr, len).set(prices);
tema_into(inPtr, outPtr, len, 9);
const values = new Float64Array(memory.buffer, outPtr, len).slice();
tema_free(inPtr, len);
tema_free(outPtr, len); Batch Processing ▼
Test multiple period values using the unified API:
import { tema_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Unified batch API with config
const out = tema_batch(prices, { period_range: [5, 30, 5] });
// out: { values: Float64Array, combos: { period?: number }[], rows: number, cols: number }
console.log(out.rows, out.cols);
// Deprecated legacy variant also exists: tema_batch_js(prices, start, end, step)
// which returns a flat Float64Array of length rows * cols. Performance Analysis
Across sizes, Rust CPU runs about 1.30× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
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