Skip to content

Instantly share code, notes, and snippets.

@tommady
Created January 2, 2021 12:38
Show Gist options
  • Save tommady/076ffb088259ef5e0c21e1f7bb14e46d to your computer and use it in GitHub Desktop.
Save tommady/076ffb088259ef5e0c21e1f7bb14e46d to your computer and use it in GitHub Desktop.
Rust HashMap with Traits #post

I was trying to use this lib

this lib is cool that can help you to calculate financing indicators, and it used a beautiful way to present the incoming value of an indicator: a Next trait

so after some coding the code looks like

let mut ema5 = EMA::new(5)?;
let mut ema7 = EMA::new(7)?;
let mut ema10 = EMA::new(10)?;
let mut ema14 = EMA::new(14)?;
let mut ema20 = EMA::new(20)?;
let mut ema35 = EMA::new(35)?;
let mut ema60 = EMA::new(60)?;
let mut ema90 = EMA::new(90)?;
let mut ema120 = EMA::new(120)?;

for info in infos {
    let dt = DataItem::builder()
        .open(info.open)
        .high(info.high)
        .low(info.low)
        .close(info.close)
        volume(info.volume)
        .build()?
        
    r.ema5 = ema5.next(&dt);
    r.ema7 = ema7.next(&dt);
    r.ema10 = ema10.next(&dt);
    r.ema14 = ema14.next(&dt);
    r.ema20 = ema20.next(&dt);
    r.ema35 = ema35.next(&dt);
    r.ema60 = ema60.next(&dt);
    r.ema90 = ema90.next(&dt);
    r.ema120 = ema120.next(&dt);
}

ema5.reset();
ema7.reset();
ema10.reset();
ema14.reset();
ema20.reset();
ema35.reset();
ema60.reset();
ema90.reset();
ema120.reset();

a lot of duplicate function calls, so it makes me thinking a way to collect them in a Vector or Map then I can looping those function calls.

first I tried to modify a play ground example to see is storing a trait into a map as value doable, seems OK!

but here comes the hard part, because the Next trait in the TA lib has different output, like EMA returns a single f64 but MACD returns a tuple of three f64...

the error is

E0191: the value of the associated type `Output` (from trait `ta::Next`) must be specified

and since the Output type still cannot specific into multiple types, so the way I did is sperated different return types into different HashMaps, the final code will be like:

trait CalcOp: Next<&'static DataItem> + Reset {}

impl CalcOp for EMA {}
impl CalcOp for SMA {}
impl CalcOp for MACD {}

struct Calc {
    ind_f64: HashMap<Indicators, Box<dyn CalcOp<Output = f64>>>,
    ind_tripule_f64: HashMap<Indicators, Box<dyn CalcOp<Output = (f64, f64, f64)>>>,
}

impl Calc {
    fn new() -> Calc {
        let mut c = Calc {
            // the num of the Indicator enum
            // trying to find a smart way to solve this
            ind_f64: HashMap::with_capacity(15),
            ind_tripule_f64: HashMap::with_capacity(3),
        };

        c.ind_f64
            .insert(Indicators::EMA5, Box::new(EMA::new(5).unwrap()));
        c.ind_tripule_f64
            .insert(Indicators::MACD12, Box::new(MACD::new(6, 12, 9).unwrap()));

        c
    }
    fn next(&mut self, dt: &'static DataItem) {
        for (_, v) in self.ind_f64.iter_mut() {
            v.next();
        }

        for (_, v) in self.ind_tripule_f64.iter_mut() {
            v.next();
        }
    }

    fn reset(&mut self) {
        for (_, v) in self.ind_f64.iter_mut() {
            v.reset();
        }

        for (_, v) in self.ind_tripule_f64.iter_mut() {
            v.reset();
        }
    }
}

but there has some problems still remained:

  1. E0271: type mismatch resolving <ta::indicators::MovingAverageConvergenceDivergence as ta::Next<&'static ta::DataIte m>>::Output == (f64, f64, f64) expected tuple, found struct

good articles that I referenced:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment