Rate of Change (ROC)
period = 9 Overview
The Rate of Change (ROC) calculates momentum by measuring the percentage difference between the current price and the price from n periods earlier. The formula subtracts the historical price from the current value, divides by the historical price, and multiplies by 100 to express the result as a percentage. ROC oscillates around a zero centerline, with positive values signaling upward momentum and negative values indicating downward pressure. Unlike moving averages that smooth price action, ROC directly quantifies velocity of price movement without lag or smoothing. Traders employ ROC to identify momentum divergences where price makes new highs or lows while ROC fails to confirm, suggesting potential trend exhaustion. Extreme ROC readings often coincide with overbought or oversold conditions, though specific threshold levels vary by instrument and timeframe. The default 9-period lookback captures short term momentum shifts while filtering single-bar noise, though longer periods reveal more persistent trends at the cost of reduced sensitivity.
Implementation Examples
Get started with ROC in just a few lines:
use vectorta::indicators::roc::{roc, RocInput, RocParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with price data slice
let prices = vec![100.0, 99.0, 101.5, 103.0, 105.0, 104.5];
let params = RocParams { period: Some(9) };
let input = RocInput::from_slice(&prices, params);
let result = roc(&input)?;
// Using with Candles data structure
// Quick and simple with default parameters (period=9; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = RocInput::with_default_candles(&candles);
let result = roc(&input)?;
// Access the ROC values (percentage)
for value in result.values {
println!("ROC: {}", value);
} API Reference
Input Methods ▼
// From price slice
RocInput::from_slice(&[f64], RocParams) -> RocInput
// From candles with custom source
RocInput::from_candles(&Candles, &str, RocParams) -> RocInput
// From candles with default params (close prices, period=9)
RocInput::with_default_candles(&Candles) -> RocInput Parameters Structure ▼
pub struct RocParams {
pub period: Option<usize>, // Default: 9
} Output Structure ▼
pub struct RocOutput {
pub values: Vec<f64>, // ROC values in percent with NaN warmup prefix
} Validation, Warmup & NaNs ▼
period > 0; otherwiseRocError::InvalidPeriod.- Requires at least
periodvalid points after the first finite input; elseRocError::NotEnoughValidData. - If all inputs are
NaN→RocError::AllValuesNaN; empty slice →RocError::EmptyData. - Warmup: indices before
first_finite_index + periodareNaN(batch); streaming returnsNoneuntil ready. - When
Pt−n == 0orNaN, output is0.0for that index (defined behavior).
Error Handling ▼
use vectorta::indicators::roc::{roc, RocError};
match roc(&input) {
Ok(output) => process_results(output.values),
Err(RocError::EmptyData) =>
eprintln!("Input data is empty"),
Err(RocError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for data length {}", period, data_len),
Err(RocError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {} available", needed, valid),
Err(RocError::AllValuesNaN) =>
eprintln!("All input values are NaN"),
} Python Bindings
Basic Usage ▼
Calculate ROC using NumPy arrays:
import numpy as np
from vectorta import roc
# Prepare price data as NumPy array
prices = np.array([100.0, 99.0, 101.5, 103.0, 105.0, 104.5])
# Calculate ROC (period is required)
result = roc(prices, period=9)
# Specify kernel (optional): "auto", "scalar", "avx2", "avx512" (if available)
result = roc(prices, period=9, kernel="auto")
# Result is a NumPy array matching input length
print(f"ROC values: {result}") Streaming Real-time Updates ▼
Process real-time price updates efficiently:
from vectorta import RocStream
# Initialize streaming ROC calculator
stream = RocStream(period=9)
# Process real-time price updates
for price in price_feed:
roc_value = stream.update(price)
if roc_value is not None:
print(f"Current ROC: {roc_value}") Batch Parameter Optimization ▼
Test multiple period values for optimization:
import numpy as np
from vectorta import roc_batch
prices = np.array([...])
# Define period range as (start, end, step)
period_range = (5, 20, 5) # 5, 10, 15, 20
results = roc_batch(
prices,
period_range=period_range,
kernel="auto",
)
print(f"Values shape: {results['values'].shape}") # (num_periods, len(prices))
print(f"Periods tested: {results['periods']}") CUDA Acceleration ▼
CUDA support for ROC is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated ROC calculations
# See other indicators for expected API shape and examples. JavaScript/WASM Bindings
Basic Usage ▼
Calculate ROC in JavaScript/TypeScript:
import { roc_js } from 'vectorta-wasm';
// Price data as Float64Array or regular array
const prices = new Float64Array([100, 99, 101.5, 103, 105, 104.5]);
// Calculate ROC with specified period
const result = roc_js(prices, 9);
// Result is a Float64Array
console.log('ROC values:', result); Memory-Efficient Operations ▼
Use zero-copy operations for better performance with large datasets:
import { roc_alloc, roc_free, roc_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const length = prices.length;
// Allocate WASM memory for input and output
const inPtr = roc_alloc(length);
const outPtr = roc_alloc(length);
// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);
// Calculate ROC directly into allocated memory
// Args: in_ptr, out_ptr, len, period
roc_into(inPtr, outPtr, length, 9);
// Read results from WASM memory (slice() to copy out)
const rocValues = new Float64Array(memory.buffer, outPtr, length).slice();
// Free allocated memory when done
roc_free(inPtr, length);
roc_free(outPtr, length);
console.log('ROC values:', rocValues); Batch Processing ▼
Test multiple period values efficiently:
import { roc_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Run batch calculation with a period range
const output = roc_batch(prices, { period_range: [5, 20, 5] });
// Output fields:
// - output.values: flat array of length rows*cols (row-major)
// - output.rows: number of period values tested
// - output.cols: length of prices
// - output.combos: array of { period: number | null } used
// Reshape values into 2D [rows x cols]
const matrix = [];
for (let r = 0; r < output.rows; r++) {
const start = r * output.cols;
matrix.push(output.values.slice(start, start + output.cols));
}
// List tested periods
const testedPeriods = output.combos.map(c => c.period ?? 9);
console.log('Periods:', testedPeriods);
console.log('First combo ROC series length:', matrix[0].length); Performance Analysis
Across sizes, Rust CPU runs about 7.38× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05