1use rand::Rng;
4
5use cxmr_ta_core::indicators;
6
7use super::{impls, traits, Error, Operation};
8
9pub type Param = u32;
11
12pub type Params = Vec<Param>;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum ParamDescriptor {
18 Step {
20 min: Param,
22 max: Param,
24 step: Param,
26 },
27 Power {
29 min: Param,
31 max: Param,
33 power: Param,
35 },
36}
37
38impl ParamDescriptor {
39 pub fn rand<R: Rng>(&self, r: &mut R) -> Param {
41 match self {
42 ParamDescriptor::Step { min, max, step } => {
43 let rand = r.gen_range(min, max);
44 rand * step
45 }
46 ParamDescriptor::Power { min, max, power } => {
47 let rand = r.gen_range(min, max);
48 rand.pow(*power)
49 }
50 }
51 }
52}
53
54#[macro_export]
55macro_rules! param {
56 ( $min:expr, $max:expr, $step:expr ) => {
57 ParamDescriptor::Step {
58 min: $min,
59 max: $max,
60 step: $step,
61 }
62 };
63}
64
65#[macro_export]
66macro_rules! pow_param {
67 ( $min:expr, $max:expr, $power:expr ) => {
68 ParamDescriptor::Power {
69 min: $min,
70 max: $max,
71 power: $power,
72 }
73 };
74}
75
76#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
78pub enum Marker {
79 Rate,
81 Freq,
83}
84
85impl From<&Indicator> for Marker {
86 fn from(indicator: &Indicator) -> Marker {
87 match indicator {
88 Indicator::FastStochastic
89 | Indicator::SlowStochastic
90 | Indicator::EfficiencyRatio
91 | Indicator::RelativeStrengthIndex
92 | Indicator::MoneyFlowIndex
93 | Indicator::RateOfChange
94 | Indicator::AverageTrueRange
95 | Indicator::TrueRange
96 | Indicator::Spread => Marker::Freq,
97 _ => Marker::Rate,
98 }
99 }
100}
101
102#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
104pub enum Indicator {
105 AverageTrueRange,
107 EfficiencyRatio,
109 ExponentialMovingAverage,
111 FastStochastic,
113 Maximum,
115 Minimum,
117 MoneyFlowIndex,
119 OnBalanceVolume,
121 RateOfChange,
123 RelativeStrengthIndex,
125 SimpleMovingAverage,
127 SlowStochastic,
129 TrueRange,
131 Spread,
133 MovingAverageConvergenceDivergence,
135 ClosePrice,
137 AskRate,
139 BidRate,
141 EntryRate,
143 ExitRate,
145}
146
147pub type BoxIndicator<S> = Box<dyn traits::Next<S>>;
149
150#[macro_export]
151macro_rules! construct_indicator {
152 ( $s:ident, $p:ident, $t:ident, $( $x:tt ), * ) => {
153 {
154 let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
155 Box::new(indicators::$t::new(
156 $(
157 $s.get_param(¶ms, $x)?,
158 )*
159 )?) as BoxIndicator<S>
160 }
161 };
162}
163
164#[macro_export]
165macro_rules! construct_indicator_local {
166 ( $s:ident, $p:ident, $t:ident, $i:expr, $( $x:tt ), * ) => {
167 {
168 let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
169 Box::new($i(
170 $(
171 $s.get_param(¶ms, $x)? as usize,
172 )*
173 )) as BoxIndicator<S>
174 }
175 };
176}
177
178#[macro_export]
179macro_rules! construct_indicator_wrapper {
180 ( $s:ident, $p:ident, $t:ident, $i:expr, $( $x:tt ), * ) => {
181 {
182 let params = $p.ok_or_else(|| Error::ExpectedParams($s.clone()))?;
183 Box::new($i(indicators::$t::new(
184 $(
185 $s.get_param(¶ms, $x)?,
186 )*
187 )?)) as BoxIndicator<S>
188 }
189 };
190}
191
192#[macro_export]
193macro_rules! static_params {
194 ( $( $x:expr ), * ) => {
195 {
196 static LIST: &'static [ParamDescriptor] = &[$($x,)*];
197 LIST
198 }
199 };
200}
201
202impl Indicator {
203 pub fn parameters(&self) -> usize {
205 match self {
206 Indicator::ExponentialMovingAverage
207 | Indicator::SimpleMovingAverage
208 | Indicator::RelativeStrengthIndex
209 | Indicator::FastStochastic
210 | Indicator::Maximum
211 | Indicator::Minimum
212 | Indicator::MoneyFlowIndex
213 | Indicator::AverageTrueRange
214 | Indicator::EfficiencyRatio
215 | Indicator::ClosePrice
216 | Indicator::Spread
217 | Indicator::AskRate
218 | Indicator::BidRate
219 | Indicator::EntryRate
220 | Indicator::ExitRate
221 | Indicator::RateOfChange => 1,
222 Indicator::SlowStochastic => 2,
223 Indicator::TrueRange | Indicator::OnBalanceVolume => 0,
224 Indicator::MovingAverageConvergenceDivergence => 3,
225 }
226 }
227
228 pub fn descriptors(&self) -> Option<&'static [ParamDescriptor]> {
231 match self {
232 Indicator::MovingAverageConvergenceDivergence => Some(static_params!(
233 param!(1, 4, 10),
234 param!(1, 4, 20),
235 param!(1, 4, 3)
236 )),
237 Indicator::ExponentialMovingAverage => Some(static_params!(pow_param!(2, 8, 3))),
238 Indicator::SimpleMovingAverage => Some(static_params!(pow_param!(2, 8, 3))),
239 Indicator::RelativeStrengthIndex => Some(static_params!(pow_param!(2, 8, 3))),
240 Indicator::FastStochastic => Some(static_params!(pow_param!(2, 8, 3))),
241 Indicator::SlowStochastic => {
242 Some(static_params!(pow_param!(2, 8, 3), pow_param!(2, 8, 3)))
243 }
244 Indicator::Maximum => Some(static_params!(pow_param!(2, 8, 3))),
245 Indicator::Minimum => Some(static_params!(pow_param!(2, 8, 3))),
246 Indicator::MoneyFlowIndex => Some(static_params!(pow_param!(2, 8, 3))),
247 Indicator::EfficiencyRatio => Some(static_params!(pow_param!(2, 8, 3))),
248 Indicator::AverageTrueRange => Some(static_params!(pow_param!(2, 8, 3))),
249 Indicator::RateOfChange => Some(static_params!(pow_param!(2, 8, 3))),
250 Indicator::Spread => Some(static_params!(param!(0, 2, 1))),
251 Indicator::AskRate => Some(static_params!(param!(0, 2, 1))),
252 Indicator::BidRate => Some(static_params!(param!(0, 2, 1))),
253 Indicator::EntryRate => Some(static_params!(param!(0, 2, 1))),
254 Indicator::ExitRate => Some(static_params!(param!(0, 2, 1))),
255 Indicator::TrueRange | Indicator::OnBalanceVolume | Indicator::ClosePrice => None,
256 }
257 }
258
259 pub fn construct<S: traits::State>(
262 &self,
263 params: Option<&Params>,
264 ) -> Result<BoxIndicator<S>, Error> {
265 match self {
266 Indicator::ExponentialMovingAverage => Ok(construct_indicator!(
267 self,
268 params,
269 ExponentialMovingAverage,
270 0
271 )),
272 Indicator::SimpleMovingAverage => {
273 Ok(construct_indicator!(self, params, SimpleMovingAverage, 0))
274 }
275 Indicator::RelativeStrengthIndex => {
276 Ok(construct_indicator!(self, params, RelativeStrengthIndex, 0))
277 }
278 Indicator::FastStochastic => Ok(construct_indicator!(self, params, FastStochastic, 0)),
279 Indicator::SlowStochastic => {
280 Ok(construct_indicator!(self, params, SlowStochastic, 0, 1))
281 }
282 Indicator::Maximum => Ok(construct_indicator!(self, params, Maximum, 0)),
283 Indicator::Minimum => Ok(construct_indicator!(self, params, Minimum, 0)),
284 Indicator::MoneyFlowIndex => Ok(construct_indicator!(self, params, MoneyFlowIndex, 0)),
285 Indicator::AverageTrueRange => Ok(construct_indicator_wrapper!(
286 self,
287 params,
288 AverageTrueRange,
289 impls::AverageTrueRange,
290 0
291 )),
292 Indicator::EfficiencyRatio => Ok(construct_indicator_wrapper!(
293 self,
294 params,
295 EfficiencyRatio,
296 impls::EfficiencyRatio,
297 0
298 )),
299 Indicator::RateOfChange => Ok(construct_indicator!(self, params, EfficiencyRatio, 0)),
300 Indicator::TrueRange => {
301 Ok(Box::new(impls::TrueRange(indicators::TrueRange::new())) as BoxIndicator<S>)
302 }
303 Indicator::OnBalanceVolume => {
304 Ok(Box::new(indicators::OnBalanceVolume::new()) as BoxIndicator<S>)
305 }
306 Indicator::ClosePrice => Ok(Box::new(impls::ClosePrice) as BoxIndicator<S>),
307 Indicator::Spread => Ok(construct_indicator_local!(
308 self,
309 params,
310 Spread,
311 impls::Spread,
312 0
313 )),
314 Indicator::AskRate => Ok(construct_indicator_local!(
315 self,
316 params,
317 AskRate,
318 impls::AskRate,
319 0
320 )),
321 Indicator::BidRate => Ok(construct_indicator_local!(
322 self,
323 params,
324 BidRate,
325 impls::BidRate,
326 0
327 )),
328 Indicator::EntryRate => Ok(construct_indicator_local!(
329 self,
330 params,
331 EntryRate,
332 impls::EntryRate,
333 0
334 )),
335 Indicator::ExitRate => Ok(construct_indicator_local!(
336 self,
337 params,
338 ExitRate,
339 impls::ExitRate,
340 0
341 )),
342 Indicator::MovingAverageConvergenceDivergence => {
343 Err(Error::IndicatorNotImplemented(self.clone()))
344 }
345 }
346 }
347
348 pub fn into_op(self) -> Result<Operation, Error> {
350 Operation::new(self, None)
351 }
352
353 pub fn with_params(self, params: Params) -> Result<Operation, Error> {
355 Operation::new(self, Some(params))
356 }
357
358 fn get_param(&self, params: &Params, index: usize) -> Result<Param, Error> {
359 params
360 .get(index)
361 .map(|v| v.clone())
362 .ok_or_else(|| Error::ExpectedParameter(self.clone(), index))
363 }
364}
365
366impl std::fmt::Debug for Indicator {
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368 match self {
369 Indicator::AverageTrueRange => write!(f, "ATR"),
370 Indicator::EfficiencyRatio => write!(f, "ER"),
371 Indicator::ExponentialMovingAverage => write!(f, "EMA"),
372 Indicator::FastStochastic => write!(f, "FST"),
373 Indicator::Maximum => write!(f, "MAX"),
374 Indicator::Minimum => write!(f, "MIN"),
375 Indicator::MoneyFlowIndex => write!(f, "MFI"),
376 Indicator::OnBalanceVolume => write!(f, "OBV"),
377 Indicator::RateOfChange => write!(f, "ROC"),
378 Indicator::RelativeStrengthIndex => write!(f, "RSI"),
379 Indicator::SimpleMovingAverage => write!(f, "SMA"),
380 Indicator::SlowStochastic => write!(f, "SST"),
381 Indicator::TrueRange => write!(f, "TR"),
382 Indicator::Spread => write!(f, "Spread"),
383 Indicator::MovingAverageConvergenceDivergence => write!(f, "MACD"),
384 Indicator::AskRate => write!(f, "Ask"),
385 Indicator::BidRate => write!(f, "Bid"),
386 Indicator::EntryRate => write!(f, "Entry"),
387 Indicator::ExitRate => write!(f, "Exit"),
388 Indicator::ClosePrice => write!(f, "Close"),
389 }
390 }
391}
392
393impl std::str::FromStr for Indicator {
394 type Err = ();
395 fn from_str(s: &str) -> Result<Self, Self::Err> {
396 match s {
397 "AverageTrueRange" => Ok(Indicator::AverageTrueRange),
398 "EfficiencyRatio" => Ok(Indicator::EfficiencyRatio),
399 "ExponentialMovingAverage" => Ok(Indicator::ExponentialMovingAverage),
400 "FastStochastic" => Ok(Indicator::FastStochastic),
401 "Maximum" => Ok(Indicator::Maximum),
402 "Minimum" => Ok(Indicator::Minimum),
403 "MoneyFlowIndex" => Ok(Indicator::MoneyFlowIndex),
404 "OnBalanceVolume" => Ok(Indicator::OnBalanceVolume),
405 "RateOfChange" => Ok(Indicator::RateOfChange),
406 "RelativeStrengthIndex" => Ok(Indicator::RelativeStrengthIndex),
407 "SimpleMovingAverage" => Ok(Indicator::SimpleMovingAverage),
408 "SlowStochastic" => Ok(Indicator::SlowStochastic),
409 "TrueRange" => Ok(Indicator::TrueRange),
410 "Spread" => Ok(Indicator::Spread),
411 "MovingAverageConvergenceDivergence" => {
412 Ok(Indicator::MovingAverageConvergenceDivergence)
413 }
414 "AskRate" => Ok(Indicator::AskRate),
415 "BidRate" => Ok(Indicator::BidRate),
416 "EntryRate" => Ok(Indicator::EntryRate),
417 "ExitRate" => Ok(Indicator::ExitRate),
418 "ClosePrice" => Ok(Indicator::ClosePrice),
419 _ => Err(()),
420 }
421 }
422}