Statistical Trailing Stop

Parameters: data_length = 10 | normalization_length = 100 | base_level = level2

Overview

Statistical Trailing Stop turns recent high, low, and close behavior into a volatility-aware stop level. The indicator keeps track of the active stop level, the underlying anchor used to place that stop, a bias line that reflects directional state, and a change flag that marks bars where the stop regime flips.

In VectorTA the indicator works directly on high, low, and close slices or on candle collections, supports a streaming state machine for live updates, and exposes batch sweeps across both lookback lengths and the named base-level presets. That makes it useful both for end-of-day stop studies and for live trailing logic that has to react to changing range conditions without rebuilding state on every bar.

Defaults: `data_length = 10`, `normalization_length = 100`, and `base_level = "level2"`.

Implementation Examples

Run the indicator on direct high/low/close slices or on candle data with the default stop profile.

use vector_ta::indicators::statistical_trailing_stop::{
    statistical_trailing_stop,
    StatisticalTrailingStopInput,
    StatisticalTrailingStopParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let direct = statistical_trailing_stop(&StatisticalTrailingStopInput::from_slices(
    &high,
    &low,
    &close,
    StatisticalTrailingStopParams {
        data_length: Some(10),
        normalization_length: Some(100),
        base_level: Some("level2".to_string()),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let from_candles = statistical_trailing_stop(
    &StatisticalTrailingStopInput::with_default_candles(&candles),
)?;

println!("latest level = {:?}", direct.level.last());
println!("latest anchor = {:?}", direct.anchor.last());
println!("latest bias = {:?}", direct.bias.last());
println!("latest changed = {:?}", direct.changed.last());
println!("candle level = {:?}", from_candles.level.last());

API Reference

Input Methods
StatisticalTrailingStopInput::from_candles(&Candles, StatisticalTrailingStopParams)
    -> StatisticalTrailingStopInput

StatisticalTrailingStopInput::from_slices(&[f64], &[f64], &[f64], StatisticalTrailingStopParams)
    -> StatisticalTrailingStopInput

StatisticalTrailingStopInput::with_default_candles(&Candles)
    -> StatisticalTrailingStopInput
Parameters Structure
pub struct StatisticalTrailingStopParams {
    pub data_length: Option<usize>,           // default 10
    pub normalization_length: Option<usize>,  // default 100
    pub base_level: Option<String>,           // default "level2"
}
Output Structure
pub struct StatisticalTrailingStopOutput {
    pub level: Vec<f64>,
    pub anchor: Vec<f64>,
    pub bias: Vec<f64>,
    pub changed: Vec<f64>,
}
Validation, Base Levels & NaNs
  • high, low, and close must have matching lengths and contain at least one finite region.
  • The resolved periods must satisfy the indicator validation rules, including data_length + normalization_length + 1 <= data_len.
  • base_level must resolve to one of level0, level1, level2, or level3.
  • Direct and candle paths reject empty or all-NaN input and return the documented period, level, or output-length errors when validation fails.
  • The stream path resets itself when a non-finite bar arrives and returns None until enough valid data accumulates again.
  • Batch mode validates all sweep axes and rejects non-batch kernels.
Builder, Streaming & Batch APIs
StatisticalTrailingStopBuilder::new()
    .data_length(usize)
    .normalization_length(usize)
    .base_level("level2")
    .kernel(Kernel)
    .apply(&Candles)
    .apply_slices(&[f64], &[f64], &[f64])
    .into_stream()

StatisticalTrailingStopStream::try_new(params)
stream.update(high, low, close) -> Option<(f64, f64, f64, f64)>

StatisticalTrailingStopBatchBuilder::new()
    .data_length_range(start, end, step)
    .normalization_length_range(start, end, step)
    .base_level_range("level1", "level3", step)
    .kernel(Kernel)
    .apply(&Candles)
    .apply_slices(&[f64], &[f64], &[f64])

Python Bindings

Python exposes a direct function, a stream class, and a batch helper that returns the four output matrices plus the resolved sweep axes.

from vector_ta import (
    statistical_trailing_stop,
    statistical_trailing_stop_batch,
    StatisticalTrailingStopStream,
)

level, anchor, bias, changed = statistical_trailing_stop(
    high,
    low,
    close,
    data_length=10,
    normalization_length=100,
    base_level="level2",
)

stream = StatisticalTrailingStopStream(
    data_length=10,
    normalization_length=100,
    base_level="level2",
)
point = stream.update(high[-1], low[-1], close[-1])

batch = statistical_trailing_stop_batch(
    high,
    low,
    close,
    data_length_range=(8, 14, 2),
    normalization_length_range=(80, 120, 20),
    base_level_range=("level1", "level3", 1),
)

print(batch["level"].shape)
print(batch["base_levels"])

JavaScript/WASM Bindings

The WASM layer exposes a high-level object-returning call, an explicit batch entry point, and low-level allocation and into-buffer APIs for caller-managed memory.

import init, {
  statistical_trailing_stop_js,
  statistical_trailing_stop_batch,
  statistical_trailing_stop_alloc,
  statistical_trailing_stop_free,
  statistical_trailing_stop_into,
  statistical_trailing_stop_batch_into,
} from "vector-ta-wasm";

await init();

const single = statistical_trailing_stop_js(high, low, close, 10, 100, "level2");
console.log(single.level, single.anchor, single.bias, single.changed);

const batch = statistical_trailing_stop_batch(high, low, close, {
  data_length_range: [8, 14, 2],
  normalization_length_range: [80, 120, 20],
  base_level_range: ["level1", "level3", 1],
});

const ptrHigh = statistical_trailing_stop_alloc(high.length);
const ptrLow = statistical_trailing_stop_alloc(low.length);
const ptrClose = statistical_trailing_stop_alloc(close.length);
const ptrLevel = statistical_trailing_stop_alloc(close.length);
const ptrAnchor = statistical_trailing_stop_alloc(close.length);
const ptrBias = statistical_trailing_stop_alloc(close.length);
const ptrChanged = statistical_trailing_stop_alloc(close.length);

statistical_trailing_stop_into(
  ptrHigh,
  ptrLow,
  ptrClose,
  ptrLevel,
  ptrAnchor,
  ptrBias,
  ptrChanged,
  close.length,
  10,
  100,
  "level2",
);

statistical_trailing_stop_free(ptrHigh, high.length);
statistical_trailing_stop_free(ptrLow, low.length);
statistical_trailing_stop_free(ptrClose, close.length);

CUDA Bindings (Rust)

Additional details for the CUDA bindings can be found inside the VectorTA repository.

Performance Analysis

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators