quant_suite/models/
price_alert.rs

1use std::collections::HashMap;
2
3use nanoid::nanoid;
4
5use crate::AlertSet;
6
7/// A `PriceAlert` struct for setting up price alerts on trading symbols.
8///
9/// This struct allows to set high and low price alerts for specific trading symbols.
10/// Alerts are triggered based on the bid price of the symbol.
11pub struct PriceAlert {
12    // For each symbol_id with its alert type (high or low)
13    alert_price: HashMap<u32, HashMap<String, AlertSet>>,
14    // Maps each alert ID to its corresponding symbol_id
15    id2symbol: HashMap<String, u32>,
16    // Stores the current price for symbol_id (bid, ask)
17    price: HashMap<u32, (f64, f64)>,
18}
19
20impl PriceAlert {
21    pub fn new() -> Self {
22        Self {
23            alert_price: HashMap::new(),
24            price: HashMap::new(),
25            id2symbol: HashMap::new(),
26        }
27    }
28    pub fn get_price(&self, symbol_id: u32) -> Option<f64> {
29        self.price.get(&symbol_id).map(|(b, _)| *b)
30    }
31
32    pub fn on_price(&mut self, symbol_id: u32, price: (f64, f64)) -> Option<Vec<String>> {
33        let Some(alert_list) = self.alert_price.get_mut(&symbol_id) else {
34            return None;
35        };
36        let old_price = self.price.insert(symbol_id, price.clone()).unwrap_or(price);
37        let ids = alert_list
38            .iter()
39            .map(|(id, alertset)| match alertset {
40                AlertSet::High(p) => (id, *p <= old_price.0 || *p <= price.0),
41                AlertSet::Low(p) => (id, *p >= old_price.0 || *p >= price.0),
42            })
43            .filter(|(_, b)| *b)
44            .map(|(id, _)| id.clone())
45            .collect::<Vec<_>>();
46
47        ids.iter().for_each(|id| {
48            let _alertset = alert_list.remove(id);
49            self.id2symbol.remove(id);
50        });
51
52        if ids.is_empty() {
53            return None;
54        }
55        Some(ids)
56    }
57
58    /// Modify the price
59    ///
60    /// * Returns the new AlertSet
61    pub fn modify_price(&mut self, alert_id: String, price: f64) -> Option<AlertSet> {
62        if let Some(symbol) = self.id2symbol.get(&alert_id) {
63            if let Some(cont) = self.alert_price.get_mut(&symbol) {
64                if let Some(alert) = cont.remove(&alert_id) {
65                    let new_alert = match alert {
66                        AlertSet::Low(_) => AlertSet::Low(price),
67                        AlertSet::High(_) => AlertSet::High(price),
68                    };
69                    cont.insert(alert_id, new_alert.clone());
70                    return Some(new_alert);
71                }
72            }
73        }
74        None
75    }
76
77    pub fn remove(&mut self, alert_id: String) -> Option<AlertSet> {
78        if let Some(symbol) = self.id2symbol.remove(&alert_id) {
79            if let Some(cont) = self.alert_price.get_mut(&symbol) {
80                return cont.remove(&alert_id);
81            }
82        }
83        None
84    }
85
86    pub fn set_alert(&mut self, symbol_id: u32, set: AlertSet, alert_id: Option<String>) -> String {
87        // FIXME later check the alert_id
88        //
89        let alert_id = alert_id.unwrap_or(nanoid!(10));
90
91        self.id2symbol.insert(alert_id.clone(), symbol_id);
92        self.alert_price
93            .entry(symbol_id)
94            .or_insert(HashMap::new())
95            .insert(alert_id.clone(), set);
96
97        alert_id
98    }
99}