SafeZone Stop

Parameters: period = 22 | mult = 2.5 | max_lookback = 3

Overview

SafeZone Stop creates volatility adjusted trailing stops that adapt to changing market conditions by measuring directional penetration strength. The indicator calculates Wilder smoothed directional movement separately for upward and downward price action, then applies a multiplier to this smoothed value to establish a buffer zone around the position. For long positions, it tracks downside penetrations from previous highs and places stops below the recent low, while short positions use upside penetrations to position stops above recent highs. The calculation applies a rolling maximum or minimum over a lookback window to produce a ratcheting stop that only moves in one direction, preventing whipsaws from minor retracements. This approach ensures stops widen during volatile periods when directional movement increases and tighten during calm markets when movements shrink. Traders value SafeZone stops for automatically adjusting to market conditions without manual intervention, providing protection while allowing profitable trends to develop fully.

Implementation Examples

Compute SafeZone Stop from high/low slices or a Candles struct:

use vectorta::indicators::safezonestop::{safezonestop, SafeZoneStopInput, SafeZoneStopParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// High/Low arrays
let high = vec![101.0, 102.5, 103.0, 102.0, 101.5];
let low  = vec![ 99.5, 100.8, 101.2, 100.0,  99.9];
let params = SafeZoneStopParams { period: Some(22), mult: Some(2.5), max_lookback: Some(3) };
let input = SafeZoneStopInput::from_slices(&high, &low, "long", params);
let out = safezonestop(&input)?;

// Candles input (defaults: period=22, mult=2.5, max_lookback=3, direction="long")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SafeZoneStopInput::with_default_candles(&candles);
let out = safezonestop(&input)?;

// Access stop values
for v in out.values { println!("SafeZone: {}", v); }

API Reference

Input Methods
// From candles (uses 'high'/'low' sources)
SafeZoneStopInput::from_candles(&Candles, &str /* direction: "long"|"short" */, SafeZoneStopParams) -> SafeZoneStopInput

// From high/low slices
SafeZoneStopInput::from_slices(&[f64], &[f64], &str /* direction */, SafeZoneStopParams) -> SafeZoneStopInput

// Candles with defaults (period=22, mult=2.5, max_lookback=3, direction="long")
SafeZoneStopInput::with_default_candles(&Candles) -> SafeZoneStopInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct SafeZoneStopParams {
    pub period: Option<usize>,      // Default: 22
    pub mult: Option<f64>,          // Default: 2.5
    pub max_lookback: Option<usize> // Default: 3
}
Output Structure
#[derive(Debug, Clone)]
pub struct SafeZoneStopOutput {
    pub values: Vec<f64>, // Stop levels (long or short depending on direction)
}
Validation, Warmup & NaNs
  • High/low lengths must match; otherwise MismatchedLengths.
  • period > 0 and period ≤ len; else InvalidPeriod.
  • direction must be "long" or "short"; else InvalidDirection.
  • First valid index is the earliest bar where both high/low are finite; all-leading-NaN inputs yield AllValuesNaN.
  • Require at least max(period+1, max_lookback) valid values after the first valid pair; else NotEnoughValidData.
  • Warmup: outputs are NaN until DM bootstrap and lookback are satisfied (prefix length ≈ first + max(period, max_lookback−1)).
Error Handling
use vectorta::indicators::safezonestop::SafeZoneStopError;

match safezonestop(&input) {
    Ok(output) => process(output.values),
    Err(SafeZoneStopError::AllValuesNaN) => eprintln!("All inputs are NaN"),
    Err(SafeZoneStopError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for length {}", period, data_len),
    Err(SafeZoneStopError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, found {}", needed, valid),
    Err(SafeZoneStopError::MismatchedLengths) => eprintln!("High/low length mismatch"),
    Err(SafeZoneStopError::InvalidDirection) => eprintln!("Direction must be 'long' or 'short'"),
}

Python Bindings

Basic Usage

Calculate SafeZone Stop from NumPy arrays:

import numpy as np
from vectorta import safezonestop

high = np.array([101.0, 102.5, 103.0, 102.0, 101.5])
low  = np.array([ 99.5, 100.8, 101.2, 100.0,  99.9])

# Args: high, low, period, mult, max_lookback, direction, kernel(optional)
stops = safezonestop(high, low, 22, 2.5, 3, 'long', kernel='auto')
print(stops)
Streaming Real-time Updates
from vectorta import SafeZoneStopStream

stream = SafeZoneStopStream(22, 2.5, 3, 'long')
for (h, l) in feed:
    val = stream.update(h, l)
    if val is not None:
        handle_stop(val)
Batch Parameter Optimization
import numpy as np
from vectorta import safezonestop_batch

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

res = safezonestop_batch(
    high, low,
    period_range=(14, 28, 7),
    mult_range=(1.5, 3.0, 0.5),
    max_lookback_range=(2, 6, 2),
    direction='long',
    kernel='auto'
)

values = res['values']      # shape: (rows, len)
periods = res['periods']
mults = res['mults']
max_lookbacks = res['max_lookbacks']

JavaScript/WASM

Basic Usage

Compute SafeZone Stop using WASM bindings:

import { safezonestop_js } from 'vectorta-wasm';

const high = new Float64Array([101, 102.5, 103, 102, 101.5]);
const low  = new Float64Array([ 99.5, 100.8, 101.2, 100,  99.9]);

// Args: high, low, period, mult, max_lookback, direction
const stops = safezonestop_js(high, low, 22, 2.5, 3, 'long');
console.log(stops);
Memory-Efficient Operations

Zero-copy compute into preallocated buffers:

import { safezonestop_alloc, safezonestop_free, safezonestop_into, memory } from 'vectorta-wasm';

const len = high.length;
const highPtr = safezonestop_alloc(len);
const lowPtr  = safezonestop_alloc(len);
const outPtr  = safezonestop_alloc(len);

new Float64Array(memory.buffer, highPtr, len).set(high);
new Float64Array(memory.buffer, lowPtr,  len).set(low);

// Args: high_ptr, low_ptr, out_ptr, len, period, mult, max_lookback, direction
safezonestop_into(highPtr, lowPtr, outPtr, len, 22, 2.5, 3, 'long');

const out = new Float64Array(memory.buffer, outPtr, len).slice();

safezonestop_free(highPtr, len);
safezonestop_free(lowPtr, len);
safezonestop_free(outPtr, len);
Batch Processing

Test many parameter combos in one call:

import { safezonestop_batch } from 'vectorta-wasm';

const config = {
  period_range: [14, 28, 7],
  mult_range: [1.5, 3.0, 0.5],
  max_lookback_range: [2, 6, 2],
  direction: 'long',
};

const out = safezonestop_batch(high, low, config);
console.log(out.rows, out.cols);
console.log(out.combos); // SafeZoneStopParams per row
// out.values is flat: rows*cols

CUDA

CUDA support for SafeZone Stop is coming soon.

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators