Dynamic Trend Index (DTI)
r_period = 14 | s_period = 10 | u_period = 5 Overview
The Dynamic Trend Index (DTI) measures trend strength and direction by analyzing the relationship between high and low price movements, then applying sophisticated triple exponential smoothing. The indicator first calculates directional movement by comparing consecutive highs and lows to determine whether upward or downward forces dominate. This raw directional data then undergoes three sequential exponential moving average calculations, each with different periods, creating a cascade of smoothing that filters noise while preserving trend information. Both the signed directional movement and its absolute value receive this triple smoothing treatment. The final DTI value emerges from dividing the smoothed signed movement by the smoothed absolute movement, then scaling by 100 to create a bounded oscillator.
DTI oscillates between negative 100 and positive 100, with the sign indicating trend direction and magnitude showing trend strength. Positive values signal upward trends, with readings above 50 suggesting strong bullish momentum. Negative values indicate downward trends, with readings below negative 50 warning of powerful bearish forces. Values near zero reflect trendless, choppy markets where neither buyers nor sellers maintain control. The triple smoothing process makes DTI exceptionally stable, avoiding the whipsaws common in single smoothed indicators while still responding to genuine trend changes. The three period parameters allow fine tuning the balance between responsiveness and stability.
Traders use DTI to confirm trend direction, measure trend strength, and identify potential reversals through divergence analysis. Zero line crossovers provide clear trend change signals, though the triple smoothing means these occur after trends are well established. The bounded nature makes DTI excellent for overbought and oversold analysis, with extreme readings often preceding corrections or consolidations. Many systematic traders employ DTI as a trend filter, only taking positions aligned with DTI direction and avoiding trades when DTI hovers near zero. The indicator also works well in multi timeframe analysis, with longer period DTI confirming the major trend while shorter settings time entries and exits.
Implementation Examples
Get started with DTI from highs and lows:
use vectorta::indicators::dti::{dti, DtiInput, DtiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with high/low slices
let high = vec![10.0, 11.0, 12.0, 12.5, 13.0];
let low = vec![ 9.0, 9.5, 10.0, 11.0, 11.5];
let params = DtiParams { r: Some(14), s: Some(10), u: Some(5) };
let input = DtiInput::from_slices(&high, &low, params);
let result = dti(&input)?;
// Or with Candles (high/low are selected internally)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DtiInput::with_default_candles(&candles); // defaults r=14, s=10, u=5
let result = dti(&input)?;
for value in result.values {
println!("DTI: {}", value);
} API Reference
Input Methods ▼
// From high/low slices
DtiInput::from_slices(&[f64], &[f64], DtiParams) -> DtiInput
// From candles (uses fields "high" and "low")
DtiInput::from_candles(&Candles, DtiParams) -> DtiInput
// From candles with default params (r=14, s=10, u=5)
DtiInput::with_default_candles(&Candles) -> DtiInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct DtiParams {
pub r: Option<usize>, // Default: 14
pub s: Option<usize>, // Default: 10
pub u: Option<usize>, // Default: 5
} Output Structure ▼
#[derive(Debug, Clone)]
pub struct DtiOutput {
pub values: Vec<f64>, // DTI values in [-100, 100]
} Validation, Warmup & NaNs ▼
r > 0,s > 0,u > 0; each must be ≤ input length; otherwiseDtiError::InvalidPeriod.- There must be at least
periodvalid points after the first finite high/low; otherwiseDtiError::NotEnoughValidData. - Leading invalid pairs yield
DtiError::AllValuesNaNif none are finite; length mismatch yieldsDtiError::LengthMismatch. - Warmup: indices up to the first valid pair are
NaN; first computed value appears atfirst_valid + 1. - Candles input selects
"high"and"low"fields; selection issues returnDtiError::CandleFieldError.
Error Handling ▼
use vectorta::indicators::dti::{dti, DtiError};
match dti(&input) {
Ok(output) => use_results(output.values),
Err(DtiError::EmptyData) => eprintln!("Empty input data"),
Err(DtiError::CandleFieldError(msg)) => eprintln!("Candle field error: {}", msg),
Err(DtiError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for length {}", period, data_len),
Err(DtiError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points after first finite, got {}", needed, valid),
Err(DtiError::AllValuesNaN) => eprintln!("All high/low values are NaN"),
Err(DtiError::LengthMismatch { high, low }) =>
eprintln!("High/low length mismatch: {} vs {}", high, low),
} Python Bindings
Basic Usage ▼
Calculate DTI from NumPy arrays of highs and lows:
import numpy as np
from vectorta import dti
high = np.array([10.0, 11.0, 12.0, 12.5, 13.0])
low = np.array([ 9.0, 9.5, 10.0, 11.0, 11.5])
# Specify periods (r, s, u). Kernel optional: "auto" | "scalar" | "avx2" | "avx512"
values = dti(high, low, 14, 10, 5, kernel="auto")
print(values) Streaming Real-time Updates ▼
Update incrementally as new bars arrive:
from vectorta import DtiStream
stream = DtiStream(r=14, s=10, u=5)
for high_val, low_val in zip(high_series, low_series):
val = stream.update(high_val, low_val)
if val is not None:
print("DTI:", val) Batch Parameter Optimization ▼
Test multiple (r, s, u) combinations:
import numpy as np
from vectorta import dti_batch
high = np.array([...])
low = np.array([...])
# Define ranges as (start, end, step)
r_range = (5, 20, 5)
s_range = (5, 20, 5)
u_range = (3, 10, 1)
results = dti_batch(high, low, r_range, s_range, u_range, kernel="auto")
# Access 2D values and parameter lists
values = results["values"] # shape: (num_combos, len(series))
r_list = results["r"]
s_list = results["s"]
u_list = results["u"]
print(values.shape, len(r_list)) CUDA Acceleration ▼
CUDA support for DTI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated DTI calculations
# Expected patterns (subject to change):
# from vectorta import dti_cuda_batch, dti_cuda_many_series_one_param, dti_cuda_many_series_one_param_into
# dti_cuda_batch(...)
# dti_cuda_many_series_one_param(...)
# dti_cuda_many_series_one_param_into(...)
JavaScript/WASM Bindings
Basic Usage ▼
Calculate DTI in JavaScript/TypeScript from high/low arrays:
import { dti_js } from 'vectorta-wasm';
const high = new Float64Array([10.0, 11.0, 12.0, 12.5, 13.0]);
const low = new Float64Array([ 9.0, 9.5, 10.0, 11.0, 11.5]);
// r=14, s=10, u=5
const values = await dti_js(high, low, 14, 10, 5);
console.log('DTI values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { dti_alloc, dti_free, dti_into, memory } from 'vectorta-wasm';
const high = new Float64Array([/* your highs */]);
const low = new Float64Array([/* your lows */]);
const len = high.length;
// Allocate WASM memory for inputs and output
const highPtr = dti_alloc(len);
const lowPtr = dti_alloc(len);
const outPtr = dti_alloc(len);
// Copy input data into WASM memory
new Float64Array(memory.buffer, highPtr, len).set(high);
new Float64Array(memory.buffer, lowPtr, len).set(low);
// Compute into pre-allocated output buffer
await dti_into(highPtr, lowPtr, outPtr, len, 14, 10, 5);
// Read results from WASM memory
const out = new Float64Array(memory.buffer, outPtr, len).slice();
// Free allocated memory
dti_free(highPtr, len);
dti_free(lowPtr, len);
dti_free(outPtr, len);
console.log('DTI:', out); Batch Processing ▼
Calculate multiple parameter combinations in one call:
import { dti_batch_js } from 'vectorta-wasm';
const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);
// Define sweep ranges as (start, end, step)
const config = {
r_range: [5, 20, 5],
s_range: [5, 20, 5],
u_range: [3, 10, 1],
};
const out = await dti_batch_js(high, low, config);
// out: { values: Float64Array, combos: Array<{r?:number,s?:number,u?:number}>, rows: number, cols: number }
// Reshape flat values into row-major matrix
const matrix: number[][] = [];
for (let row = 0; row < out.rows; row++) {
const start = row * out.cols;
const end = start + out.cols;
matrix.push(Array.from(out.values.slice(start, end)));
}
console.log('Combos:', out.combos);
console.log('DTI matrix rows x cols:', out.rows, out.cols); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
Alligator
Technical analysis indicator
Aroon
Technical analysis indicator
Aroon Oscillator
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator