#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::core::{Error, Method, PeriodType, ValueType, OHLCV};
use crate::core::{IndicatorConfig, IndicatorInstance, IndicatorResult};
use crate::methods::{Cross, HighestIndex, LowestIndex};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Aroon {
pub period: PeriodType,
pub signal_zone: ValueType,
pub over_zone_period: PeriodType,
}
impl IndicatorConfig for Aroon {
type Instance = AroonInstance;
const NAME: &'static str = "Aroon";
fn init<T: OHLCV>(self, candle: &T) -> Result<Self::Instance, Error> {
if !self.validate() {
return Err(Error::WrongConfig);
}
let cfg = self;
Ok(Self::Instance {
lowest_index: LowestIndex::new(cfg.period, &candle.low())?,
highest_index: HighestIndex::new(cfg.period, &candle.high())?,
cross: Cross::default(),
uptrend: 0,
downtrend: 0,
cfg,
})
}
fn validate(&self) -> bool {
self.signal_zone >= 0.0
&& self.signal_zone <= 1.0
&& self.period > 1
&& self.period < PeriodType::MAX
&& self.over_zone_period > 0
&& self.over_zone_period < PeriodType::MAX
}
fn set(&mut self, name: &str, value: String) -> Result<(), Error> {
match name {
"signal_zone" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.signal_zone = value,
},
"over_zone_period" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.over_zone_period = value,
},
"period" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.period = value,
},
_ => {
return Err(Error::ParameterParse(name.to_string(), value));
}
};
Ok(())
}
fn size(&self) -> (u8, u8) {
(2, 3)
}
}
impl Default for Aroon {
fn default() -> Self {
Self {
signal_zone: 0.3,
period: 14,
over_zone_period: 7,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AroonInstance {
cfg: Aroon,
lowest_index: LowestIndex,
highest_index: HighestIndex,
cross: Cross,
uptrend: isize,
downtrend: isize,
}
impl IndicatorInstance for AroonInstance {
type Config = Aroon;
fn config(&self) -> &Self::Config {
&self.cfg
}
fn next<T: OHLCV>(&mut self, candle: &T) -> IndicatorResult {
let highest_index = self.highest_index.next(&candle.high());
let lowest_index = self.lowest_index.next(&candle.low());
let aroon_up =
(self.cfg.period - highest_index) as ValueType / self.cfg.period as ValueType;
let aroon_down =
(self.cfg.period - lowest_index) as ValueType / self.cfg.period as ValueType;
let trend_signal = self.cross.next(&(aroon_up, aroon_down));
let edge_signal = (highest_index == 0) as i8 - (lowest_index == 0) as i8;
let is_up_over = (aroon_up >= (1.0 - self.cfg.signal_zone)) as isize;
let is_up_under = (aroon_up <= self.cfg.signal_zone) as isize;
let is_down_over = (aroon_down >= (1.0 - self.cfg.signal_zone)) as isize;
let is_down_under = (aroon_down <= self.cfg.signal_zone) as isize;
self.uptrend = (self.uptrend + 1) * is_up_over * is_down_under;
self.downtrend = (self.downtrend + 1) * is_down_over * is_up_under;
let trend_value =
(self.uptrend - self.downtrend) as ValueType / self.cfg.over_zone_period as ValueType;
IndicatorResult::new(
&[aroon_up, aroon_down],
&[trend_signal, edge_signal.into(), trend_value.into()],
)
}
}