roc-rs 0.1.0

Rate Of Change (ROC) Implementation In Rust
#![feature(external_doc)]

use std::ops::{Div, Sub};
use ta_common::fixed_queue::FixedQueue;
use ta_common::traits::Indicator;

pub enum ROCType {
    ROCR,
    ROCP,
    ROC100,
    ROC,
}


#[doc(include = "../README.md")]
pub struct ROC {
    history: FixedQueue<f64>,
    price_ago: u32,
    result_type: ROCType,
}

impl ROC {
    pub fn new(price_ago: u32, result_type: Option<ROCType>) -> ROC {
        let result_type = result_type.unwrap_or(ROCType::ROC);
        Self {
            history: FixedQueue::new(price_ago),
            price_ago,
            result_type,
        }
    }
    pub fn calc(&self, input: f64) -> Option<f32> {
        let history = &self.history;
        let size = history.size() as i32;
        let period = self.price_ago as i32;
        let prev_index: i32 = (size - period) as i32;
        let prev = history.at(prev_index);
        return prev.map(|v|input.div(v) as f32);
    }
}


impl Indicator<f64, Option<f32>> for ROC {
    fn next(&mut self, input: f64) -> Option<f32> {
        let value = self.calc(input);
        let result = match &self.result_type {
            ROCType::ROCR => value,
            ROCType::ROCP => value.map(|v| v - 1_f32),
            ROCType::ROC => value.map(|v| (v - 1_f32) * 100_f32),
            ROCType::ROC100 => value.map(|v| v * 100_f32),
        };
        self.history.add(input);
        result
    }

    fn reset(&mut self) {
        self.history.clear();
    }
}


#[cfg(test)]
mod tests {
    use crate::{ROC, ROCType};
    use ta_common::traits::{Indicator};

    #[test]
    fn roc_percent_works() {
        let mut roc = ROC::new(1, None);
        let res = roc.next(100 as f64);
        assert_eq!(None, res);
        let res = roc.next(50 as f64);
        assert_eq!(Some(-50_f32), res);
    }

    #[test]
    fn roc_value_works() {
        let mut roc = ROC::new(1, Some(ROCType::ROCR));
        let res = roc.next(100 as f64);
        assert_eq!(None, res);
        let res = roc.next(50 as f64);
        assert_eq!(Some(0.5_f32), res);
    }

    #[test]
    fn roc_hundred_works() {
        let mut roc = ROC::new(1, Some(ROCType::ROC100));
        let res = roc.next(100 as f64);
        assert_eq!(None, res);
        let res = roc.next(50 as f64);
        assert_eq!(Some(50_f32), res);
    }

    #[test]
    fn roc_momentum_works() {
        let mut roc = ROC::new(1, Some(ROCType::ROCP));
        let res = roc.next(100 as f64);
        assert_eq!(None, res);
        let res = roc.next(50 as f64);
        assert_eq!(Some(-0.5_f32), res);
    }
}