Gopalakrishnan Range Index
length = 5 Overview
Gopalakrishnan Range Index, often abbreviated GAPO or GRI, tracks how large the rolling high-low envelope has become relative to the selected window length. It takes the logarithm of the current rolling range and divides it by the logarithm of the lookback period, which creates a normalized scale that rises when price swings widen quickly and falls when the market compresses.
This implementation works directly from high and low inputs, so it is a pure range indicator rather than a close-based volatility estimate. In streaming mode it maintains rolling high and low deques, while the batch path sweeps alternative window lengths and returns one flattened output row per tested length.
Defaults: Gopalakrishnan Range Index uses `length = 5`.
Implementation Examples
Compute the normalized range series from raw high/low inputs or candle data.
use vector_ta::indicators::gopalakrishnan_range_index::{
gopalakrishnan_range_index,
GopalakrishnanRangeIndexInput,
GopalakrishnanRangeIndexParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let high = vec![101.0, 102.4, 101.8, 103.1, 104.0, 103.6];
let low = vec![99.8, 100.7, 100.2, 101.4, 102.8, 102.1];
let output = gopalakrishnan_range_index(&GopalakrishnanRangeIndexInput::from_slices(
&high,
&low,
GopalakrishnanRangeIndexParams { length: Some(5) },
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = gopalakrishnan_range_index(
&GopalakrishnanRangeIndexInput::with_default_candles(&candles),
)?;
println!("latest gri = {:?}", output.values.last());
println!("series length = {}", candle_output.values.len()); API Reference
Input Methods ▼
// From candles
GopalakrishnanRangeIndexInput::from_candles(&Candles, GopalakrishnanRangeIndexParams)
-> GopalakrishnanRangeIndexInput
// From high/low slices
GopalakrishnanRangeIndexInput::from_slices(&[f64], &[f64], GopalakrishnanRangeIndexParams)
-> GopalakrishnanRangeIndexInput
// From candles with default parameters
GopalakrishnanRangeIndexInput::with_default_candles(&Candles)
-> GopalakrishnanRangeIndexInput Parameters Structure ▼
pub struct GopalakrishnanRangeIndexParams {
pub length: Option<usize>, // default 5
} Output Structure ▼
pub struct GopalakrishnanRangeIndexOutput {
pub values: Vec<f64>,
} Validation, Warmup & NaNs ▼
- High and low inputs must be non-empty and have matching lengths.
lengthmust be greater than1.- The indicator requires at least
lengthvalid high/low bars. - The one-shot output is NaN-prefixed until the first full rolling window is available.
- Streaming returns
Noneduring warmup, and if a completed window contains invalid bars it returnsNaNfor that step. - If the rolling high-low range is non-positive, the normalized value is
NaN. - Batch mode validates the length sweep and rejects non-batch kernels through
InvalidKernelForBatch.
Builder, Streaming & Batch APIs ▼
// Builder
GopalakrishnanRangeIndexBuilder::new()
.length(usize)
.kernel(Kernel)
.apply(&Candles)
GopalakrishnanRangeIndexBuilder::new()
.apply_slices(&[f64], &[f64])
GopalakrishnanRangeIndexBuilder::new()
.into_stream()
// Stream
GopalakrishnanRangeIndexStream::try_new(GopalakrishnanRangeIndexParams)
GopalakrishnanRangeIndexStream::update(f64, f64) -> Option<f64>
GopalakrishnanRangeIndexStream::get_warmup_period() -> usize
// Batch
GopalakrishnanRangeIndexBatchBuilder::new()
.kernel(Kernel)
.length_range(start, end, step)
.length_static(value)
.apply_slices(&[f64], &[f64])
GopalakrishnanRangeIndexBatchBuilder::new()
.apply_candles(&Candles)
GopalakrishnanRangeIndexBatchBuilder::with_default_candles(&Candles) Error Handling ▼
pub enum GopalakrishnanRangeIndexError {
EmptyInputData,
AllValuesNaN,
InvalidLength { length: usize, data_len: usize },
NotEnoughValidData { needed: usize, valid: usize },
InconsistentSliceLengths { high_len: usize, low_len: usize },
OutputLengthMismatch { expected: usize, got: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
} Python Bindings
Python exposes a scalar high/low function, a streaming class, and a batch sweep. The scalar path returns one NumPy range-index array. Streaming returns one floating-point value or `None`, while batch returns the flattened values matrix together with the tested lengths and output dimensions.
import numpy as np
from vector_ta import (
gopalakrishnan_range_index,
gopalakrishnan_range_index_batch,
GopalakrishnanRangeIndexStream,
)
high = np.asarray(high_values, dtype=np.float64)
low = np.asarray(low_values, dtype=np.float64)
values = gopalakrishnan_range_index(high, low, length=5, kernel="auto")
stream = GopalakrishnanRangeIndexStream(length=5)
print(stream.update(high[-1], low[-1]))
batch = gopalakrishnan_range_index_batch(
high,
low,
length_range=(5, 15, 5),
kernel="auto",
)
print(batch["values"].shape)
print(batch["lengths"])
print(batch["rows"], batch["cols"]) JavaScript/WASM Bindings
The WASM layer exposes a scalar function, a batch function, and raw allocation and into-buffer helpers. The scalar binding returns a single array of GRI values. The batch binding returns the flattened matrix together with the tested lengths, parameter combinations, and matrix dimensions.
import init, {
gopalakrishnan_range_index_js,
gopalakrishnan_range_index_batch_js,
} from "@vectoralpha/vector_ta";
await init();
const values = gopalakrishnan_range_index_js(high, low, 5);
console.log(values);
const batch = gopalakrishnan_range_index_batch_js(high, low, {
length_range: [5, 15, 5],
});
console.log(batch.values);
console.log(batch.lengths);
console.log(batch.rows, batch.cols); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)