use crate::{KandError, TAFloat};
pub const fn lookback(param_period: usize) -> Result<usize, KandError> {
#[cfg(feature = "check")]
{
if param_period < 2 {
return Err(KandError::InvalidParameter);
}
}
Ok(param_period)
}
pub fn mom(
input_prices: &[TAFloat],
param_period: usize,
output_mom: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_prices.len();
let lookback = lookback(param_period)?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
if output_mom.len() != len {
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for price in input_prices {
if price.is_nan() {
return Err(KandError::NaNDetected);
}
}
}
for i in lookback..len {
output_mom[i] = input_prices[i] - input_prices[i - param_period];
}
for item in output_mom.iter_mut().take(lookback) {
*item = TAFloat::NAN;
}
Ok(())
}
pub fn mom_inc(
input_current_price: TAFloat,
input_old_price: TAFloat,
) -> Result<TAFloat, KandError> {
#[cfg(feature = "deep-check")]
{
if input_current_price.is_nan() || input_old_price.is_nan() {
return Err(KandError::NaNDetected);
}
}
Ok(input_current_price - input_old_price)
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_mom_calculation() {
let input_prices = 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, 34939.5, 34952.6, 35000.0, 35041.8, 35080.0, 35114.5, 35097.2,
35092.0, 35073.2, 35139.3,
];
let param_period = 14;
let mut output_mom = vec![0.0; input_prices.len()];
mom(&input_prices, param_period, &mut output_mom).unwrap();
for value in output_mom.iter().take(14) {
assert!(value.is_nan());
}
let expected_values = [
-125.8, -180.2, -191.4, -156.6, -112.5, -230.0, -263.3, -299.3, -197.6, -142.9, -95.1,
-115.4, -115.3, -68.7, -17.1, 98.1,
];
for (i, expected) in expected_values.iter().enumerate() {
assert_relative_eq!(output_mom[i + 14], *expected, epsilon = 0.1);
}
for i in param_period..input_prices.len() {
let result = mom_inc(input_prices[i], input_prices[i - param_period]).unwrap();
assert_relative_eq!(result, output_mom[i], epsilon = 0.00001);
}
}
}