Stochastic Distance

Parameters: lookback_length = 200 | length1 = 12 | length2 = 3 | ob_level = 40 | os_level = -40

Overview

Stochastic Distance evaluates where the current price sits inside a broad rolling range, then turns that distance into an oscillator with a companion signal line. Instead of outputting the classic stochastic percentage directly, it focuses on the distance relationship and gives you configurable overbought and oversold guide rails so the same study can be interpreted as either a mean-reversion or momentum-following oscillator.

In VectorTA the indicator works on a single price slice or on candle closes, supports a streaming update path for live bars, and exposes batch sweeps across the long lookback, the two smoothing lengths, and both threshold levels. That makes it practical for regime studies where you want to compare how the same distance oscillator behaves under different range horizons and threshold settings.

Defaults: `lookback_length = 200`, `length1 = 12`, `length2 = 3`, `ob_level = 40`, and `os_level = -40`.

Implementation Examples

Run the indicator on a direct price slice or on candle closes using the default thresholds.

use vector_ta::indicators::stochastic_distance::{
    stochastic_distance,
    StochasticDistanceInput,
    StochasticDistanceParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let direct = stochastic_distance(&StochasticDistanceInput::from_slice(
    &close,
    StochasticDistanceParams {
        lookback_length: Some(200),
        length1: Some(12),
        length2: Some(3),
        ob_level: Some(40),
        os_level: Some(-40),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let from_candles = stochastic_distance(
    &StochasticDistanceInput::with_default_candles(&candles),
)?;

println!("latest oscillator = {:?}", direct.oscillator.last());
println!("latest signal = {:?}", direct.signal.last());
println!("candle signal = {:?}", from_candles.signal.last());

API Reference

Input Methods
StochasticDistanceInput::from_candles(&Candles, StochasticDistanceParams)
    -> StochasticDistanceInput

StochasticDistanceInput::from_slice(&[f64], StochasticDistanceParams)
    -> StochasticDistanceInput

StochasticDistanceInput::with_default_candles(&Candles)
    -> StochasticDistanceInput
Parameters Structure
pub struct StochasticDistanceParams {
    pub lookback_length: Option<usize>,  // default 200
    pub length1: Option<usize>,          // default 12
    pub length2: Option<usize>,          // default 3
    pub ob_level: Option<i32>,           // default 40
    pub os_level: Option<i32>,           // default -40
}
Output Structure
pub struct StochasticDistanceOutput {
    pub oscillator: Vec<f64>,
    pub signal: Vec<f64>,
}
Validation, Thresholds & NaNs
  • The source slice must not be empty or entirely NaN, and the resolved lookback and smoothing lengths must fit the available data.
  • lookback_length, length1, and length2 are validated independently and return the documented length errors when they do not fit.
  • os_level must be strictly less than ob_level or the indicator returns the threshold validation error.
  • The stream path returns None until enough valid values arrive to fill the rolling range and both smoothing stages.
  • Output buffers must match the source length or the indicator returns the documented output-length mismatch error.
  • Batch mode validates every axis and rejects non-batch kernels.
Builder, Streaming & Batch APIs
StochasticDistanceBuilder::new()
    .lookback_length(usize)
    .length1(usize)
    .length2(usize)
    .ob_level(i32)
    .os_level(i32)
    .kernel(Kernel)
    .apply(&Candles)
    .apply_slice(&[f64])
    .into_stream()

StochasticDistanceStream::try_new(params)
stream.update(value) -> Option<(f64, f64)>

StochasticDistanceBatchBuilder::new()
    .lookback_length(start, end, step)
    .length1(start, end, step)
    .length2(start, end, step)
    .ob_level(start, end, step)
    .os_level(start, end, step)
    .kernel(Kernel)
    .apply_slice(&[f64])

Python Bindings

Python exposes a direct function, a streaming class, and a batch helper that returns oscillator and signal matrices alongside the resolved sweep axes for the lookback, smoothing, and threshold settings.

from vector_ta import (
    stochastic_distance,
    stochastic_distance_batch,
    StochasticDistanceStream,
)

oscillator, signal = stochastic_distance(
    close,
    lookback_length=200,
    length1=12,
    length2=3,
    ob_level=40,
    os_level=-40,
)

stream = StochasticDistanceStream(
    lookback_length=200,
    length1=12,
    length2=3,
    ob_level=40,
    os_level=-40,
)
point = stream.update(close[-1])

batch = stochastic_distance_batch(
    close,
    lookback_length_range=(150, 200, 25),
    length1_range=(10, 14, 2),
    length2_range=(3, 5, 1),
    ob_level_range=(35, 45, 5),
    os_level_range=(-45, -35, 5),
)

print(batch["oscillator"].shape)
print(batch["lookback_lengths"])

JavaScript/WASM Bindings

The WASM layer exposes a high-level single-call API, an object-returning batch helper, and low-level allocation and into-buffer entry points for caller-managed memory.

import init, {
  stochastic_distance_js,
  stochastic_distance_batch_js,
  stochastic_distance_alloc,
  stochastic_distance_free,
  stochastic_distance_into,
  stochastic_distance_batch_into,
} from "vector-ta-wasm";

await init();

const single = stochastic_distance_js(close, 200, 12, 3, 40, -40);
console.log(single.oscillator, single.signal);

const batch = stochastic_distance_batch_js(close, {
  lookback_length_range: [150, 200, 25],
  length1_range: [10, 14, 2],
  length2_range: [3, 5, 1],
  ob_level_range: [35, 45, 5],
  os_level_range: [-45, -35, 5],
});

const ptrIn = stochastic_distance_alloc(close.length);
const ptrOsc = stochastic_distance_alloc(close.length);
const ptrSig = stochastic_distance_alloc(close.length);
stochastic_distance_into(ptrIn, ptrOsc, ptrSig, close.length, 200, 12, 3, 40, -40);
stochastic_distance_free(ptrIn, close.length);
stochastic_distance_free(ptrOsc, close.length);
stochastic_distance_free(ptrSig, close.length);

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