Laguerre RSI (LRSI)
alpha = 0.2 (0.01–0.99) Overview
The Laguerre RSI, developed by John F. Ehlers in his work on cybernetic analysis, applies recursive calculations through four Laguerre filter stages (L0 through L3) to the mid price, creating a momentum oscillator that ranges from 0 to 1 with exceptional noise reduction and minimal lag. Each Laguerre level depends on the previous level and current price input through an IIR filter structure controlled by the alpha parameter, with smaller alpha values producing smoother but laggier results. After filtering, the indicator calculates the ratio of upward movement to total movement across these stages, essentially measuring whether bullish or bearish pressure dominates the filtered price action. Traders typically watch for crosses above 0.15 to signal oversold bounces and crosses below 0.85 to indicate overbought reversals, while horizontal movement at 1 suggests strong uptrends and movement at 0 indicates strong downtrends. Unlike traditional RSI that can generate whipsaws during ranging markets, Laguerre RSI's polynomial filter design captures the essence of price movements while filtering out insignificant fluctuations, making it particularly effective for identifying genuine momentum shifts without false signals.
Implementation Examples
Compute LRSI from candle highs/lows or slices:
use vectorta::indicators::lrsi::{lrsi, LrsiInput, LrsiParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From candles (uses mid-price = (high+low)/2), default alpha=0.2
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = LrsiInput::with_default_candles(&candles);
let result = lrsi(&input)?;
// From high/low slices with explicit alpha
let high = vec![101.0, 102.0, 103.0, 103.5];
let low = vec![ 99.0, 100.5, 101.5, 102.5];
let params = LrsiParams { alpha: Some(0.2) };
let input = LrsiInput::from_slices(&high, &low, params);
let result = lrsi(&input)?;
// Access values (NaN during warmup, then in [0,1])
for v in result.values {
println!("LRSI: {}", v);
} API Reference
Input Methods ▼
// From candles (uses high/low to compute mid-price)
LrsiInput::from_candles(&Candles, LrsiParams) -> LrsiInput
// From separate high/low slices
LrsiInput::from_slices(&[f64], &[f64], LrsiParams) -> LrsiInput
// Default params with candles (alpha=0.2)
LrsiInput::with_default_candles(&Candles) -> LrsiInput Parameters Structure ▼
pub struct LrsiParams {
pub alpha: Option<f64>, // Default: 0.2 (must satisfy 0<alpha<1)
} Output Structure ▼
pub struct LrsiOutput {
pub values: Vec<f64>, // LRSI values (NaN during warmup, else in [0,1])
} Validation, Warmup & NaNs ▼
0 < alpha < 1orLrsiError::InvalidAlpha.- Requires at least
4valid points after the first finite mid-price; otherwiseLrsiError::NotEnoughValidData. - Before warmup
(first_valid_idx + 3), outputs areNaN. After warmup,NaNinputs propagate. - Candles input uses
highandlow; slices must have equal length orLrsiError::EmptyData. lrsi_into_sliceenforces exact output length orLrsiError::OutputLengthMismatch.
Error Handling ▼
use vectorta::indicators::lrsi::{lrsi, LrsiInput, LrsiParams, LrsiError};
let input = LrsiInput::from_slices(&high, &low, LrsiParams { alpha: Some(0.2) });
match lrsi(&input) {
Ok(output) => process(output.values),
Err(LrsiError::EmptyData) => eprintln!("Empty or mismatched input data"),
Err(LrsiError::InvalidAlpha { alpha }) => eprintln!("Invalid alpha: {}", alpha),
Err(LrsiError::AllValuesNaN) => eprintln!("All values are NaN"),
Err(LrsiError::NotEnoughValidData { needed, valid }) => {
eprintln!("Need {} valid points after first finite; got {}", needed, valid)
}
Err(LrsiError::OutputLengthMismatch { expected, provided }) => {
eprintln!("Output len {} != expected {}", provided, expected)
}
} Python Bindings
Basic Usage ▼
Calculate LRSI from NumPy arrays of high/low:
import numpy as np
from vectorta import lrsi
high = np.array([101.0, 102.0, 103.0, 103.5])
low = np.array([ 99.0, 100.5, 101.5, 102.5])
# alpha in (0,1); kernel optional: 'auto', 'scalar', 'avx2', 'avx512', ...
values = lrsi(high, low, alpha=0.2, kernel='auto')
print(values) # 1D numpy array, NaN during warmup then [0,1] Streaming ▼
Use LrsiStream for real-time updates:
from vectorta import LrsiStream
stream = LrsiStream(alpha=0.2)
for h, l in zip(high_series, low_series):
v = stream.update(h, l) # returns None during warmup
if v is not None:
handle(v) # value in [0,1] Batch Processing ▼
Sweep multiple alpha values:
import numpy as np
from vectorta import lrsi_batch
high = np.array([...])
low = np.array([...])
# (start, end, step)
result = lrsi_batch(high, low, alpha_range=(0.05, 0.5, 0.05), kernel='auto')
# Dict keys: 'values' (2D [rows, cols]) and 'alphas' (1D)
matrix = result['values']
alphas = result['alphas']
print(matrix.shape, alphas) CUDA Acceleration ▼
CUDA support for LRSI is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated LRSI calculations
# (Patterns will mirror other indicators: *_cuda_batch, *_cuda_many_series_one_param, etc.) JavaScript/WASM Bindings
Basic Usage ▼
Calculate LRSI in JavaScript/TypeScript:
import { lrsi_js } from 'vectorta-wasm';
// High/Low arrays as Float64Array or regular arrays
const high = new Float64Array([101.0, 102.0, 103.0, 103.5]);
const low = new Float64Array([ 99.0, 100.5, 101.5, 102.5]);
// Calculate LRSI with alpha in (0,1)
const values = lrsi_js(high, low, 0.2);
console.log('LRSI:', values); // Float64Array Memory-Efficient Operations ▼
Use zero-copy operations with explicit memory management:
import { lrsi_alloc, lrsi_free, lrsi_into, memory } from 'vectorta-wasm';
const high = new Float64Array([/* your data */]);
const low = new Float64Array([/* your data */]);
const n = high.length;
// Allocate WASM memory for inputs and output
const highPtr = lrsi_alloc(n);
const lowPtr = lrsi_alloc(n);
const outPtr = lrsi_alloc(n);
// Copy inputs
new Float64Array(memory.buffer, highPtr, n).set(high);
new Float64Array(memory.buffer, lowPtr, n).set(low);
// Compute into pre-allocated buffer: (high_ptr, low_ptr, out_ptr, len, alpha)
lrsi_into(highPtr, lowPtr, outPtr, n, 0.2);
// Read back and free
const out = new Float64Array(memory.buffer, outPtr, n).slice();
lrsi_free(highPtr, n); lrsi_free(lowPtr, n); lrsi_free(outPtr, n);
console.log(out); Batch Processing ▼
Calculate many alpha values at once:
import { lrsi_batch } from 'vectorta-wasm';
const high = new Float64Array([/* ... */]);
const low = new Float64Array([/* ... */]);
// Config uses (start, end, step) for alpha
const config = { alpha_range: [0.05, 0.5, 0.05] };
const out = lrsi_batch(high, low, config);
// out: { values: Float64Array, combos: { alpha: number | null }[], rows: number, cols: number }
console.log(out.rows, out.cols, out.combos);
// Flattened values are [row0..., row1..., ...] where row corresponds to a combo
// To extract a row by alpha:
const idx = out.combos.findIndex(c => (c.alpha ?? 0.2) === 0.2);
const row = out.values.slice(idx * out.cols, (idx + 1) * out.cols); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05