Relative Strength Index (RSI)

Parameters: period = 14

Overview

The Relative Strength Index (RSI) measures momentum by comparing the magnitude of recent gains to recent losses, oscillating between 0 and 100 to reveal whether bullish or bearish momentum dominates. J. Welles Wilder developed RSI in 1978 using a smoothed ratio of average gains to average losses over 14 periods, with his proprietary smoothing technique that weights recent data more heavily than simple averages. The calculation first separates positive and negative price changes, averages each using Wilder's smoothing method, then transforms the ratio into an oscillator bounded between 0 and 100. Readings above 70 traditionally signal overbought conditions where price has risen too far too fast, while values below 30 indicate oversold territory where selling pressure may have exhausted. Traders watch for divergences between RSI and price as early reversal warnings, such as price making new highs while RSI fails to exceed its previous peak. The 50 level acts as a momentum threshold, with sustained readings above confirming bullish bias and values below suggesting bearish control of the market.

Defaults: period = 14.

Implementation Examples

Get started with RSI in just a few lines:

use vector_ta::indicators::rsi::{rsi, RsiInput, RsiParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice
let prices = vec![100.0, 102.0, 101.0, 103.0, 104.5, 104.0];
let params = RsiParams { period: Some(14) }; // default is 14
let input = RsiInput::from_slice(&prices, params);
let result = rsi(&input)?;

// Using with Candles (defaults: period=14, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = RsiInput::with_default_candles(&candles);
let result = rsi(&input)?;

// Access RSI values
for value in result.values {
    println!("RSI: {}", value);
}

API Reference

Input Methods
// From price slice
RsiInput::from_slice(&[f64], RsiParams) -> RsiInput

// From candles with custom source
RsiInput::from_candles(&Candles, &str, RsiParams) -> RsiInput

// From candles with default params (close, period=14)
RsiInput::with_default_candles(&Candles) -> RsiInput
Parameters Structure
pub struct RsiParams {
    pub period: Option<usize>, // Default: 14
}
Output Structure
pub struct RsiOutput {
    pub values: Vec<f64>, // RSI values (0..=100), warmup as NaN
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ len(data); otherwise RsiError::InvalidPeriod.
  • If all inputs are NaN, returns RsiError::AllValuesNaN.
  • Must have at least period valid values after the first finite input; else RsiError::NotEnoughValidData.
  • Warmup: indices before first + period are NaN; the first non-NaN is at first + period.
  • If AvgGain + AvgLoss == 0 at any step, RSI is 50.0.
  • rsi_into_slice requires dst.len() == data.len(), otherwise RsiError::LengthMismatch.
  • Batch requires a batch kernel; otherwise RsiError::UnsupportedKernel.
Error Handling
use vector_ta::indicators::rsi::RsiError;

match rsi(&input) {
    Ok(output) => process(output.values),
    Err(RsiError::EmptyInputData) => println!("Input data is empty"),
    Err(RsiError::AllValuesNaN) => println!("All input values are NaN"),
    Err(RsiError::InvalidPeriod { period, data_len }) =>
        println!("Invalid period {} for length {}", period, data_len),
    Err(RsiError::NotEnoughValidData { needed, valid }) =>
        println!("Need {} valid points, only {}", needed, valid),
    Err(RsiError::LengthMismatch { dst, src }) =>
        println!("Destination len {} != source len {}", dst, src),
    Err(RsiError::UnsupportedKernel) => println!("Unsupported kernel for batch operation"),
}

Python Bindings

Basic Usage

Calculate RSI using NumPy arrays (default period=14):

import numpy as np
from vector_ta import rsi

prices = np.array([100.0, 102.0, 101.0, 103.0, 104.5])

# Default period (14)
values = rsi(prices, period=14)

# Select kernel ("auto", "scalar", "avx2", "avx512") if available on your platform
values = rsi(prices, period=14, kernel="auto")

print(values)
Streaming Real-time Updates
from vector_ta import RsiStream

stream = RsiStream(period=14)
for price in price_feed:
    rsi_value = stream.update(price)
    if rsi_value is not None:
        print("RSI:", rsi_value)
Batch Parameter Optimization

Test multiple period values efficiently:

import numpy as np
from vector_ta import rsi_batch

prices = np.array([...], dtype=float)

# Define (start, end, step)
period_range = (5, 30, 5)  # 5, 10, 15, 20, 25, 30

results = rsi_batch(prices, period_range, kernel="auto")

print(results["values"].shape)  # (num_periods, len(prices))
print(results["periods"])       # array of tested periods
CUDA Acceleration

CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).

import numpy as np
from vector_ta import rsi_cuda_batch_dev, rsi_cuda_many_series_one_param_dev

# One series (float32)
data_f32 = np.asarray(load_data(), dtype=np.float32)

dev = rsi_cuda_batch_dev(
    data_f32=data_f32,
    period_range=(5, 30, 5),
    device_id=0,
)

# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)

dev_tm = rsi_cuda_many_series_one_param_dev(
    data_tm_f32=data_tm_f32,
    period=14,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Calculate RSI with the WASM module:

import { rsi_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 102.0, 101.0, 103.0]);
const values = rsi_js(prices, 14); // period=14
console.log('RSI:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { rsi_alloc, rsi_free, rsi_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const len = prices.length;

const inPtr = rsi_alloc(len);
const outPtr = rsi_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(prices);

// Args: in_ptr, out_ptr, len, period
rsi_into(inPtr, outPtr, len, 14);

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

rsi_free(inPtr, len);
rsi_free(outPtr, len);

console.log('RSI:', out);
Batch Processing

Test multiple period values with a single call:

import { rsi_batch } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

// config = { period_range: [start, end, step] }
const config = { period_range: [5, 30, 5] };
const res = rsi_batch(prices, config);

// res = { values: Float64Array, combos: RsiParams[], rows, cols }
// values are flattened row-major: rows=num_combos, cols=prices.length
console.log(res.rows, res.cols, res.combos);

CUDA Bindings (Rust)

use vector_ta::cuda::CudaRsi;
use vector_ta::indicators::rsi::RsiBatchRange;

let cuda = CudaRsi::new(0)?;

let prices_f32: [f32] = /* ... */;
let sweep = RsiBatchRange::default();

let out = cuda.rsi_batch_dev(&prices_f32, &sweep)?;
let _ = out;

Performance Analysis

Comparison:
View:

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

Loading chart...

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

Related Indicators