use rand::Rng;
use cxmr_ta_core::indicators;
use super::{impls, traits, Error, Operation};
pub type Param = u32;
pub type Params = Vec<Param>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ParamDescriptor {
Step {
min: Param,
max: Param,
step: Param,
},
Power {
min: Param,
max: Param,
power: Param,
},
}
impl ParamDescriptor {
pub fn rand<R: Rng>(&self, r: &mut R) -> Param {
match self {
ParamDescriptor::Step { min, max, step } => {
let rand = r.gen_range(min, max);
rand * step
}
ParamDescriptor::Power { min, max, power } => {
let rand = r.gen_range(min, max);
rand.pow(*power)
}
}
}
}
#[macro_export]
macro_rules! param {
( $min:expr, $max:expr, $step:expr ) => {
ParamDescriptor::Step {
min: $min,
max: $max,
step: $step,
}
};
}
#[macro_export]
macro_rules! pow_param {
( $min:expr, $max:expr, $power:expr ) => {
ParamDescriptor::Power {
min: $min,
max: $max,
power: $power,
}
};
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
pub enum Marker {
Rate,
Freq,
}
impl From<&Indicator> for Marker {
fn from(indicator: &Indicator) -> Marker {
match indicator {
Indicator::FastStochastic
| Indicator::SlowStochastic
| Indicator::EfficiencyRatio
| Indicator::RelativeStrengthIndex
| Indicator::MoneyFlowIndex
| Indicator::RateOfChange
| Indicator::AverageTrueRange
| Indicator::TrueRange
| Indicator::Spread => Marker::Freq,
_ => Marker::Rate,
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
pub enum Indicator {
AverageTrueRange,
EfficiencyRatio,
ExponentialMovingAverage,
FastStochastic,
Maximum,
Minimum,
MoneyFlowIndex,
OnBalanceVolume,
RateOfChange,
RelativeStrengthIndex,
SimpleMovingAverage,
SlowStochastic,
TrueRange,
Spread,
MovingAverageConvergenceDivergence,
ClosePrice,
AskRate,
BidRate,
EntryRate,
ExitRate,
}
pub type BoxIndicator<S> = Box<dyn traits::Next<S>>;
#[macro_export]
macro_rules! construct_indicator {
( $s:ident, $p:ident, $t:ident, $( $x:tt ), * ) => {
{
let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
Box::new(indicators::$t::new(
$(
$s.get_param(¶ms, $x)?,
)*
)?) as BoxIndicator<S>
}
};
}
#[macro_export]
macro_rules! construct_indicator_local {
( $s:ident, $p:ident, $t:ident, $i:expr, $( $x:tt ), * ) => {
{
let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
Box::new($i(
$(
$s.get_param(¶ms, $x)? as usize,
)*
)) as BoxIndicator<S>
}
};
}
#[macro_export]
macro_rules! construct_indicator_wrapper {
( $s:ident, $p:ident, $t:ident, $i:expr, $( $x:tt ), * ) => {
{
let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
Box::new($i(indicators::$t::new(
$(
$s.get_param(¶ms, $x)?,
)*
)?)) as BoxIndicator<S>
}
};
}
#[macro_export]
macro_rules! static_params {
( $( $x:expr ), * ) => {
{
static LIST: &'static [ParamDescriptor] = &[$($x,)*];
LIST
}
};
}
impl Indicator {
pub fn parameters(&self) -> usize {
match self {
Indicator::ExponentialMovingAverage
| Indicator::SimpleMovingAverage
| Indicator::RelativeStrengthIndex
| Indicator::FastStochastic
| Indicator::Maximum
| Indicator::Minimum
| Indicator::MoneyFlowIndex
| Indicator::AverageTrueRange
| Indicator::EfficiencyRatio
| Indicator::ClosePrice
| Indicator::Spread
| Indicator::AskRate
| Indicator::BidRate
| Indicator::EntryRate
| Indicator::ExitRate
| Indicator::RateOfChange => 1,
Indicator::SlowStochastic => 2,
Indicator::TrueRange | Indicator::OnBalanceVolume => 0,
Indicator::MovingAverageConvergenceDivergence => 3,
}
}
pub fn descriptors(&self) -> Option<&'static [ParamDescriptor]> {
match self {
Indicator::MovingAverageConvergenceDivergence => Some(static_params!(
param!(1, 4, 10),
param!(1, 4, 20),
param!(1, 4, 3)
)),
Indicator::ExponentialMovingAverage => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::SimpleMovingAverage => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::RelativeStrengthIndex => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::FastStochastic => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::SlowStochastic => {
Some(static_params!(pow_param!(2, 8, 3), pow_param!(2, 8, 3)))
}
Indicator::Maximum => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::Minimum => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::MoneyFlowIndex => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::EfficiencyRatio => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::AverageTrueRange => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::RateOfChange => Some(static_params!(pow_param!(2, 8, 3))),
Indicator::Spread => Some(static_params!(param!(0, 2, 1))),
Indicator::AskRate => Some(static_params!(param!(0, 2, 1))),
Indicator::BidRate => Some(static_params!(param!(0, 2, 1))),
Indicator::EntryRate => Some(static_params!(param!(0, 2, 1))),
Indicator::ExitRate => Some(static_params!(param!(0, 2, 1))),
Indicator::TrueRange | Indicator::OnBalanceVolume | Indicator::ClosePrice => None,
}
}
pub fn construct<S: traits::State>(
&self,
params: Option<&Params>,
) -> Result<BoxIndicator<S>, Error> {
match self {
Indicator::ExponentialMovingAverage => Ok(construct_indicator!(
self,
params,
ExponentialMovingAverage,
0
)),
Indicator::SimpleMovingAverage => {
Ok(construct_indicator!(self, params, SimpleMovingAverage, 0))
}
Indicator::RelativeStrengthIndex => {
Ok(construct_indicator!(self, params, RelativeStrengthIndex, 0))
}
Indicator::FastStochastic => Ok(construct_indicator!(self, params, FastStochastic, 0)),
Indicator::SlowStochastic => {
Ok(construct_indicator!(self, params, SlowStochastic, 0, 1))
}
Indicator::Maximum => Ok(construct_indicator!(self, params, Maximum, 0)),
Indicator::Minimum => Ok(construct_indicator!(self, params, Minimum, 0)),
Indicator::MoneyFlowIndex => Ok(construct_indicator!(self, params, MoneyFlowIndex, 0)),
Indicator::AverageTrueRange => Ok(construct_indicator_wrapper!(
self,
params,
AverageTrueRange,
impls::AverageTrueRange,
0
)),
Indicator::EfficiencyRatio => Ok(construct_indicator_wrapper!(
self,
params,
EfficiencyRatio,
impls::EfficiencyRatio,
0
)),
Indicator::RateOfChange => Ok(construct_indicator!(self, params, EfficiencyRatio, 0)),
Indicator::TrueRange => {
Ok(Box::new(impls::TrueRange(indicators::TrueRange::new())) as BoxIndicator<S>)
}
Indicator::OnBalanceVolume => {
Ok(Box::new(indicators::OnBalanceVolume::new()) as BoxIndicator<S>)
}
Indicator::ClosePrice => Ok(Box::new(impls::ClosePrice) as BoxIndicator<S>),
Indicator::Spread => Ok(construct_indicator_local!(
self,
params,
Spread,
impls::Spread,
0
)),
Indicator::AskRate => Ok(construct_indicator_local!(
self,
params,
AskRate,
impls::AskRate,
0
)),
Indicator::BidRate => Ok(construct_indicator_local!(
self,
params,
BidRate,
impls::BidRate,
0
)),
Indicator::EntryRate => Ok(construct_indicator_local!(
self,
params,
EntryRate,
impls::EntryRate,
0
)),
Indicator::ExitRate => Ok(construct_indicator_local!(
self,
params,
ExitRate,
impls::ExitRate,
0
)),
Indicator::MovingAverageConvergenceDivergence => {
Err(Error::IndicatorNotImplemented(self.clone()))
}
}
}
pub fn into_op(self) -> Result<Operation, Error> {
Operation::new(self, None)
}
pub fn with_params(self, params: Params) -> Result<Operation, Error> {
Operation::new(self, Some(params))
}
fn get_param(&self, params: &Params, index: usize) -> Result<Param, Error> {
params
.get(index)
.map(|v| v.clone())
.ok_or_else(|| Error::ExpectedParameter(self.clone(), index))
}
}
impl std::fmt::Debug for Indicator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Indicator::AverageTrueRange => write!(f, "ATR"),
Indicator::EfficiencyRatio => write!(f, "ER"),
Indicator::ExponentialMovingAverage => write!(f, "EMA"),
Indicator::FastStochastic => write!(f, "FST"),
Indicator::Maximum => write!(f, "MAX"),
Indicator::Minimum => write!(f, "MIN"),
Indicator::MoneyFlowIndex => write!(f, "MFI"),
Indicator::OnBalanceVolume => write!(f, "OBV"),
Indicator::RateOfChange => write!(f, "ROC"),
Indicator::RelativeStrengthIndex => write!(f, "RSI"),
Indicator::SimpleMovingAverage => write!(f, "SMA"),
Indicator::SlowStochastic => write!(f, "SST"),
Indicator::TrueRange => write!(f, "TR"),
Indicator::Spread => write!(f, "Spread"),
Indicator::MovingAverageConvergenceDivergence => write!(f, "MACD"),
Indicator::AskRate => write!(f, "Ask"),
Indicator::BidRate => write!(f, "Bid"),
Indicator::EntryRate => write!(f, "Entry"),
Indicator::ExitRate => write!(f, "Exit"),
Indicator::ClosePrice => write!(f, "Close"),
}
}
}
impl std::str::FromStr for Indicator {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"AverageTrueRange" => Ok(Indicator::AverageTrueRange),
"EfficiencyRatio" => Ok(Indicator::EfficiencyRatio),
"ExponentialMovingAverage" => Ok(Indicator::ExponentialMovingAverage),
"FastStochastic" => Ok(Indicator::FastStochastic),
"Maximum" => Ok(Indicator::Maximum),
"Minimum" => Ok(Indicator::Minimum),
"MoneyFlowIndex" => Ok(Indicator::MoneyFlowIndex),
"OnBalanceVolume" => Ok(Indicator::OnBalanceVolume),
"RateOfChange" => Ok(Indicator::RateOfChange),
"RelativeStrengthIndex" => Ok(Indicator::RelativeStrengthIndex),
"SimpleMovingAverage" => Ok(Indicator::SimpleMovingAverage),
"SlowStochastic" => Ok(Indicator::SlowStochastic),
"TrueRange" => Ok(Indicator::TrueRange),
"Spread" => Ok(Indicator::Spread),
"MovingAverageConvergenceDivergence" => {
Ok(Indicator::MovingAverageConvergenceDivergence)
}
"AskRate" => Ok(Indicator::AskRate),
"BidRate" => Ok(Indicator::BidRate),
"EntryRate" => Ok(Indicator::EntryRate),
"ExitRate" => Ok(Indicator::ExitRate),
"ClosePrice" => Ok(Indicator::ClosePrice),
_ => Err(()),
}
}
}