Rate of Change Percentage (ROCP)

Parameters: period = 10

Overview

The Rate of Change Percentage (ROCP) expresses momentum as a fractional change rather than a scaled percentage, making it ideal for programmatic calculations and statistical analysis. The indicator computes the difference between current price and the price n periods ago, then divides by the historical price to produce a decimal ratio centered at zero. A value of 0.05 represents a 5% gain, while -0.03 indicates a 3% decline. This fractional representation eliminates the need to divide by 100 when using ROCP values in further calculations or algorithms. The centered oscillator makes trend direction immediately apparent through positive or negative readings, with magnitude reflecting momentum strength. Analysts often prefer ROCP over traditional ROC when building quantitative models or comparing momentum across instruments, as the fractional format simplifies mathematical operations. The default 10-period lookback strikes a balance between capturing meaningful price swings and maintaining reasonable sensitivity to recent changes.

Implementation Examples

Compute ROCP from price slices or candles:

use vectorta::indicators::rocp::{rocp, RocpInput, RocpParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice (fractional output)
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = RocpParams { period: Some(10) };
let input = RocpInput::from_slice(&prices, params);
let result = rocp(&input)?; // result.values: Vec<f64>

// Using with Candles and default settings (period=10; source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = RocpInput::with_default_candles(&candles);
let result = rocp(&input)?;

for v in result.values { println!("ROCP: {}", v); }

API Reference

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

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

// From candles with default params (close prices, period=10)
RocpInput::with_default_candles(&Candles) -> RocpInput
Parameters Structure
pub struct RocpParams {
    pub period: Option<usize>, // Default: 10
}
Output Structure
pub struct RocpOutput {
    pub values: Vec<f64>, // (curr - prev_n) / prev_n; NaN prefix during warmup
}
Validation, Warmup & NaNs
  • period > 0; otherwise RocpError::InvalidPeriod.
  • If input is empty: RocpError::EmptyInputData. If all values are NaN: RocpError::AllValuesNaN.
  • Require at least period valid points after the first finite value; else RocpError::NotEnoughValidData.
  • Warmup: indices [0 .. first + period) are NaN. First finite output at first + period.
  • Streaming returns None until the internal ring buffer has period values.
Error Handling
use vectorta::indicators::rocp::{rocp, RocpError};

match rocp(&input) {
    Ok(out) => process(out.values),
    Err(RocpError::EmptyInputData) => eprintln!("empty input"),
    Err(RocpError::AllValuesNaN) => eprintln!("all values NaN"),
    Err(RocpError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period: {} (len={})", period, data_len)
    }
    Err(RocpError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {} valid values, have {}", needed, valid)
    }
}
CUDA Acceleration

CUDA support for ROCP is coming soon.

# Coming soon: CUDA-accelerated ROCP APIs

JavaScript/WASM Bindings

Basic Usage

Calculate ROCP in JavaScript/TypeScript:

import { rocp_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 102.0, 101.5, 103.0]);
const values = rocp_js(prices, 10); // period = 10
console.log('ROCP values:', values);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { rocp_alloc, rocp_free, rocp_into, memory } from 'vectorta-wasm';

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

const inPtr = rocp_alloc(n);
const outPtr = rocp_alloc(n);

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

// Args: in_ptr, out_ptr, len, period
rocp_into(inPtr, outPtr, n, 10);

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

rocp_free(inPtr, n);
rocp_free(outPtr, n);

console.log(out);
Batch Processing

Sweep multiple periods with a unified API:

import { rocp_batch } from 'vectorta-wasm';

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

// Unified config object
const out = rocp_batch(prices, { period_range: [5, 20, 5] });

// out has shape metadata and flattened values
// { values: Float64Array, combos: [{ period: 5 }, ...], rows, cols }
console.log(out.rows, out.cols, out.combos.map(c => c.period));

Python Bindings

Basic Usage

Calculate ROCP using NumPy arrays (period required; optional kernel):

import numpy as np
from vectorta import rocp

prices = np.array([100.0, 101.0, 102.5, 101.7, 103.2, 104.0])

values = rocp(prices, period=10)
values = rocp(prices, period=10, kernel="auto")  # "auto", "scalar", "avx2", "avx512"

print(values)  # NumPy array (float64)
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import RocpStream

stream = RocpStream(period=10)
for price in feed:
    x = stream.update(price)
    if x is not None:
        print("ROCP:", x)
Batch Processing

Compute many periods efficiently:

import numpy as np
from vectorta import rocp_batch

prices = np.asarray([/* ... */], dtype=np.float64)

res = rocp_batch(prices, period_range=(5, 20, 5), kernel="auto")
print(res['values'].shape)  # (num_periods, len(prices))
print(res['periods'])       # list of tested periods

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators