Stochastic Oscillator (Stoch)

Parameters: fastk_period = 14 | slowk_period = 3 | slowk_ma_type = sma | slowd_period = 3 | slowd_ma_type = sma

Overview

The Stochastic Oscillator evaluates momentum by positioning the current close within the recent high-low range. The raw %K calculation finds the highest high and lowest low over the fast period, then determines where the current close sits within that range on a 0-100 scale. This raw %K value undergoes smoothing to produce the slow %K line, which then receives additional smoothing to generate the %D signal line. The resulting dual-line oscillator identifies overbought conditions above 80 and oversold conditions below 20. Traders watch for %K and %D line crossovers to signal momentum shifts, with bullish crosses above 20 suggesting buying opportunities and bearish crosses below 80 indicating selling pressure. VectorTA provides flexible moving average type selection for both smoothing stages, defaulting to SMA(3) for both. The configurable smoothing allows adaptation from highly sensitive fast stochastics to smoother slow stochastics depending on trading timeframe and volatility environment.

Implementation Examples

Compute %K and %D from slices or candles:

use vectorta::indicators::stoch::{stoch, StochInput, StochParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// From high/low/close slices
let high = vec![/* ... */];
let low = vec![/* ... */];
let close = vec![/* ... */];
let params = StochParams { fastk_period: Some(14), slowk_period: Some(3), slowk_ma_type: Some("sma".into()), slowd_period: Some(3), slowd_ma_type: Some("sma".into()) };
let input = StochInput::from_slices(&high, &low, &close, params);
let out = stoch(&input)?; // out.k, out.d

// From Candles with defaults (fastk=14, slowk=3 SMA, slowd=3 SMA)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = StochInput::with_default_candles(&candles);
let out = stoch(&input)?;

API Reference

Input Methods
// From candles (uses high/low/close fields)
StochInput::from_candles(&Candles, StochParams) -> StochInput

// From high/low/close slices
StochInput::from_slices(&[f64], &[f64], &[f64], StochParams) -> StochInput

// Defaults: fastk=14, slowk=3 (SMA), slowd=3 (SMA)
StochInput::with_default_candles(&Candles) -> StochInput
Parameters Structure
pub struct StochParams {
    pub fastk_period: Option<usize>,   // Default: 14
    pub slowk_period: Option<usize>,   // Default: 3
    pub slowk_ma_type: Option<String>, // Default: "sma"
    pub slowd_period: Option<usize>,   // Default: 3
    pub slowd_ma_type: Option<String>, // Default: "sma"
}
Output Structure
pub struct StochOutput {
    pub k: Vec<f64>, // %K (smoothed by slowk)
    pub d: Vec<f64>, // %D (MA of K)
}
Validation, Warmup & NaNs
  • fastk_period > 0, slowk_period > 0, slowd_period > 0; otherwise StochError::InvalidPeriod.
  • All input series must have equal length; else StochError::MismatchedLength. Empty inputs => EmptyData.
  • First finite H/L/C index = first. Need at least fastk_period valid values after first or NotEnoughValidData.
  • Warmup indices are NaN by design: raw %K starts at first + fastk_period - 1, K at + slowk_period - 1, D at + slowd_period - 1.
  • If HH − LL ≈ 0, %K is set to 50.0 for that bar.
Error Handling
use vectorta::indicators::stoch::{stoch, StochError};

match stoch(&input) {
    Ok(out) => consume(out.k, out.d),
    Err(StochError::EmptyData) => eprintln!("no data"),
    Err(StochError::MismatchedLength) => eprintln!("mismatched series lengths"),
    Err(StochError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(StochError::InvalidPeriod { period, data_len }) => eprintln!("invalid period {} for len {}", period, data_len),
    Err(StochError::NotEnoughValidData { needed, valid }) => eprintln!("need {} valid points after first finite, found {}", needed, valid),
    Err(StochError::Other(e)) => eprintln!("error: {}", e),
}

Python Bindings

Basic Usage

Compute %K and %D from NumPy arrays:

import numpy as np
from vectorta import stoch

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

k, d = stoch(
    high, low, close,
    fastk_period=14,
    slowk_period=3,
    slowk_ma_type='sma',
    slowd_period=3,
    slowd_ma_type='sma',
    kernel='auto'
)
print(k[-3:], d[-3:])
Streaming Real-time Updates
from vectorta import StochStream

stream = StochStream(14, 3, 'sma', 3, 'sma')
for (h, l, c) in hlc_feed:
    upd = stream.update(h, l, c)
    if upd is not None:
        k, d = upd
        handle(k, d)
Batch Parameter Sweep
import numpy as np
from vectorta import stoch_batch

hi = np.array([...], dtype=float)
lo = np.array([...], dtype=float)
cl = np.array([...], dtype=float)

res = stoch_batch(
    hi, lo, cl,
    fastk_range=(10, 20, 5),
    slowk_range=(3, 5, 1),
    slowk_ma_type='sma',
    slowd_range=(3, 5, 1),
    slowd_ma_type='sma',
    kernel='auto'
)

k = res['k']  # shape: (num_combos, len(cl))
d = res['d']
fk = res['fastk_periods']; sk = res['slowk_periods']
kt = res['slowk_types'];   sd = res['slowd_periods']; dt = res['slowd_types']

JavaScript/WASM Bindings

Basic Usage

Compute %K and %D in JavaScript/TypeScript:

import { stoch } from 'vectorta-wasm';

const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
const close = new Float64Array([/* ... */]);

// Args: high, low, close, fastk, slowk, slowk_ma_type, slowd, slowd_ma_type
const out = stoch(high, low, close, 14, 3, 'sma', 3, 'sma');

// out: { values: Float64Array, rows: 2, cols: N }
const cols = out.cols;
const values = out.values; // [K row..., D row...]
const k = values.slice(0, cols);
const d = values.slice(cols, 2 * cols);
Memory-Efficient Operations

Use zero‑copy operations with explicit alloc/free:

import { stoch_alloc, stoch_free, stoch_into, memory } from 'vectorta-wasm';

const len = close.length;
const hiPtr = stoch_alloc(len);
const loPtr = stoch_alloc(len);
const clPtr = stoch_alloc(len);
const kPtr  = stoch_alloc(len);
const dPtr  = stoch_alloc(len);

new Float64Array(memory.buffer, hiPtr, len).set(high);
new Float64Array(memory.buffer, loPtr, len).set(low);
new Float64Array(memory.buffer, clPtr, len).set(close);

// Args: hi_ptr, lo_ptr, cl_ptr, len, fastk, slowk, slowk_ma, slowd, slowd_ma, out_k_ptr, out_d_ptr
stoch_into(hiPtr, loPtr, clPtr, len, 14, 3, 'sma', 3, 'sma', kPtr, dPtr);

const k = new Float64Array(memory.buffer, kPtr, len).slice();
const d = new Float64Array(memory.buffer, dPtr, len).slice();

stoch_free(hiPtr, len); stoch_free(loPtr, len); stoch_free(clPtr, len);
stoch_free(kPtr,  len); stoch_free(dPtr,  len);
Batch Processing

Test multiple parameter combinations efficiently:

import { stoch_batch } from 'vectorta-wasm';

const hi = new Float64Array([/* ... */]);
const lo = new Float64Array([/* ... */]);
const cl = new Float64Array([/* ... */]);

// Args: hi, lo, cl, fastk_start, fastk_end, fastk_step, slowk_start, slowk_end, slowk_step, slowk_type, slowd_start, slowd_end, slowd_step, slowd_type
const res = stoch_batch(hi, lo, cl, 10, 20, 5, 3, 5, 1, 'sma', 3, 5, 1, 'sma');

// res: { values, combos, rows_per_combo: 2, cols }
const cols = res.cols;
const combos = res.combos; // array of StochParams objects
const values = res.values; // flattened [all K rows..., then all D rows...]

// Example: extract the first combo's K and D
const k0 = values.slice(0, cols);
const d0 = values.slice(values.length/2, values.length/2 + cols);

CUDA Acceleration

CUDA support for Stoch is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated Stoch calculations
# from vectorta import stoch_cuda_batch_dev, stoch_cuda_many_series_one_param_dev
# ...

Performance Analysis

Comparison:
View:

Across sizes, Rust CPU runs about 1.05× faster than Tulip C in this benchmark.

Loading chart...

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

Related Indicators