True Strength Index (TSI)

Parameters: long_period = 25 | short_period = 13

Overview

The True Strength Index (TSI) normalizes momentum by comparing smoothed price changes to smoothed absolute price changes, producing a bounded oscillator that reveals both trend direction and strength. The calculation begins with period-to-period price momentum, which undergoes double exponential smoothing using first a long period and then a short period. Simultaneously, the absolute value of momentum receives identical double smoothing. Dividing the smoothed momentum by smoothed absolute momentum and multiplying by 100 yields TSI values typically ranging from -100 to +100. This normalization technique makes TSI less susceptible to extreme price movements compared to raw momentum oscillators. Traders interpret positive TSI values as bullish momentum and negative values as bearish momentum, with the magnitude indicating strength. Crossovers of the zero centerline signal momentum regime changes, while divergences between TSI and price often foreshadow reversals. The default parameters of 25 and 13 periods provide smooth signals suitable for swing trading, though day traders often shorten both periods for increased responsiveness to intraday momentum shifts.

Formula: TSIt = 100 × EMAshort(EMAlong(m)) / EMAshort(EMAlong(|m|)), where m = pricet − pricet−1 and α = 2/(n+1) for each EMA. Defaults (VectorTA): long=25, short=13.

See also

Interpretation & Use

  • Zero-line: TSI above 0 suggests bullish momentum; below 0 suggests bearish momentum.
  • Normalization: Output is approximately bounded to [-100, 100] in batch/slice APIs.
  • Smoothing effect: Double-EMA reduces noise while retaining directional changes in momentum.
  • Constant prices: When momentum and its absolute value are both 0, the ratio is undefined and yields NaN.

Implementation Examples

Get started with TSI in just a few lines:

use vectorta::indicators::tsi::{tsi, TsiInput, TsiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = TsiParams { long_period: Some(25), short_period: Some(13) };
let input = TsiInput::from_slice(&prices, params);
let result = tsi(&input)?;

// Using with Candles data structure
// Quick and simple with default parameters (long=25, short=13; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = TsiInput::with_default_candles(&candles);
let result = tsi(&input)?;

// Access the TSI values
for value in result.values {
    println!("TSI: {}", value);
}

API Reference

Input Methods
// From price slice
TsiInput::from_slice(&[f64], TsiParams) -> TsiInput

// From candles with custom source
TsiInput::from_candles(&Candles, &str, TsiParams) -> TsiInput

// From candles with default params (close prices, long=25/short=13)
TsiInput::with_default_candles(&Candles) -> TsiInput
Parameters Structure
pub struct TsiParams {
    pub long_period: Option<usize>, // Default: 25
    pub short_period: Option<usize>, // Default: 13
}
Output Structure
pub struct TsiOutput {
    pub values: Vec<f64>, // TSI values; batch/slice kernels clamp to [-100, 100]
}
Validation, Warmup & NaNs
  • long_period > 0, short_period > 0, each ≤ data length; otherwise TsiError::InvalidPeriod.
  • Needs at least 1 + long + short valid points after the first finite value; otherwise TsiError::NotEnoughValidData.
  • Indices before first + long + short are NaN (warmup). Gaps in input propagate NaN without poisoning EMA state.
  • If the denominator path equals 0.0, output is NaN; constant prices yield NaN throughout.
  • Whole/slice and batch implementations clamp outputs to [-100, 100]; the streaming TsiStream::update returns the raw ratio×100.
  • All-NaN inputs: TsiError::AllValuesNaN.
Error Handling
use vectorta::indicators::tsi::{tsi, TsiInput, TsiParams, TsiError};

let params = TsiParams { long_period: Some(25), short_period: Some(13) };
let input = TsiInput::from_slice(&prices, params);
match tsi(&input) {
    Ok(output) => process(output.values),
    Err(TsiError::AllValuesNaN) => eprintln!("input was all NaNs"),
    Err(TsiError::InvalidPeriod { long_period, short_period, data_len }) => {
        eprintln!("invalid periods: long={}, short={}, len={}", long_period, short_period, data_len);
    }
    Err(TsiError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {} valid points after first finite; have {}", needed, valid);
    }
    Err(TsiError::EmaSubError(e)) => eprintln!("ema error: {}", e),
}

Python Bindings

Basic Usage

Calculate TSI in Python (NumPy array in, NumPy array out):

import numpy as np
from vectorta import tsi

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=np.float64)
values = tsi(prices, long_period=25, short_period=13, kernel=None)
print(values)
Streaming
from vectorta import TsiStream

stream = TsiStream(25, 13)
for p in price_stream:
    v = stream.update(p)
    if v is not None:
        handle(v)
Batch Parameter Sweep
import numpy as np
from vectorta import tsi_batch

prices = np.asarray([...], dtype=np.float64)
out = tsi_batch(
    prices,
    long_period_range=(20, 30, 5),
    short_period_range=(10, 16, 3),
    kernel=None,
)

# out is a dict with 'values' shaped [rows, cols] and parameter arrays
print(out['values'].shape, out['long_periods'], out['short_periods'])
CUDA Acceleration

CUDA support for TSI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated TSI calculations

JavaScript/WASM Bindings

Basic Usage

Calculate TSI in JavaScript/TypeScript:

import { tsi_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// Calculate TSI with specified parameters
const values = tsi_js(prices, 25, 13);  // long=25, short=13
console.log('TSI values:', values);
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { tsi_alloc, tsi_free, tsi_into, memory } from 'vectorta-wasm';

// Prepare your price data
const prices = new Float64Array([/* your data */]);
const length = prices.length;

// Allocate WASM memory for input and output
const inPtr = tsi_alloc(length);
const outPtr = tsi_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate TSI directly into allocated memory
// Args: in_ptr, out_ptr, len, long_period, short_period
tsi_into(inPtr, outPtr, length, 25, 13);

// Read results from WASM memory (slice() to copy out)
const tsiValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
tsi_free(inPtr, length);
tsi_free(outPtr, length);

console.log('TSI values:', tsiValues);
Batch Processing

Test multiple parameter combinations efficiently:

import { tsi_batch_js } from 'vectorta-wasm';

// Your price data
const prices = new Float64Array([/* historical prices */]);

// Define parameter sweep ranges (start, end, step)
const config = {
  long_period_range: [20, 30, 5],  // 20, 25, 30
  short_period_range: [10, 16, 3], // 10, 13, 16
};

// Calculate all combinations
const result = tsi_batch_js(prices, config);

// Result fields
// result.values: flat array of size rows*len
// result.combos: array of { long_period: Some(n), short_period: Some(n) }
// result.rows, result.cols: matrix shape
console.log(result.rows, result.cols, result.combos);

Export Code

use vectorta::indicators::tsi;

// Calculate TSI with custom parameters
let result = tsi(&data, 25, 13);

// Or using the builder pattern
let result = indicators::tsi::new()
    .long_period(25)
    .short_period(13)
    .calculate(&data);

This code snippet shows how to use the TSI indicator with your current parameter settings.

Performance Analysis

Comparison:
View:
Loading chart...

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