pricedb/
model.rs

1/*!
2 * Model definitions
3 */
4
5use std::fmt::Display;
6
7use rust_decimal::Decimal;
8
9#[derive(Debug, Default, Clone, Eq, PartialEq, PartialOrd, Ord)]
10pub struct Price {
11    /// Symbol in format NAMESPACE:SYMBOL
12    pub symbol: String,
13    pub id: i64,
14    pub date: String,
15    pub time: String,
16    pub value: i64,
17    pub denom: i64,
18    pub currency: String,
19}
20
21impl Price {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn to_decimal(&self) -> Decimal {
27        let scale = self.scale();
28
29        Decimal::new(self.value, scale)
30    }
31
32    pub fn scale(&self) -> u32 {
33        let denom_f = self.denom as f64;
34        let scale = denom_f.log10();
35
36        scale as u32
37    }
38
39    pub fn default_time() -> String {
40        "00:00:00".to_owned()
41    }
42}
43
44#[derive(Debug, Default)]
45pub struct PriceFilter {
46    pub symbol: Option<String>,
47    pub date: Option<String>,
48    pub time: Option<String>,
49}
50
51impl PriceFilter {
52    pub fn new() -> Self {
53        Self::default()
54    }
55}
56
57#[derive(Debug, Default, PartialEq)]
58pub struct SecurityFilter {
59    pub currency: Option<String>,
60    pub agent: Option<String>,
61    pub exchange: Option<String>,
62    pub symbol: Option<String>,
63}
64
65impl SecurityFilter {
66    pub fn new() -> Self {
67        Self {
68            currency: None,
69            agent: None,
70            exchange: None,
71            symbol: None,
72        }
73    }
74}
75
76#[derive(Debug)]
77pub struct SecuritySymbol {
78    pub namespace: String,
79    pub mnemonic: String,
80}
81
82impl SecuritySymbol {
83    /// Parse symbol syntax, i.e. "XETRA:EL4X"
84    pub fn new(symbol: &str) -> SecuritySymbol {
85        // Create default values
86        let mut namespace = String::default();
87        let mut mnemonic = symbol.to_uppercase();
88
89        // Try simple parsing.
90        let parts: Vec<&str> = symbol.split(':').collect();
91        if parts.len() > 1 {
92            namespace = parts[0].to_uppercase();
93            mnemonic = parts[1].to_uppercase();
94        }
95        log::debug!("parts: {:?}", &parts);
96
97        SecuritySymbol {
98            namespace,
99            mnemonic,
100        }
101    }
102
103    pub fn new_separated(exchange: &str, symbol: &str) -> Self {
104        Self {
105            namespace: exchange.to_uppercase(),
106            mnemonic: symbol.to_uppercase()
107        }
108    }
109}
110
111impl Display for SecuritySymbol {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        write!(f, "{}:{}", self.namespace, self.mnemonic)?;
114        Ok(())
115    }
116}
117
118impl From<&str> for SecuritySymbol {
119    fn from(item: &str) -> Self {
120        SecuritySymbol::new(item)
121    }
122}
123
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_parse() {
131        let s = SecuritySymbol::new("XETRA:EL4X");
132
133        assert_eq!(s.namespace, "XETRA");
134        assert_eq!(s.mnemonic, "EL4X");
135    }
136
137    #[test]
138    fn test_parse_currency() {
139        let s = SecuritySymbol::new("AUD");
140
141        assert_eq!(s.namespace, "");
142        assert_eq!(s.mnemonic, "AUD");
143    }
144
145    #[test]
146    fn scale_calculation() {
147        let mut p = Price::new();
148
149        // we need only the price values
150        p.value = 12345;
151        p.denom = 100;
152        assert_eq!(2, p.scale());
153
154        p.value = 12345;
155        p.denom = 1000;
156        assert_eq!(3, p.scale());
157    }
158
159    #[test]
160    fn price_value() {
161        let mut p = Price::new();
162        // we need only the price values
163        p.value = 12345;
164        p.denom = 100;
165
166        let actual = p.to_decimal();
167
168        assert_eq!(actual, Decimal::from_str_exact("123.45").unwrap());
169        assert_eq!(actual.to_string(), "123.45");
170    }
171
172    #[test]
173    /// What is the default?
174    fn test_sec_filter_default() {
175        let def = SecurityFilter::default();
176        let new = SecurityFilter::new();
177
178        assert_eq!(def, new);
179    }
180}