use rust_decimal::Decimal;
use crate::error::FinError;
use crate::signals::{BarInput, Signal, SignalValue};
pub struct CloseAccelerationSign {
c0: Option<Decimal>,
c1: Option<Decimal>,
}
impl CloseAccelerationSign {
pub fn new() -> Self {
Self { c0: None, c1: None }
}
}
impl Default for CloseAccelerationSign {
fn default() -> Self { Self::new() }
}
impl Signal for CloseAccelerationSign {
fn update(&mut self, bar: &BarInput) -> Result<SignalValue, FinError> {
let result = if let (Some(c0), Some(c1)) = (self.c0, self.c1) {
let d1 = bar.close - c1;
let d2 = c1 - c0;
let accel = d1 - d2;
let sign: i32 = if accel > Decimal::ZERO { 1 } else if accel < Decimal::ZERO { -1 } else { 0 };
SignalValue::Scalar(Decimal::from(sign))
} else {
SignalValue::Unavailable
};
self.c0 = self.c1;
self.c1 = Some(bar.close);
Ok(result)
}
fn is_ready(&self) -> bool { self.c0.is_some() && self.c1.is_some() }
fn period(&self) -> usize { 3 }
fn reset(&mut self) { self.c0 = None; self.c1 = None; }
fn name(&self) -> &str { "CloseAccelerationSign" }
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
fn bar(c: &str) -> BarInput {
BarInput {
open: c.parse().unwrap(),
high: c.parse().unwrap(),
low: c.parse().unwrap(),
close: c.parse().unwrap(),
volume: dec!(1000),
}
}
#[test]
fn test_cas_accelerating() {
let mut sig = CloseAccelerationSign::new();
sig.update(&bar("100")).unwrap();
sig.update(&bar("101")).unwrap();
let v = sig.update(&bar("103")).unwrap(); assert_eq!(v, SignalValue::Scalar(dec!(1)));
}
#[test]
fn test_cas_decelerating() {
let mut sig = CloseAccelerationSign::new();
sig.update(&bar("100")).unwrap();
sig.update(&bar("103")).unwrap();
let v = sig.update(&bar("104")).unwrap(); assert_eq!(v, SignalValue::Scalar(dec!(-1)));
}
#[test]
fn test_cas_constant_velocity_zero() {
let mut sig = CloseAccelerationSign::new();
sig.update(&bar("100")).unwrap();
sig.update(&bar("102")).unwrap();
let v = sig.update(&bar("104")).unwrap(); assert_eq!(v, SignalValue::Scalar(dec!(0)));
}
}