FVG Trailing Stop
unmitigated_fvg_lookback = 5 | smoothing_length = 9 | reset_on_cross = Overview
The FVG Trailing Stop identifies price imbalances known as Fair Value Gaps where market inefficiency creates unfilled zones between candles, then constructs adaptive stop loss levels that follow trend direction based on these structural price levels. Fair Value Gaps form when aggressive buying or selling creates a void between consecutive candles, leaving areas where no trading occurred that often act as future support or resistance when price returns to fill them. The indicator tracks recent unmitigated gaps, averaging their levels to create dynamic boundaries that trail price during trends while respecting these structural zones where institutional orders likely reside. When price moves above the bullish FVG boundary, the stop trails upward using the smoothed gap levels as support, protecting profits while allowing the trend room to breathe. Traders employ FVG Trailing Stops to maintain positions through pullbacks that respect gap structure while exiting when price violates these key imbalance zones, signaling potential trend exhaustion. Unlike fixed percentage or volatility based stops, this approach anchors exit levels to actual market structure where supply and demand imbalances occurred, providing more intelligent stop placement that adapts to the underlying order flow dynamics.
Implementation Examples
Compute FVG Trailing Stop from OHLC slices or candles:
use vectorta::indicators::fvg_trailing_stop::{
fvg_trailing_stop, FvgTrailingStopInput, FvgTrailingStopParams
};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with OHLC slices
let high = vec![100.0, 101.0, 102.0, 103.0, 104.0];
let low = vec![ 95.0, 96.0, 97.0, 98.0, 99.0];
let close= vec![ 97.0, 98.0, 99.0, 100.0, 101.0];
let params = FvgTrailingStopParams {
unmitigated_fvg_lookback: Some(5),
smoothing_length: Some(9),
reset_on_cross: Some(false),
};
let input = FvgTrailingStopInput::from_slices(&high, &low, &close, params);
let out = fvg_trailing_stop(&input)?;
// Using with Candles (defaults: lookback=5, smoothing=9, reset_on_cross=false)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = FvgTrailingStopInput::with_default_candles(&candles);
let out = fvg_trailing_stop(&input)?;
// Access the four output series
for i in 0..out.upper.len() {
println!("upper={}, lower={}, upper_ts={}, lower_ts=
{}",
out.upper[i], out.lower[i], out.upper_ts[i], out.lower_ts[i]);
} API Reference
Input Methods ▼
// From OHLC slices
FvgTrailingStopInput::from_slices(&[f64], &[f64], &[f64], FvgTrailingStopParams) -> FvgTrailingStopInput
// From candles (uses high/low/close fields)
FvgTrailingStopInput::from_candles(&Candles, FvgTrailingStopParams) -> FvgTrailingStopInput
// With defaults (lookback=5, smoothing=9, reset_on_cross=false)
FvgTrailingStopInput::with_default_candles(&Candles) -> FvgTrailingStopInput Parameters Structure ▼
#[derive(Debug, Clone)]
pub struct FvgTrailingStopParams {
pub unmitigated_fvg_lookback: Option<usize>, // Default: 5
pub smoothing_length: Option<usize>, // Default: 9
pub reset_on_cross: Option<bool>, // Default: false
} Output Structure ▼
#[derive(Debug, Clone)]
pub struct FvgTrailingStopOutput {
pub upper: Vec<f64>, // Upper boundary (NaN in long regime)
pub lower: Vec<f64>, // Lower boundary (NaN in short regime)
pub upper_ts: Vec<f64>, // Upper trailing stop when short
pub lower_ts: Vec<f64>, // Lower trailing stop when long
} Validation, Warmup & NaNs ▼
- Inputs must be non‑empty and equal‑length; otherwise
FvgTrailingStopError::EmptyInputDataorFvgTrailingStopError::InvalidPeriod. - Requires at least one index with finite
high/low/close; otherwiseFvgTrailingStopError::AllValuesNaN. unmitigated_fvg_lookback > 0elseInvalidLookback;smoothing_length > 0elseInvalidSmoothingLength.- Needs at least
2 + (smoothing_length − 1)valid points after the first finite bar; otherwiseNotEnoughValidData. - Warmup prefix set to
NaNfor all four outputs:warm = first_valid + 2 + (smoothing_length − 1). - Streaming emits
Noneuntilbar_count ≥ smoothing_length + 2, then returns(upper, lower, upper_ts, lower_ts)per bar.
Error Handling ▼
use vectorta::indicators::fvg_trailing_stop::{
fvg_trailing_stop, FvgTrailingStopError, FvgTrailingStopInput, FvgTrailingStopParams
};
let input = FvgTrailingStopInput::from_slices(&high, &low, &close, FvgTrailingStopParams::default());
match fvg_trailing_stop(&input) {
Ok(out) => consume(out),
Err(FvgTrailingStopError::EmptyInputData) => eprintln!("Empty input"),
Err(FvgTrailingStopError::AllValuesNaN) => eprintln!("All OHLC values are NaN"),
Err(FvgTrailingStopError::InvalidPeriod { .. }) => eprintln!("Mismatched lengths or invalid period"),
Err(FvgTrailingStopError::NotEnoughValidData { needed, valid }) => eprintln!("Need {} valid points, only {}", needed, valid),
Err(FvgTrailingStopError::InvalidSmoothingLength { smoothing }) => eprintln!("Invalid smoothing_length: {}", smoothing),
Err(FvgTrailingStopError::InvalidLookback { lookback }) => eprintln!("Invalid lookback: {}", lookback),
} Python Bindings
Basic Usage ▼
Compute FVG Trailing Stop with NumPy arrays:
import numpy as np
from vectorta import fvg_trailing_stop
high = np.array([100, 101, 102, 103, 104], dtype=np.float64)
low = np.array([ 95, 96, 97, 98, 99], dtype=np.float64)
close= np.array([ 97, 98, 99, 100, 101], dtype=np.float64)
upper, lower, upper_ts, lower_ts = fvg_trailing_stop(
high, low, close,
unmitigated_fvg_lookback=5,
smoothing_length=9,
reset_on_cross=False,
kernel="auto", # "auto", "scalar", "avx2", "avx512" (if available)
)
print(upper, lower, upper_ts, lower_ts) Streaming ▼
Process real‑time OHLC bars:
from vectorta import FvgTrailingStopStreamPy as FvgTrailingStopStream
stream = FvgTrailingStopStream(
unmitigated_fvg_lookback=5,
smoothing_length=9,
reset_on_cross=False,
)
for (h, l, c) in stream_feed:
out = stream.update(h, l, c)
if out is not None:
upper, lower, upper_ts, lower_ts = out
consume(upper, lower, upper_ts, lower_ts) Batch Parameter Optimization ▼
Test multiple lookbacks/smoothings and reset flags:
import numpy as np
from vectorta import fvg_trailing_stop_batch
high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
close= np.array([...], dtype=np.float64)
res = fvg_trailing_stop_batch(
high, low, close,
lookback_range=(3, 7, 2),
smoothing_range=(5, 10, 5),
reset_toggle=(True, True),
kernel="auto",
)
# res['values']: shape (rows*4, len)
# res['lookbacks'], res['smoothings'], res['resets'] enumerate tested combos
print(res['values'].shape, res['lookbacks'], res['smoothings'], res['resets']) CUDA Acceleration ▼
CUDA support for FVG Trailing Stop is coming soon.
# Coming soon: CUDA-accelerated FVG Trailing Stop calculations JavaScript/WASM Bindings
Basic Usage ▼
Calculate FVG Trailing Stop in JavaScript/TypeScript:
import { fvgTrailingStop } from 'vectorta-wasm';
const high = new Float64Array([100, 101, 102, 103, 104]);
const low = new Float64Array([ 95, 96, 97, 98, 99]);
const close = new Float64Array([ 97, 98, 99, 100, 101]);
const result = fvgTrailingStop(high, low, close, 5, 9, false);
// { upper: number[], lower: number[], upperTs: number[], lowerTs: number[] }
console.log('upper:', result.upper);
console.log('lower:', result.lower);
console.log('upperTs:', result.upperTs);
console.log('lowerTs:', result.lowerTs); Memory-Efficient Operations ▼
Use zero‑copy and pointer APIs for large datasets:
import { memory, fvg_ts_alloc, fvg_ts_free, fvgTrailingStopAlloc, fvgTrailingStopFree, fvg_trailing_stop_into_flat } from 'vectorta-wasm';
const n = close.length;
// Allocate and copy inputs into WASM memory
const highPtr = fvg_ts_alloc(n);
const lowPtr = fvg_ts_alloc(n);
const closePtr= fvg_ts_alloc(n);
new Float64Array(memory.buffer, highPtr, n).set(high);
new Float64Array(memory.buffer, lowPtr, n).set(low);
new Float64Array(memory.buffer, closePtr,n).set(close);
// Allocate output buffer for the 4 series (upper|lower|upper_ts|lower_ts)
const outPtr = fvgTrailingStopAlloc(n);
// Fill the flat output buffer
fvg_trailing_stop_into_flat(highPtr, lowPtr, closePtr, outPtr, n, 5, 9, false);
// Read back views
const buf = new Float64Array(memory.buffer, outPtr, 4 * n);
const upper = buf.subarray(0, n).slice();
const lower = buf.subarray(n, 2*n).slice();
const upperTs = buf.subarray(2*n, 3*n).slice();
const lowerTs = buf.subarray(3*n, 4*n).slice();
// Free WASM allocations
fvg_ts_free(highPtr, n);
fvg_ts_free(lowPtr, n);
fvg_ts_free(closePtr, n);
fvgTrailingStopFree(outPtr, n); Batch Processing ▼
Test multiple combinations efficiently:
import { fvgTrailingStopBatch } from 'vectorta-wasm';
const out = fvgTrailingStopBatch(high, low, close, 3, 7, 2, 5, 10, 5, true, true);
// out: { values: number[], combos: { unmitigated_fvg_lookback?: number, smoothing_length?: number, reset_on_cross?: boolean }[], rows: number, cols: number }
const { values, combos, rows, cols } = out;
// Extract series for the first combo row
const base = 0 * 4 * cols;
const upper = values.slice(base, base + cols);
const lower = values.slice(base + cols, base + 2*cols);
const upperTs = values.slice(base + 2*cols, base + 3*cols);
const lowerTs = values.slice(base + 3*cols, base + 4*cols); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05