Stochastic RSI (SRSI)
rsi_period = 14 | stoch_period = 14 | k = 3 (1–20) | d = 3 (1–20) Overview
Stochastic RSI applies the stochastic oscillator formula to RSI values rather than raw prices, creating a double layer of momentum analysis that produces extremely sensitive overbought and oversold signals. The indicator first calculates standard RSI over its period, then measures where the current RSI reading falls within its own high to low range over the stochastic lookback window. This position gets normalized to create the fast K line oscillating between 0 and 100, with a smoothed moving average of K producing the D line. Because SRSI applies momentum analysis to an already momentum based indicator, it reaches extreme levels much more frequently than standard RSI, with readings above 80 signaling overbought and below 20 indicating oversold conditions. The double momentum transformation makes SRSI particularly useful for identifying short term reversals and generating early entry signals, though the heightened sensitivity can produce more false signals in trending markets compared to traditional RSI.
Defaults: rsi_period=14, stoch_period=14, k=3, d=3.
Implementation Examples
Get SRSI K/D in just a few lines:
use vectorta::indicators::srsi::{srsi, SrsiInput, SrsiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Example with a price slice
let prices = vec![100.0, 101.0, 102.5, 101.8, 103.2];
let params = SrsiParams {
rsi_period: Some(14),
stoch_period: Some(14),
k: Some(3),
d: Some(3),
source: None,
};
let input = SrsiInput::from_slice(&prices, params);
let out = srsi(&input)?; // SrsiOutput { k, d }
// Or with Candles and defaults (source = "close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SrsiInput::with_default_candles(&candles);
let out = srsi(&input)?;
// Access K/D
for (k, d) in out.k.iter().zip(out.d.iter()) {
println!("K: {k}, D: {d}");
} API Reference
Input Methods ▼
// From price slice
SrsiInput::from_slice(&[f64], SrsiParams) -> SrsiInput
// From candles with custom source (e.g., "close")
SrsiInput::from_candles(&Candles, &str, SrsiParams) -> SrsiInput
// From candles with defaults (close, 14/14/3/3)
SrsiInput::with_default_candles(&Candles) -> SrsiInput Parameters Structure ▼
pub struct SrsiParams {
pub rsi_period: Option<usize>, // Default: 14
pub stoch_period: Option<usize>, // Default: 14
pub k: Option<usize>, // Default: 3
pub d: Option<usize>, // Default: 3
pub source: Option<String>, // Default: "close"
} Output Structure ▼
pub struct SrsiOutput {
pub k: Vec<f64>, // Slow %K values (0..=100)
pub d: Vec<f64>, // Slow %D values (0..=100)
} Validation, Warmup & NaNs ▼
rsi_period > 0,stoch_period > 0,k > 0,d > 0; otherwiseSrsiError::NotEnoughValidData.- If all inputs are
NaN→SrsiError::AllValuesNaN. Validation uses the index of the first finite value. - Requires at least
max(rsi_period, stoch_period, k, d)valid points after the first finite input; elseSrsiError::NotEnoughValidData. - Warmup indices are filled with
NaNusingalloc_with_nan_prefix:rsi_warmup = first + rsi_period,stoch_warmup = rsi_warmup + stoch_period - 1,k_warmup = stoch_warmup + k - 1,d_warmup = k_warmup + d - 1. srsi_into_slice(k_out, d_out, input, kernel)requires output buffers to match input length; elseSrsiError::SizeMismatch.
Error Handling ▼
use vectorta::indicators::srsi::{srsi, SrsiError, SrsiInput, SrsiParams};
let input = SrsiInput::from_slice(&prices, SrsiParams::default());
match srsi(&input) {
Ok(out) => println!("last K/D: {:?} {:?}", out.k.last(), out.d.last()),
Err(SrsiError::RsiError(e)) => eprintln!("RSI error: {}", e),
Err(SrsiError::StochError(e)) => eprintln!("Stoch error: {}", e),
Err(SrsiError::AllValuesNaN) => eprintln!("All input values are NaN"),
Err(SrsiError::NotEnoughValidData) => eprintln!("Not enough valid data for requested periods"),
Err(SrsiError::SizeMismatch { expected, k_len, d_len }) => {
eprintln!("Size mismatch: expected {}, got k={}, d={}", expected, k_len, d_len)
}
} Python Bindings
Basic Usage ▼
Calculate SRSI K/D using NumPy arrays (defaults 14/14/3/3):
import numpy as np
from vectorta import srsi
prices = np.array([100.0, 101.0, 102.5, 101.8, 103.2])
# Defaults (rsi=14, stoch=14, k=3, d=3)
k, d = srsi(prices)
# Custom parameters and kernel
k, d = srsi(prices, rsi_period=14, stoch_period=14, k=3, d=3, kernel="auto")
print(k[-5:], d[-5:]) Streaming Real-time Updates ▼
Initialize a streaming calculator (update returns None until implemented):
from vectorta import SrsiStream
stream = SrsiStream(rsi_period=14, stoch_period=14, k=3, d=3)
for price in price_feed:
pair = stream.update(price)
if pair is not None:
k, d = pair
print(k, d) Batch Parameter Optimization ▼
Test multiple parameter combinations:
import numpy as np
from vectorta import srsi_batch
prices = np.array([...])
res = srsi_batch(
prices,
rsi_period_range=(10, 20, 5),
stoch_period_range=(10, 20, 5),
k_range=(3, 5, 1),
d_range=(3, 5, 1),
kernel="auto",
)
# Shapes: (rows, cols)
print(res["k"].shape, res["d"].shape)
print(res["rsi_periods"], res["stoch_periods"], res["k_periods"], res["d_periods"]) JavaScript/WASM Bindings
Basic Usage ▼
Compute SRSI in JavaScript/TypeScript:
import { srsi_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 101.0, 102.5, 101.8, 103.2]);
// Args: data, rsi_period, stoch_period, k, d
const flat = srsi_js(prices, 14, 14, 3, 3);
// Returned as flattened [K..., D...]
const len = prices.length;
const k = flat.slice(0, len);
const d = flat.slice(len);
console.log(k, d); Memory-Efficient Operations ▼
Use zero-copy into-variants to write K/D directly:
import { srsi_alloc, srsi_free, srsi_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const len = prices.length;
// Allocate WASM buffers
const inPtr = srsi_alloc(len);
const kPtr = srsi_alloc(len);
const dPtr = srsi_alloc(len);
// Copy input into WASM memory
new Float64Array(memory.buffer, inPtr, len).set(prices);
// Compute directly into output buffers
// Args: in_ptr, k_ptr, d_ptr, len, rsi_period, stoch_period, k, d
srsi_into(inPtr, kPtr, dPtr, len, 14, 14, 3, 3);
// Read back K/D
const K = new Float64Array(memory.buffer, kPtr, len).slice();
const D = new Float64Array(memory.buffer, dPtr, len).slice();
// Free
srsi_free(inPtr, len);
srsi_free(kPtr, len);
srsi_free(dPtr, len); Batch Processing ▼
Unified batch API returns flattened K/D arrays plus metadata:
import { srsi_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
const config = {
rsi_period_range: [10, 20, 5],
stoch_period_range: [10, 20, 5],
k_range: [3, 5, 1],
d_range: [3, 5, 1],
};
const result = srsi_batch(prices, config);
// result: { k_values, d_values, rows, cols, combos }
const { rows, cols, k_values, d_values, combos } = result;
// Reshape the flattened arrays if needed
const K = [] as Float64Array[];
const D = [] as Float64Array[];
for (let r = 0; r < rows; r++) {
const start = r * cols;
K.push(k_values.slice(start, start + cols));
D.push(d_values.slice(start, start + cols));
}
console.log(combos[0]); // First parameter combo as SrsiParams-like object CUDA Acceleration
CUDA support for SRSI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
Performance Analysis
Across sizes, Rust CPU runs about 1.38× slower than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05