cxmr_ta/
operation.rs

1//! Technical analysis operations with parameters.
2
3use super::{traits::State, BoxIndicator, Error, Indicator, Params};
4
5/// Parametrized technical analysis operation.
6#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Serialize, Deserialize)]
7pub struct Operation {
8    pub indicator: Indicator,
9    pub parameters: Option<Params>,
10}
11
12impl Operation {
13    /// Creates new variable structure with no parameters.
14    pub fn constant(op: Indicator) -> Result<Self, Error> {
15        Self::new(op, None)
16    }
17
18    /// Creates new variable structure with optional parameters.
19    /// Returns error if parameter count doesn't match expected.
20    pub fn new(op: Indicator, params: Option<Params>) -> Result<Self, Error> {
21        match op {
22            Indicator::MovingAverageConvergenceDivergence
23            | Indicator::MoneyFlowIndex
24            | Indicator::EfficiencyRatio
25            | Indicator::RateOfChange
26            | Indicator::AverageTrueRange
27            | Indicator::ExponentialMovingAverage
28            | Indicator::SimpleMovingAverage
29            | Indicator::RelativeStrengthIndex
30            | Indicator::FastStochastic
31            | Indicator::SlowStochastic
32            | Indicator::Spread
33            | Indicator::AskRate
34            | Indicator::BidRate
35            | Indicator::EntryRate
36            | Indicator::ExitRate
37            | Indicator::Maximum
38            | Indicator::Minimum => {
39                let params = params.ok_or_else(|| Error::ExpectedParams(op.clone()))?;
40                if params.len() != op.parameters() {
41                    Err(Error::InvalidParams(op, params))
42                } else {
43                    Ok(Operation {
44                        indicator: op,
45                        parameters: Some(params),
46                    })
47                }
48            }
49            Indicator::TrueRange | Indicator::OnBalanceVolume | Indicator::ClosePrice => {
50                Ok(Operation {
51                    indicator: op,
52                    parameters: None,
53                })
54            }
55        }
56    }
57
58    /// Constructs technical analysis indicator.
59    pub fn construct<S: State>(&self) -> Result<BoxIndicator<S>, Error> {
60        self.indicator.construct(self.parameters.as_ref())
61    }
62}
63
64impl std::fmt::Debug for Operation {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        match &self.parameters {
67            Some(params) => {
68                if params.len() > 0 {
69                    write!(
70                        f,
71                        "{:?}({})",
72                        self.indicator,
73                        params
74                            .iter()
75                            .map(|p| p.to_string())
76                            .collect::<Vec<String>>()
77                            .join(", ")
78                    )
79                } else {
80                    write!(f, "{:?}", self.indicator)
81                }
82            }
83            None => write!(f, "{:?}", self.indicator),
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn var_test() {
94        let params = vec![
95            (Indicator::TrueRange.into_op(), true),
96            (Indicator::AverageTrueRange.into_op(), false),
97            (Indicator::Maximum.with_params(vec![10]), true),
98            (Indicator::Maximum.with_params(vec![10, 40, 10]), false),
99            (
100                Indicator::MovingAverageConvergenceDivergence.with_params(vec![10, 40]),
101                false,
102            ),
103            (
104                Indicator::MovingAverageConvergenceDivergence.with_params(vec![10, 40, 10]),
105                true,
106            ),
107        ];
108
109        for (res, ok) in params {
110            assert_eq!(res.is_ok(), ok, "{:?}", res);
111        }
112    }
113}