Optimized Trend Tracker (OTT)

Parameters: period = 2 | percent = 1.4 | ma_type = VAR

Overview

The Optimized Trend Tracker (OTT) constructs adaptive trailing stops by applying a percentage offset to a smoothed moving average baseline. The algorithm first calculates a base moving average using the specified type (default VAR), then builds upper and lower bands by shifting this baseline by the configured percent parameter. As prices evolve, OTT switches between these bands to produce a single optimized tracking line that follows established trends while filtering minor noise. Traders employ OTT as both a dynamic stop loss level and a trend confirmation tool; when price remains above the tracker, bullish conditions persist, and vice versa. The default parameters of period 2 and percent 1.4 create a highly responsive tracker suitable for short term momentum strategies. Varying the moving average type adjusts smoothing characteristics, with VAR providing variable adaptation and alternatives like EMA offering exponential weighting.

Defaults (VectorTA): period = 2, percent = 1.4, ma_type = "VAR".

Implementation Examples

Compute OTT from a slice or candles:

use vectorta::indicators::ott::{ott, OttInput, OttParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From a price slice
let prices = vec![100.0, 101.0, 99.5, 102.0, 103.0];
let params = OttParams { period: Some(2), percent: Some(1.4), ma_type: Some("VAR".to_string()) };
let input = OttInput::from_slice(&prices, params);
let out = ott(&input)?;

// From candles with defaults (close, period=2, percent=1.4, ma_type="VAR")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = OttInput::with_default_candles(&candles);
let out = ott(&input)?;

// Access values
for v in out.values { println!("OTT: {}", v); }

API Reference

Input Methods
// From a price slice
OttInput::from_slice(&[f64], OttParams) -> OttInput

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

// From candles with defaults (close)
OttInput::with_default_candles(&Candles) -> OttInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct OttParams {
    pub period: Option<usize>,   // default: Some(2)
    pub percent: Option<f64>,    // default: Some(1.4)
    pub ma_type: Option<String>, // default: Some("VAR")
}
Output Structure
#[derive(Debug, Clone)]
pub struct OttOutput {
    pub values: Vec<f64>, // OTT series; length matches input; warmup as NaN
}
Validation, Warmup & NaNs
  • Errors: EmptyInputData, AllValuesNaN, InvalidPeriod { period, data_len }, NotEnoughValidData { needed, valid }, InvalidPercent { percent }, InvalidMaType { ma_type }, MaCalculationFailed { reason }, InvalidBatchKernel.
  • period ≥ 1 and ≤ input length; also requires at least period valid values after the first non-NaN.
  • percent must be finite and ≥ 0.
  • Warmup equals the warmup of the chosen ma_type; leading outputs are NaN.
  • Streaming: internal buffer is max(period, 10) for VAR; update returns None until filled.
Error Handling
use vectorta::indicators::ott::{ott, OttError};

match ott(&input) {
    Ok(out) => consume(out),
    Err(OttError::EmptyInputData) => eprintln!("no data provided"),
    Err(OttError::AllValuesNaN) => eprintln!("all inputs are NaN"),
    Err(OttError::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid period {period} for length {data_len}"),
    Err(OttError::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {needed} valid values, only {valid} available"),
    Err(OttError::InvalidPercent { percent }) =>
        eprintln!("invalid percent: {percent}"),
    Err(OttError::InvalidMaType { ma_type }) =>
        eprintln!("invalid ma_type: {ma_type}"),
    Err(OttError::MaCalculationFailed { reason }) =>
        eprintln!("MA failed: {reason}"),
    Err(OttError::InvalidBatchKernel) =>
        eprintln!("invalid batch kernel"),
}

Python Bindings

Basic Usage

Call OTT on NumPy arrays:

import numpy as np
from vectorta import ott

prices = np.asarray([100.0, 101.0, 99.5, 102.0, 103.0], dtype=np.float64)

values = ott(prices, period=2, percent=1.4, ma_type="VAR", kernel="auto")
print("OTT values", values)
Streaming Real-time Updates

Maintain an OTT stream:

from vectorta import OttStream

stream = OttStream(period=2, percent=1.4, ma_type="VAR")

for price in price_feed:
    y = stream.update(price)
    if y is not None:
        use(y)
Batch Parameter Optimization

Evaluate multiple combinations:

import numpy as np
from vectorta import ott_batch

prices = np.asarray([...], dtype=np.float64)

result = ott_batch(
    prices,
    period_range=(2, 10, 2),
    percent_range=(0.5, 2.0, 0.5),
    ma_types=["VAR", "WWMA"],
    kernel="auto",
)

values = result["values"]  # shape: [rows, len(prices)]
periods = result["periods"]
percents = result["percents"]
ma_types = result["ma_types"]
CUDA Acceleration

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

# Coming soon: CUDA-accelerated OTT calculations
# from vectorta import ott_cuda_batch, ott_cuda_many_series_one_param
# import numpy as np
# ...

JavaScript/WASM Bindings

Basic Usage

Calculate OTT in JS/TS:

import { ott_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 101.0, 99.5, 102.0, 103.0]);

// Args: data, period, percent, ma_type
const values = ott_js(prices, 2, 1.4, 'VAR');
console.log('OTT values:', values);
Memory-Efficient Operations

Use zero-copy allocations for large datasets:

import { ott_alloc, ott_free, ott_into, memory } from 'vectorta-wasm';

const len = prices.length;
const inPtr = ott_alloc(len);
const outPtr = ott_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(prices);

// in_ptr, out_ptr, len, period, percent, ma_type
ott_into(inPtr, outPtr, len, 2, 1.4, 'VAR');

const out = new Float64Array(memory.buffer, outPtr, len).slice();

ott_free(inPtr, len);
ott_free(outPtr, len);

console.log('OTT values:', out);
Batch Processing

Evaluate many parameter combinations at once:

import { ott_batch } from 'vectorta-wasm';

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

const result = ott_batch(prices, {
  period_range: [2, 10, 2],
  percent_range: [0.5, 2.0, 0.5],
  ma_types: ['VAR', 'WWMA'],
});

// result: { values: Float64Array, combos: OttParams[], rows: number, cols: number }
console.log(result.rows, result.cols);
// Flattened values can be reshaped by rows x cols as needed

Performance Analysis

Comparison:
View:
Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators