use crate::core::Method;
use crate::core::{Error, PeriodType, ValueType};
use crate::helpers::Peekable;
use crate::methods::EMA;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy)]
#[doc(alias = "TrueStrengthIndex")]
#[doc(alias = "True")]
#[doc(alias = "Strength")]
#[doc(alias = "Index")]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TSI {
last_value: ValueType,
ema11: EMA,
ema12: EMA,
ema21: EMA,
ema22: EMA,
}
impl TSI {
pub fn new(
short_period: PeriodType,
long_period: PeriodType,
value: &ValueType,
) -> Result<Self, Error> {
Method::new((short_period, long_period), value)
}
}
impl Method for TSI {
type Params = (PeriodType, PeriodType);
type Input = ValueType;
type Output = Self::Input;
fn new(params: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
let (short_period, long_period) = params;
let m = Self {
last_value: value,
ema11: EMA::new(long_period, &0.0)?,
ema12: EMA::new(short_period, &0.0)?,
ema21: EMA::new(long_period, &0.0)?,
ema22: EMA::new(short_period, &0.0)?,
};
Ok(m)
}
#[inline]
fn next(&mut self, &value: &Self::Input) -> Self::Output {
let momentum = value - self.last_value;
self.last_value = value;
self.ema12.next(&self.ema11.next(&momentum));
self.ema22.next(&self.ema21.next(&momentum.abs()));
self.peek()
}
}
impl Peekable<<Self as Method>::Output> for TSI {
fn peek(&self) -> <Self as Method>::Output {
let numerator = self.ema12.peek();
let denominator = self.ema22.peek();
if denominator > 0.0 {
numerator / denominator
} else {
0.0
}
}
}