Rate of Change Ratio (ROCR)

Parameters: period = 10

Overview

The Rate of Change Ratio (ROCR) presents momentum as a simple price ratio centered at 1.0, dividing the current value by the value from n periods earlier. This multiplicative perspective differs from additive ROC and fractional ROCP by showing relative price relationships directly. When ROCR equals 1.0, prices are unchanged over the lookback window; values above 1.0 indicate cumulative gains, and values below 1.0 reflect losses. For example, a ROCR of 1.08 means current price is 8% higher than n periods ago, while 0.95 indicates a 5% decline. This ratio format proves particularly useful for compounding calculations and geometric mean computations in quantitative strategies. Traders monitor ROCR for trend strength assessment, with sustained readings above 1.0 confirming uptrends and readings below 1.0 validating downtrends. The implementation returns 0.0 when encountering zero or NaN in the historical lookback, preventing division errors while maintaining a valid numeric output series. The default 10-period window provides a practical balance between short term noise and meaningful momentum measurement.

Implementation Examples

Get started with ROCR in just a few lines:

use vectorta::indicators::rocr::{rocr, RocrInput, RocrParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = RocrParams { period: Some(10) }; // default = 10
let input = RocrInput::from_slice(&prices, params);
let result = rocr(&input)?;

// Using with Candles data structure (defaults: period=10; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = RocrInput::with_default_candles(&candles);
let result = rocr(&input)?;

// Access the ROCR values (centered at 1.0)
for value in result.values {
    println!("ROCR: {}", value);
}

API Reference

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

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

// From candles with default params (close prices, period=10)
RocrInput::with_default_candles(&Candles) -> RocrInput
Parameters Structure
pub struct RocrParams {
    pub period: Option<usize>, // Default: 10
}
Output Structure
pub struct RocrOutput {
    pub values: Vec<f64>, // ROCR values (centered at 1.0; 0.0 when past==0 or NaN)
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ len(data); else RocrError::InvalidPeriod.
  • There must be at least period valid points after the first finite value; otherwise RocrError::NotEnoughValidData.
  • If all values are NaN: RocrError::AllValuesNaN. Empty input: RocrError::EmptyData.
  • Warmup: indices before first_non_nan + period are set to NaN.
  • Division edge case: when the past value is 0.0 or NaN, the output at that index is 0.0.
Error Handling
use vectorta::indicators::rocr::RocrError;

match rocr(&input) {
    Ok(output) => process_results(output.values),
    Err(RocrError::EmptyData) =>
        println!("Input data is empty"),
    Err(RocrError::InvalidPeriod { period, data_len }) =>
        println!("Invalid period {} for data length {}", period, data_len),
    Err(RocrError::NotEnoughValidData { needed, valid }) =>
        println!("Need {} data points, only {} valid after first non-NaN", needed, valid),
    Err(RocrError::AllValuesNaN) =>
        println!("All input values are NaN"),
}

Python Bindings

Basic Usage

Calculate ROCR using NumPy arrays (default period = 10):

import numpy as np
from vectorta import rocr

# Prepare price data as NumPy array
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Calculate ROCR with defaults (period=10)
result = rocr(prices)

# Or specify custom parameters
result = rocr(prices, period=14)

# Optionally specify kernel ("auto", "scalar", "avx2", "avx512")
result = rocr(prices, period=10, kernel="auto")

# Result is a NumPy array matching input length
print(f"ROCR values: {result}")
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import RocrStream

# Initialize streaming ROCR calculator
stream = RocrStream(period=10)

# Process real-time price updates
for price in price_feed:
    rocr_value = stream.update(price)

    if rocr_value is not None:
        # ROCR value is ready (None during warmup period)
        print(f"Current ROCR: {rocr_value}")
Batch Parameter Optimization

Test multiple period values for optimization:

import numpy as np
from vectorta import rocr_batch

# Your price data
prices = np.array([...])  # Your historical prices

# Define period range to test: (start, end, step)
period_range = (5, 20, 5)    # Test: 5, 10, 15, 20

# Run batch calculation
results = rocr_batch(
    prices,
    period_range=period_range,
    kernel="auto"  # Auto-select best kernel
)

# Access results
print(f"Values shape: {results['values'].shape}")  # (num_combinations, len(prices))
print(f"Periods tested: {results['periods']}")
CUDA Acceleration

CUDA support for ROCR is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated ROCR calculations

JavaScript/WASM Bindings

Basic Usage

Calculate ROCR in JavaScript/TypeScript:

import { rocr_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// Calculate ROCR with specified period
const result = rocr_js(prices, 10);  // period=10

// Result is a Float64Array
console.log('ROCR values:', result);
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { rocr_alloc, rocr_free, rocr_into, memory } from 'vectorta-wasm';

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

// Allocate WASM memory for input and output
const inPtr = rocr_alloc(length);
const outPtr = rocr_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate ROCR directly into allocated memory
// Args: in_ptr, out_ptr, len, period
rocr_into(inPtr, outPtr, length, 10);

// Read results from WASM memory (slice() to copy out)
const rocrValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
rocr_free(inPtr, length);
rocr_free(outPtr, length);

console.log('ROCR values:', rocrValues);
Batch Processing

Test multiple period values efficiently:

import { rocr_batch } from 'vectorta-wasm';

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

// Define parameter sweep ranges via config object
const config = { period_range: [5, 20, 5] }; // start, end, step

// Calculate all combinations
const results = rocr_batch(prices, config);

// results = { values: Float64Array (flattened), combos: RocrParams[], rows, cols }
console.log('Rows (num combos):', results.rows);
console.log('Cols (series length):', results.cols);

Performance Analysis

Comparison:
View:

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

Loading chart...

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

Related Indicators