#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::core::{Error, Method, MovingAverageConstructor, PeriodType, ValueType, OHLCV};
use crate::core::{IndicatorConfig, IndicatorInstance, IndicatorResult};
use crate::helpers::MA;
use crate::methods::{Cross, SMA, SWMA};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RelativeVigorIndex<M: MovingAverageConstructor = MA> {
pub period1: PeriodType,
pub period2: PeriodType,
pub signal: M,
pub zone: ValueType,
}
impl<M: MovingAverageConstructor> IndicatorConfig for RelativeVigorIndex<M> {
type Instance = RelativeVigorIndexInstance<M>;
const NAME: &'static str = "RelativeVigorIndex";
fn init<T: OHLCV>(self, candle: &T) -> Result<Self::Instance, Error> {
if !self.validate() {
return Err(Error::WrongConfig);
}
let cfg = self;
let d_close = &0.0; let d_hl = &(candle.high() - candle.low());
let rvi = 0.0;
Ok(Self::Instance {
prev_close: candle.open(),
swma1: SWMA::new(cfg.period2, d_close)?,
sma1: SMA::new(cfg.period1, d_close)?,
swma2: SWMA::new(cfg.period2, d_hl)?,
sma2: SMA::new(cfg.period1, d_hl)?,
ma: cfg.signal.init(rvi)?,
cross: Cross::default(),
cfg,
})
}
fn validate(&self) -> bool {
self.period1 >= 2
&& self.zone >= 0.
&& self.zone < 0.5
&& self.period2 > 1
&& self.signal.ma_period() > 1
}
fn set(&mut self, name: &str, value: String) -> Result<(), Error> {
match name {
"period1" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.period1 = value,
},
"period2" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.period2 = value,
},
"signal" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.signal = value,
},
"zone" => match value.parse() {
Err(_) => return Err(Error::ParameterParse(name.to_string(), value.to_string())),
Ok(value) => self.zone = value,
},
_ => {
return Err(Error::ParameterParse(name.to_string(), value));
}
};
Ok(())
}
fn size(&self) -> (u8, u8) {
(2, 2)
}
}
impl Default for RelativeVigorIndex {
fn default() -> Self {
Self {
period1: 10,
period2: 4,
signal: MA::SWMA(4),
zone: 0.25,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RelativeVigorIndexInstance<M: MovingAverageConstructor = MA> {
cfg: RelativeVigorIndex<M>,
prev_close: ValueType,
swma1: SWMA,
sma1: SMA,
swma2: SWMA,
sma2: SMA,
ma: M::Instance,
cross: Cross,
}
impl<M: MovingAverageConstructor> IndicatorInstance for RelativeVigorIndexInstance<M> {
type Config = RelativeVigorIndex<M>;
#[inline]
fn config(&self) -> &Self::Config {
&self.cfg
}
#[allow(clippy::similar_names)]
fn next<T: OHLCV>(&mut self, candle: &T) -> IndicatorResult {
let close_open = candle.close() - self.prev_close;
let high_low = candle.high() - candle.low();
self.prev_close = candle.close();
let swma1 = self.swma1.next(&close_open);
let sma1 = self.sma1.next(&swma1);
let swma2 = self.swma2.next(&high_low);
let sma2 = self.sma2.next(&swma2);
let rvi = if sma2 == 0. { 0. } else { sma1 / sma2 };
let sig: ValueType = self.ma.next(&rvi);
let s1 = self.cross.next(&(rvi, sig)).analog();
let s2 = (s1 < 0 && rvi > self.cfg.zone && sig > self.cfg.zone) as i8
- (s1 > 0 && rvi < -self.cfg.zone && sig < -self.cfg.zone) as i8;
IndicatorResult::new(&[rvi, sig], &[s1.into(), s2.into()])
}
}