Market Structure Trailing Stop

Parameters: length = 14 | increment_factor = 100 | reset_on = CHoCH

Overview

Market Structure Trailing Stop reads market structure directly from OHLC bars. It scans for pivot highs and lows, promotes those pivots into structure events, and then advances a trailing stop only when the active structure regime remains intact. The result is a stop line that follows confirmed structure rather than raw volatility.

VectorTA returns three aligned outputs: the trailing stop, a directional `state` series, and a `structure` series that reflects the structure classification driving the stop logic. The `reset_on` mode determines whether the stop resets only on change-of-character events or on every qualifying structure event.

Defaults: `length = 14`, `increment_factor = 100.0`, and `reset_on = "CHoCH"`.

Implementation Examples

Run the indicator on aligned OHLC slices or directly from a candle set.

use vector_ta::indicators::market_structure_trailing_stop::{
    market_structure_trailing_stop,
    MarketStructureTrailingStopInput,
    MarketStructureTrailingStopParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = market_structure_trailing_stop(&MarketStructureTrailingStopInput::from_slices(
    &open,
    &high,
    &low,
    &close,
    MarketStructureTrailingStopParams {
        length: Some(14),
        increment_factor: Some(100.0),
        reset_on: Some("CHoCH".to_string()),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = market_structure_trailing_stop(
    &MarketStructureTrailingStopInput::with_default_candles(&candles),
)?;

println!("trailing stop = {:?}", output.trailing_stop.last());
println!("state = {:?}", candle_output.state.last());
println!("structure = {:?}", candle_output.structure.last());

API Reference

Input Methods
// From candles
MarketStructureTrailingStopInput::from_candles(&Candles, MarketStructureTrailingStopParams)
    -> MarketStructureTrailingStopInput

// From aligned OHLC slices
MarketStructureTrailingStopInput::from_slices(&[f64], &[f64], &[f64], &[f64], MarketStructureTrailingStopParams)
    -> MarketStructureTrailingStopInput

// From candles with default parameters
MarketStructureTrailingStopInput::with_default_candles(&Candles)
    -> MarketStructureTrailingStopInput
Parameters Structure
pub struct MarketStructureTrailingStopParams {
    pub length: Option<usize>,           // default 14
    pub increment_factor: Option<f64>,   // default 100.0
    pub reset_on: Option<String>,        // "CHoCH" or "All"
}
Output Structure
pub struct MarketStructureTrailingStopOutput {
    pub trailing_stop: Vec<f64>,
    pub state: Vec<f64>,
    pub structure: Vec<f64>,
}
Validation, Warmup & NaNs
  • All four OHLC series must be non-empty and have identical lengths.
  • length must be greater than 0.
  • increment_factor must be finite and non-negative.
  • reset_on must resolve to CHoCH or All, case-insensitively.
  • The longest contiguous finite OHLC run must be at least 2 * length + 1 bars.
  • Output slots remain NaN outside valid runs and before enough structure context exists to establish pivots and state.
  • Batch mode only accepts scalar, auto, AVX2, and AVX512 batch-compatible kernels; other kernels raise InvalidKernelForBatch.
Builder, Batch & Into APIs
// Builder
MarketStructureTrailingStopBuilder::new()
    .length(usize)
    .increment_factor(f64)
    .reset_on(&str) -> Result<Self, MarketStructureTrailingStopError>
    .kernel(Kernel)
    .apply(&Candles)

MarketStructureTrailingStopBuilder::new()
    .apply_slices(&[f64], &[f64], &[f64], &[f64])

// Into-buffer scalar API
market_structure_trailing_stop_into_slice(
    &mut [f64],
    &mut [f64],
    &mut [f64],
    &MarketStructureTrailingStopInput,
    Kernel,
)

// Batch
MarketStructureTrailingStopBatchBuilder::new()
    .length_range(usize, usize, usize)
    .length_static(usize)
    .increment_factor_range(f64, f64, f64)
    .increment_factor_static(f64)
    .reset_on(&str) -> Result<Self, MarketStructureTrailingStopError>
    .kernel(Kernel)
    .apply_slices(&[f64], &[f64], &[f64], &[f64])

MarketStructureTrailingStopBatchBuilder::new()
    .apply_candles(&Candles)
Error Handling
pub enum MarketStructureTrailingStopError {
    EmptyInputData,
    InputLengthMismatch { open_len: usize, high_len: usize, low_len: usize, close_len: usize },
    AllValuesNaN,
    InvalidLength { length: usize },
    InvalidIncrementFactor { increment_factor: f64 },
    InvalidResetOn { value: String },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    MismatchedOutputLen { dst_len: usize, expected_len: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
    InvalidInput { msg: String },
}

Python Bindings

Python exposes a scalar OHLC function and a batch sweep. The scalar path returns three NumPy arrays: `trailing_stop`, `state`, and `structure`. Batch returns those three matrices plus the tested lengths, increment factors, reset modes, and output dimensions.

import numpy as np
from vector_ta import (
    market_structure_trailing_stop,
    market_structure_trailing_stop_batch,
)

trailing_stop, state, structure = market_structure_trailing_stop(
    open_values,
    high_values,
    low_values,
    close_values,
    length=14,
    increment_factor=100.0,
    reset_on="CHoCH",
    kernel="auto",
)

batch = market_structure_trailing_stop_batch(
    open_values,
    high_values,
    low_values,
    close_values,
    length_range=(10, 20, 5),
    increment_factor_range=(75.0, 125.0, 25.0),
    reset_on="All",
    kernel="auto",
)

print(batch["trailing_stop"].shape)
print(batch["lengths"])
print(batch["reset_ons"])

JavaScript/WASM Bindings

The WASM surface exposes scalar, batch, and into-buffer entry points. The scalar function returns an object with `trailing_stop`, `state`, and `structure`. Batch returns flattened row-major outputs plus `rows`, `cols`, and the tested parameter combinations in `combos`.

import init, {
  market_structure_trailing_stop_js,
  market_structure_trailing_stop_batch_js,
} from "@vectoralpha/vector_ta";

await init();

const single = market_structure_trailing_stop_js(
  open,
  high,
  low,
  close,
  14,
  100.0,
  "CHoCH",
);

console.log(single.trailing_stop);
console.log(single.state);
console.log(single.structure);

const batch = market_structure_trailing_stop_batch_js(open, high, low, close, {
  length_range: [10, 20, 5],
  increment_factor_range: [75.0, 125.0, 25.0],
  reset_on: "All",
});

console.log(batch.trailing_stop);
console.log(batch.state);
console.log(batch.structure);
console.log(batch.rows, batch.cols);

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