Fibonacci Trailing Stop

Parameters: left_bars = 20 | right_bars = 1 | level = -0.382 | trigger = close

Overview

Fibonacci Trailing Stop is a stateful stop engine built from confirmed pivots rather than ATR envelopes or moving averages. It watches rolling highs and lows, confirms pivot points using the left-bar and right-bar windows, and then projects long and short stop levels from those pivots using the configured Fibonacci level. The active trailing stop follows whichever side is currently in control.

That produces four outputs: the active `trailing_stop`, the directional `long_stop` and `short_stop` components, and a `direction` state. The trigger mode determines whether the stop flip logic reacts only to closes or allows wick penetration. Because pivots need confirmation, the study naturally has an early warmup region where the stop structure is still forming.

Defaults: Fibonacci Trailing Stop uses `left_bars = 20`, `right_bars = 1`, `level = -0.382`, and `trigger = "close"`.

Implementation Examples

Compute the active trailing stop, directional component stops, and direction state from HLC data.

use vector_ta::indicators::fibonacci_trailing_stop::{
    fibonacci_trailing_stop,
    FibonacciTrailingStopInput,
    FibonacciTrailingStopParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let high = vec![101.0, 101.6, 102.0, 101.7, 102.4];
let low = vec![99.8, 100.3, 100.9, 100.8, 101.3];
let close = vec![100.5, 101.1, 101.6, 101.2, 102.0];

let output = fibonacci_trailing_stop(&FibonacciTrailingStopInput::from_slices(
    &high,
    &low,
    &close,
    FibonacciTrailingStopParams {
        left_bars: Some(20),
        right_bars: Some(1),
        level: Some(-0.382),
        trigger: Some("close".to_string()),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = fibonacci_trailing_stop(&FibonacciTrailingStopInput::with_default_candles(&candles))?;

println!("trailing stop = {:?}", output.trailing_stop.last());
println!("long/short = {:?} / {:?}", candle_output.long_stop.last(), candle_output.short_stop.last());
println!("direction = {:?}", candle_output.direction.last());

API Reference

Input Methods
// From candles
FibonacciTrailingStopInput::from_candles(&Candles, FibonacciTrailingStopParams)
    -> FibonacciTrailingStopInput

// From HLC slices
FibonacciTrailingStopInput::from_slices(
    &[f64],
    &[f64],
    &[f64],
    FibonacciTrailingStopParams,
) -> FibonacciTrailingStopInput

// From candles with default parameters
FibonacciTrailingStopInput::with_default_candles(&Candles)
    -> FibonacciTrailingStopInput
Parameters Structure
pub struct FibonacciTrailingStopParams {
    pub left_bars: Option<usize>,   // default 20
    pub right_bars: Option<usize>,  // default 1
    pub level: Option<f64>,         // default -0.382
    pub trigger: Option<String>,    // default "close"
}
Output Structure
pub struct FibonacciTrailingStopOutput {
    pub trailing_stop: Vec<f64>,
    pub long_stop: Vec<f64>,
    pub short_stop: Vec<f64>,
    pub direction: Vec<f64>,
}
Validation, Warmup & NaNs
  • High, low, and close inputs must be non-empty and length-matched.
  • left_bars and right_bars must be valid for the supplied data length.
  • trigger must be either close or wick.
  • The one-shot function requires a contiguous valid run of at least left_bars + right_bars + 1 bars.
  • Streaming resets on any non-finite bar and may emit early values while pivots are still forming.
  • The active trailing stop follows either the long or short side depending on the current direction state.
Builder, Streaming & Batch APIs
// Builder
FibonacciTrailingStopBuilder::new()
    .left_bars(usize)
    .right_bars(usize)
    .level(f64)
    .trigger(&str)?
    .kernel(Kernel)
    .apply(&Candles)

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

FibonacciTrailingStopBuilder::new()
    .into_stream()

// Stream
FibonacciTrailingStopStream::try_new(FibonacciTrailingStopParams)
FibonacciTrailingStopStream::update(f64, f64, f64)
    -> Option<FibonacciTrailingStopPoint>
FibonacciTrailingStopStream::reset()

// Batch
FibonacciTrailingStopBatchBuilder::new()
    .left_bars_range(start, end, step)
    .right_bars_range(start, end, step)
    .level_range(start, end, step)
    .trigger(String)
    .kernel(Kernel)
    .apply_slices(&[f64], &[f64], &[f64])

FibonacciTrailingStopBatchBuilder::new()
    .apply_candles(&Candles)
Error Handling
pub enum FibonacciTrailingStopError {
    EmptyInputData,
    MismatchedInputLengths { high_len: usize, low_len: usize, close_len: usize },
    AllValuesNaN,
    InvalidLeftBars { left_bars: usize, data_len: usize },
    InvalidRightBars { right_bars: usize, data_len: usize },
    InvalidLevel { level: f64 },
    InvalidTrigger { trigger: String },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes a four-array scalar function, a streaming class, and a batch function. The scalar return order is `trailing_stop`, `long_stop`, `short_stop`, then `direction`. Batch returns those same outputs in matrix form plus the parameter grids for each row.

import numpy as np
from vector_ta import fibonacci_trailing_stop, fibonacci_trailing_stop_batch, FibonacciTrailingStopStream

trailing_stop, long_stop, short_stop, direction = fibonacci_trailing_stop(
    np.asarray(high_values, dtype=np.float64),
    np.asarray(low_values, dtype=np.float64),
    np.asarray(close_values, dtype=np.float64),
    left_bars=20,
    right_bars=1,
    level=-0.382,
    trigger="close",
    kernel="auto",
)

stream = FibonacciTrailingStopStream(left_bars=20, right_bars=1, level=-0.382, trigger="close")
print(stream.update(high_values[-1], low_values[-1], close_values[-1]))

batch = fibonacci_trailing_stop_batch(
    np.asarray(high_values, dtype=np.float64),
    np.asarray(low_values, dtype=np.float64),
    np.asarray(close_values, dtype=np.float64),
    left_bars_range=(10, 20, 10),
    right_bars_range=(1, 2, 1),
    level_range=(-0.618, -0.382, 0.236),
    trigger="close",
    kernel="auto",
)

print(batch["trailing_stop"].shape, batch["direction"].shape)
print(batch["left_bars"], batch["right_bars"], batch["levels"], batch["rows"], batch["cols"])

JavaScript/WASM Bindings

The WASM layer exposes object-returning scalar and batch wrappers plus low-level in-place exports. The scalar JavaScript path returns a plain object with the four stop outputs, while batch adds `combos`, `rows`, and `cols`.

import init, {
  fibonacci_trailing_stop_js,
  fibonacci_trailing_stop_batch_js,
} from "/pkg/vector_ta.js";

await init();

const out = fibonacci_trailing_stop_js(
  highValues,
  lowValues,
  closeValues,
  20,
  1,
  -0.382,
  "close",
);

console.log(out.trailing_stop, out.long_stop, out.short_stop, out.direction);

const batch = fibonacci_trailing_stop_batch_js(highValues, lowValues, closeValues, {
  left_bars_range: [10, 20, 10],
  right_bars_range: [1, 2, 1],
  level_range: [-0.618, -0.382, 0.236],
  trigger: "close",
});

console.log(batch.trailing_stop, batch.direction, batch.left_bars, batch.right_bars, batch.levels, batch.rows, batch.cols, batch.combos);

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