use crate::{KandError, TAFloat};
pub const fn lookback(param_period: usize) -> Result<usize, KandError> {
#[cfg(feature = "check")]
{
if param_period < 1 {
return Err(KandError::InvalidParameter);
}
}
Ok(param_period)
}
pub fn rocp(
input_price: &[TAFloat],
param_period: usize,
output_rocp: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_price.len();
let lookback = lookback(param_period)?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
if len != output_rocp.len() {
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for price in input_price {
if price.is_nan() {
return Err(KandError::NaNDetected);
}
}
}
for i in lookback..len {
output_rocp[i] =
(input_price[i] - input_price[i - param_period]) / input_price[i - param_period];
}
for value in output_rocp.iter_mut().take(lookback) {
*value = TAFloat::NAN;
}
Ok(())
}
pub fn rocp_inc(input: TAFloat, prev: TAFloat) -> Result<TAFloat, KandError> {
#[cfg(feature = "deep-check")]
{
if input.is_nan() || prev.is_nan() {
return Err(KandError::NaNDetected);
}
}
Ok((input - prev) / prev)
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_rocp_calculation() {
let input_price = vec![
35216.1, 35221.4, 35190.7, 35170.0, 35181.5, 35254.6, 35202.8, 35251.9, 35197.6,
35184.7, 35175.1, 35229.9, 35212.5, 35160.7, 35090.3, 35041.2, 34999.3, 35013.4,
35069.0, 35024.6,
];
let param_period = 10;
let mut output_rocp = vec![0.0; input_price.len()];
rocp(&input_price, param_period, &mut output_rocp).unwrap();
for value in output_rocp.iter().take(10) {
assert!(value.is_nan());
}
let expected_values = [
-0.001_164_240_219_672_252_2,
0.000_241_330_554_719_573_87,
0.000_619_481_851_739_320_6,
-0.000_264_429_911_856_778_8,
-0.002_592_271_506_331_37,
-0.006_053_110_799_725_468,
-0.005_780_790_164_418_739,
-0.006_765_592_776_559_561,
-0.003_653_658_203_968_411_3,
-0.004_550_273_272_189_292,
];
for (i, expected) in expected_values.iter().enumerate() {
assert_relative_eq!(output_rocp[i + 10], *expected, epsilon = 0.000_000_1);
}
for i in param_period..input_price.len() {
let result = rocp_inc(input_price[i], input_price[i - param_period]).unwrap();
assert_relative_eq!(result, output_rocp[i], epsilon = 0.000_000_1);
}
}
}