Deviation Stop

Parameters: period = 20 | mult = 0 (0–5) | devtype = 0 (0–2) | direction = long | ma_type = sma

Overview

Deviation Stop creates an adaptive trailing stop that adjusts to market volatility by combining range measurements with statistical deviation calculations. The indicator first measures the two bar price range, capturing the distance between consecutive highs and lows. This range is then smoothed using your chosen moving average type to filter out erratic spikes. A deviation measure of this smoothed range is calculated and multiplied by your specified factor, then added to create a volatility adjusted offset. The result undergoes a final transformation using rolling extremes that ensures the stop only moves favorably, never retreating against your position.

DevStop offers extensive customization through multiple parameters that control its behavior. The deviation type selection allows choosing between standard deviation for sensitivity, mean absolute deviation for stability, or median absolute deviation for outlier resistance. The multiplier parameter determines how far the stop trails from price, with higher values providing more breathing room but potentially giving back larger profits. Direction settings accommodate both long and short positions, while the moving average type selection lets you fine tune the smoothing characteristics. This flexibility makes DevStop adaptable to various trading styles and market conditions.

Traders employ DevStop as a sophisticated position management tool that automatically adapts to changing volatility. During calm markets, the stop tightens to protect profits, while volatile periods see it widen to avoid premature exits. The ratcheting mechanism ensures the stop locks in gains as trends extend, providing systematic profit protection without manual adjustment. Many traders prefer DevStop over fixed percentage or ATR based stops because it combines multiple volatility measures for more nuanced market adaptation. The indicator works particularly well in trending markets where its adaptive nature helps capture extended moves while protecting against reversals.

Implementation Examples

Compute DevStop from high/low arrays or from a Candles struct:

use vectorta::indicators::devstop::{devstop, DevStopInput, DevStopParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with high/low slices
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 = DevStopParams {
    period: Some(20),
    mult: Some(0.0),
    devtype: Some(0),
    direction: Some("long".to_string()),
    ma_type: Some("sma".to_string()),
};
let input = DevStopInput::from_slices(&high, &low, params);
let result = devstop(&input)?;

// Using Candles (defaults: high/low sources, period=20, mult=0.0, devtype=0, direction="long", ma_type="sma")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DevStopInput::with_default_candles(&candles);
let result = devstop(&input)?;

// Access the trailing stop values
for v in result.values { println!("DevStop: {}", v); }

API Reference

Input Methods
// From high/low slices
DevStopInput::from_slices(&[f64], &[f64], DevStopParams) -> DevStopInput

// From candles with custom sources
DevStopInput::from_candles(&Candles, &str /* high */, &str /* low */, DevStopParams) -> DevStopInput

// From candles with defaults (sources: "high"/"low"; params: period=20, mult=0.0, devtype=0, direction="long", ma_type="sma")
DevStopInput::with_default_candles(&Candles) -> DevStopInput
Parameters Structure
#[derive(Debug, Clone)]
pub struct DevStopParams {
    pub period: Option<usize>,   // Default: 20
    pub mult: Option<f64>,       // Default: 0.0
    pub devtype: Option<usize>,  // Default: 0 (0=std, 1=mean abs, 2=median abs)
    pub direction: Option<String>, // Default: "long"
    pub ma_type: Option<String>,   // Default: "sma"
}
Output Structure
#[derive(Debug, Clone)]
pub struct DevStopOutput {
    pub values: Vec<f64>, // Final trailing stop line (ratcheted)
}
Validation, Warmup & NaNs
  • period > 0 and not greater than data length; otherwise DevStopError::InvalidPeriod.
  • Requires at least period valid points after the first finite high/low; otherwise DevStopError::NotEnoughValidData.
  • devtype ∈ {0,1,2}; other values error via DevStopError::DevStopCalculation("invalid devtype").
  • Warmup: indices before first + 2·period − 1 are NaN (two stages: base and trailing extremum).
  • direction is case‑insensitive; non‑"long" values are treated as short path.
Error Handling
use vectorta::indicators::devstop::{devstop, DevStopError};

match devstop(&input) {
    Ok(out) => use_values(out.values),
    Err(DevStopError::AllValuesNaN) => eprintln!("All values are NaN for high/low"),
    Err(DevStopError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for data_len {}", period, data_len),
    Err(DevStopError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points after first finite; only {}", needed, valid),
    Err(DevStopError::DevStopCalculation(msg)) => eprintln!("Calculation error: {}", msg),
}

Python Bindings

Basic Usage

Compute DevStop using NumPy arrays (all parameters explicit):

import numpy as np
from vectorta import devstop

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

# period, mult, devtype, direction, ma_type, kernel (optional)
stops = devstop(high, low, 20, 0.0, 0, 'long', 'sma', kernel='auto')
print(stops)
Batch Parameter Sweep

Test multiple parameter combinations:

import numpy as np
from vectorta import devstop_batch

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

res = devstop_batch(
    high,
    low,
    period_range=(10, 30, 10),   # 10, 20, 30
    mult_range=(0.0, 3.0, 1.0),  # 0.0, 1.0, 2.0, 3.0
    devtype_range=(0, 2, 1),     # 0, 1, 2
    kernel='auto'
)

print(res['values'].shape)  # (num_combos, len(series))
print(res['periods'])
print(res['mults'])
print(res['devtypes'])

JavaScript / WASM

Basic Usage

Compute DevStop with typed arrays:

import { devstop } 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, devtype, direction, ma_type
const stops = devstop(high, low, 20, 0.0, 0, 'long', 'sma');
console.log(stops);
Memory-Efficient Operations

Use zero‑copy with explicit alloc/free:

import { devstop_alloc, devstop_free, devstop_into, memory } from 'vectorta-wasm';

const len = high.length;
const highPtr = devstop_alloc(len);
const lowPtr  = devstop_alloc(len);
const outPtr  = devstop_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, devtype, direction, ma_type
devstop_into(highPtr, lowPtr, outPtr, len, 20, 0.0, 0, 'long', 'sma');

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

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

Sweep parameter combinations in one call:

import { devstop_batch } from 'vectorta-wasm';

const config = {
  period_range: [10, 30, 10],
  mult_range: [0.0, 2.0, 1.0],
  devtype_range: [0, 2, 1]
};

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

CUDA

CUDA support for DevStop is coming soon.

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators