Adaptive Bounds RSI

Parameters: rsi_length = 14 | alpha = 0.1

Overview

Adaptive Bounds RSI starts with a conventional RSI calculation and then wraps that oscillator in a set of five moving guide levels. Instead of keeping the levels fixed at static overbought and oversold zones, the indicator shifts each level toward the nearest observed RSI state using an adaptive step. That produces a structure of lower bound, lower mid, center, upper mid, and upper bound that can expand, contract, and re-center as market behavior changes.

The result is more than a single oscillator line. You get the RSI itself, the adaptive band structure, a regime classification from deeply oversold to strongly overbought, regime-flip markers when the neutral state is abandoned, and one-shot lower and upper trigger signals when RSI crosses the outer adaptive bounds. That makes the indicator useful both for regime tracking and for detecting fresh excursions into adaptive extremes.

Defaults: Adaptive Bounds RSI uses `rsi_length = 14` and `alpha = 0.1`.

Implementation Examples

Compute the adaptive RSI structure from a slice or from a candle source field.

use vector_ta::indicators::adaptive_bounds_rsi::{
    adaptive_bounds_rsi,
    AdaptiveBoundsRsiInput,
    AdaptiveBoundsRsiParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let close = vec![100.0, 101.1, 102.0, 101.7, 102.8, 103.5, 102.9, 103.8];

let output = adaptive_bounds_rsi(&AdaptiveBoundsRsiInput::from_slice(
    &close,
    AdaptiveBoundsRsiParams {
        rsi_length: Some(14),
        alpha: Some(0.1),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = adaptive_bounds_rsi(
    &AdaptiveBoundsRsiInput::with_default_candles(&candles)
)?;

println!("Latest RSI: {:?}", output.rsi.last());
println!("Latest regime: {:?}", output.regime.last());
println!("Latest upper signal: {:?}", candle_output.upper_signal.last());

API Reference

Input Methods
// From candles and a named source field
AdaptiveBoundsRsiInput::from_candles(&Candles, &str, AdaptiveBoundsRsiParams)
    -> AdaptiveBoundsRsiInput

// From a slice
AdaptiveBoundsRsiInput::from_slice(&[f64], AdaptiveBoundsRsiParams)
    -> AdaptiveBoundsRsiInput

// From candles with default parameters
AdaptiveBoundsRsiInput::with_default_candles(&Candles)
    -> AdaptiveBoundsRsiInput
Parameters Structure
pub struct AdaptiveBoundsRsiParams {
    pub rsi_length: Option<usize>, // default 14
    pub alpha: Option<f64>,        // default 0.1
}
Output Structure
pub struct AdaptiveBoundsRsiOutput {
    pub rsi: Vec<f64>,
    pub lower_bound: Vec<f64>,
    pub lower_mid: Vec<f64>,
    pub mid: Vec<f64>,
    pub upper_mid: Vec<f64>,
    pub upper_bound: Vec<f64>,
    pub regime: Vec<f64>,
    pub regime_flip: Vec<f64>,
    pub lower_signal: Vec<f64>,
    pub upper_signal: Vec<f64>,
}
Validation, Warmup & NaNs
  • The input series must be non-empty and contain at least one finite value.
  • rsi_length must be greater than zero and no larger than the data length.
  • alpha must be finite and lie within 0.001 to 1.0.
  • The indicator needs at least rsi_length + 1 valid values after the first finite sample to seed the RSI state.
  • Warmup lasts until first_valid_index + rsi_length. Before that point all outputs remain NaN.
  • Non-finite values reset the streaming RSI state and the adaptive regime state.
  • Batch mode accepts only batch-capable kernels, and invalid integer or floating-point ranges return InvalidRange.
Builder, Streaming & Batch APIs
// Builder
AdaptiveBoundsRsiBuilder::new()
    .rsi_length(usize)
    .alpha(f64)
    .kernel(Kernel)
    .apply_slice(&[f64])

AdaptiveBoundsRsiBuilder::new()
    .apply(&Candles)

AdaptiveBoundsRsiBuilder::new()
    .into_stream()

// Stream
AdaptiveBoundsRsiStream::try_new(AdaptiveBoundsRsiParams)
AdaptiveBoundsRsiStream::update(f64) -> Option<AdaptiveBoundsRsiStreamOutput>

// Batch
AdaptiveBoundsRsiBatchBuilder::new()
    .range(AdaptiveBoundsRsiBatchRange)
    .kernel(Kernel)
    .apply_slice(&[f64])

AdaptiveBoundsRsiBatchBuilder::new()
    .apply(&Candles)
Error Handling
pub enum AdaptiveBoundsRsiError {
    EmptyInputData,
    AllValuesNaN,
    InvalidRsiLength { rsi_length: usize, data_len: usize },
    InvalidAlpha { alpha: f64 },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernelForBatch(Kernel),
}

Python Bindings

Python exposes a dictionary-returning single-run function, a batch function, and a streaming class. The single-run binding returns NumPy arrays for all ten output families. Batch returns the same ten matrix outputs plus the tested rsi_lengths, alphas, and the final rows and cols shape. The stream returns a ten-value tuple once the RSI core is ready.

import numpy as np
from vector_ta import (
    adaptive_bounds_rsi,
    adaptive_bounds_rsi_batch,
    AdaptiveBoundsRsiStream,
)

data = np.asarray(close_values, dtype=np.float64)

result = adaptive_bounds_rsi(
    data,
    rsi_length=14,
    alpha=0.1,
    kernel="auto",
)

print(result["rsi"][-1], result["upper_bound"][-1], result["regime"][-1])

batch = adaptive_bounds_rsi_batch(
    data,
    rsi_length_range=(10, 14, 2),
    alpha_range=(0.08, 0.12, 0.02),
    kernel="auto",
)

stream = AdaptiveBoundsRsiStream(14, 0.1)
print(stream.update(data[-1]))

JavaScript/WASM Bindings

The WASM layer exposes one object-returning function for normal runs and one for batch sweeps. Both return the full ten-series output surface. The batch path also returns the tested RSI lengths, alphas, and the final matrix shape so the flattened arrays can be reconstructed on the host side.

import init, {
  adaptive_bounds_rsi_js,
  adaptive_bounds_rsi_batch,
} from "/pkg/vector_ta.js";

await init();

const data = new Float64Array(closeValues);

const result = adaptive_bounds_rsi_js(data, 14, 0.1);
console.log(result.rsi, result.lower_bound, result.upper_bound, result.regime);

const batch = adaptive_bounds_rsi_batch(data, {
  rsi_length_range: [10, 14, 2],
  alpha_range: [0.08, 0.12, 0.02],
});

console.log(batch.rsi_lengths, batch.alphas, batch.rows, batch.cols);

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