SuperTrend

Parameters: period = 10 | factor = 3 (1–5)

Overview

SuperTrend creates a volatility-adjusted trend following overlay by constructing dynamic support and resistance bands around the mid-price. The algorithm calculates the average of high and low prices (HL2), then adds and subtracts a multiple of Average True Range (ATR) to form preliminary upper and lower bands. These bands tighten progressively as they compare against previous values, preventing premature trend reversals. The indicator tracks which band is active based on price position, switching from bullish to bearish only when price definitively crosses the opposing band. When price trades above the lower band, SuperTrend displays that band as support and signals an uptrend; when price falls below the upper band, it flips to show resistance and signals a downtrend. This binary trend state combined with change signals makes SuperTrend popular for mechanical trading systems. The default factor of 3.0 with a 10-period ATR balances noise filtering against responsiveness, though aggressive traders often reduce the factor while conservative approaches increase it.

Implementation Examples

Compute SuperTrend from OHLC arrays or candles:

use vectorta::indicators::supertrend::{supertrend, SuperTrendInput, SuperTrendParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using OHLC slices
let high = vec![101.2, 102.6, 103.1, 101.8, 100.9];
let low =  vec![ 99.8, 100.7, 101.4,  99.9,  99.6];
let close = vec![100.5, 102.1, 101.9, 100.8, 100.2];

let params = SuperTrendParams { period: Some(10), factor: Some(3.0) };
let input = SuperTrendInput::from_slices(&high, &low, &close, params);
let output = supertrend(&input)?;

// Using Candles with defaults (period=10, factor=3.0)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SuperTrendInput::with_default_candles(&candles);
let output = supertrend(&input)?;

// Access fields
for (i, t) in output.trend.iter().enumerate() {
    let flipped = output.changed[i] == 1.0;
    println!("trend[{i}]={t:.4}, changed={flipped}");
}

API Reference

Input Methods
// From OHLC slices
SuperTrendInput::from_slices(&[f64], &[f64], &[f64], SuperTrendParams) -> SuperTrendInput

// From candles
SuperTrendInput::from_candles(&Candles, SuperTrendParams) -> SuperTrendInput

// From candles with defaults (period=10, factor=3.0)
SuperTrendInput::with_default_candles(&Candles) -> SuperTrendInput
Parameters Structure
pub struct SuperTrendParams {
    pub period: Option<usize>, // Default: 10
    pub factor: Option<f64>,   // Default: 3.0
}
Output Structure
pub struct SuperTrendOutput {
    pub trend: Vec<f64>,   // Active band as trend line (support/resistance)
    pub changed: Vec<f64>, // 1.0 when trend flips at t, else 0.0
}
Validation, Warmup & NaNs
  • Inputs are high, low, close with equal length; candles are supported.
  • period > 0 and period ≤ len; otherwise SuperTrendError::InvalidPeriod.
  • If all points are NaN, SuperTrendError::AllValuesNaN.
  • Requires at least period valid points after the first finite input; else NotEnoughValidData.
  • Warmup: indices before first_valid + period − 1 are NaN; after that, trend/changed are computed.
  • Streaming returns None until internal buffers are filled; thereafter returns (trend, changed) each update.
  • WASM into-APIs validate output slice lengths; mismatch raises OutputLengthMismatch.
Error Handling
use vectorta::indicators::supertrend::SuperTrendError;

match supertrend(&input) {
    Ok(out) => use_output(out),
    Err(SuperTrendError::EmptyData) => eprintln!("empty input"),
    Err(SuperTrendError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(SuperTrendError::InvalidPeriod { period, data_len }) =>
        eprintln!("invalid period {period} for len {data_len}"),
    Err(SuperTrendError::NotEnoughValidData { needed, valid }) =>
        eprintln!("need {needed} valid points, only {valid}"),
    Err(SuperTrendError::OutputLengthMismatch { expected, got }) =>
        eprintln!("output len {got}, expected {expected}"),
    Err(SuperTrendError::AtrError(e)) => eprintln!("ATR error: {e}"),
}

Python Bindings

Basic Usage

Calculate SuperTrend with NumPy arrays (returns trend and changed):

import numpy as np
from vectorta import supertrend

high = np.array([101.2, 102.6, 103.1, 101.8, 100.9], dtype=np.float64)
low  = np.array([ 99.8, 100.7, 101.4,  99.9,  99.6], dtype=np.float64)
close= np.array([100.5, 102.1, 101.9, 100.8, 100.2], dtype=np.float64)

trend, changed = supertrend(high, low, close, period=10, factor=3.0, kernel="auto")
print(trend.shape, changed.shape)
Streaming Real-time Updates

Use the dedicated stream class (warmup returns None):

from vectorta import SuperTrendStream

stream = SuperTrendStream(10, 3.0)
val = stream.update(101.2, 99.8, 100.5)
if val is not None:
    trend, changed = val
    if changed == 1.0:
        print("trend flipped")
Batch Parameter Optimization

Calculate multiple period/factor combinations at once:

import numpy as np
from vectorta import supertrend_batch

high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
close = np.array([...], dtype=np.float64)

res = supertrend_batch(
    high,
    low,
    close,
    period_range=(5, 20, 5),
    factor_range=(2.0, 4.0, 0.5),
    kernel="auto"
)

# res['trend'] and res['changed'] are shaped [rows, len]
print(res['periods'], res['factors'])
CUDA Acceleration

CUDA support for SuperTrend is coming soon. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated SuperTrend calculations

JavaScript/WASM Bindings

Basic Usage

Compute SuperTrend from Float64Array inputs:

import { supertrend_js } from 'vectorta-wasm';

const high = new Float64Array([/* ... */]);
const low  = new Float64Array([/* ... */]);
const close= new Float64Array([/* ... */]);

const res = supertrend_js(high, low, close, 10, 3.0);
// res: { values: Float64Array, rows: 2, cols: N }
const { values, rows, cols } = res as any;

// First row = trend, second row = changed
const trend = values.slice(0, cols);
const changed = values.slice(cols, 2 * cols);
console.log(trend, changed);
Memory-Efficient Operations

Use the *_alloc/free and *_into functions for zero-copy results:

import { supertrend_alloc, supertrend_free, supertrend_into, memory } from 'vectorta-wasm';

const N = close.length;
const trendPtr = supertrend_alloc(N);
const changedPtr = supertrend_alloc(N);

// Assume highPtr/lowPtr/closePtr already created or use views to pass JS arrays directly
supertrend_into(highPtr, lowPtr, closePtr, trendPtr, changedPtr, N, 10, 3.0);

const trend = new Float64Array(memory.buffer, trendPtr, N).slice();
const changed = new Float64Array(memory.buffer, changedPtr, N).slice();

supertrend_free(trendPtr, N);
supertrend_free(changedPtr, N);
Batch Processing

Test multiple combinations using the WASM batch API:

import { supertrend_batch_js } from 'vectorta-wasm';

const config = { period_range: [5, 20, 5], factor_range: [2.0, 4.0, 0.5] };
const out = supertrend_batch_js(high, low, close, config);

// out: { values, periods, factors, rows: 2*combos, cols }
// For each combo i, rows 2*i and 2*i+1 are trend/changed
const { values, periods, factors, rows, cols } = out as any;

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators