quantaxis_rs/
data_item.rs

1use crate::errors::*;
2use crate::traits::{Close, High, Low, Open, Volume};
3
4#[derive(Debug, Clone)]
5pub struct DataItem {
6    open: f64,
7    high: f64,
8    low: f64,
9    close: f64,
10    volume: f64,
11}
12
13impl DataItem {
14    pub fn builder() -> DataItemBuilder {
15        DataItemBuilder::new()
16    }
17}
18
19impl Open for DataItem {
20    fn open(&self) -> f64 {
21        self.open
22    }
23}
24
25impl High for DataItem {
26    fn high(&self) -> f64 {
27        self.high
28    }
29}
30
31impl Low for DataItem {
32    fn low(&self) -> f64 {
33        self.low
34    }
35}
36
37impl Close for DataItem {
38    fn close(&self) -> f64 {
39        self.close
40    }
41}
42
43impl Volume for DataItem {
44    fn volume(&self) -> f64 {
45        self.volume
46    }
47}
48
49pub struct DataItemBuilder {
50    open: Option<f64>,
51    high: Option<f64>,
52    low: Option<f64>,
53    close: Option<f64>,
54    volume: Option<f64>,
55}
56
57impl DataItemBuilder {
58    pub fn new() -> Self {
59        Self {
60            open: None,
61            high: None,
62            low: None,
63            close: None,
64            volume: None,
65        }
66    }
67
68    pub fn open(mut self, val: f64) -> Self {
69        self.open = Some(val);
70        self
71    }
72
73    pub fn high(mut self, val: f64) -> Self {
74        self.high = Some(val);
75        self
76    }
77
78    pub fn low(mut self, val: f64) -> Self {
79        self.low = Some(val);
80        self
81    }
82
83    pub fn close(mut self, val: f64) -> Self {
84        self.close = Some(val);
85        self
86    }
87
88    pub fn volume(mut self, val: f64) -> Self {
89        self.volume = Some(val);
90        self
91    }
92
93    pub fn build(self) -> Result<DataItem> {
94        if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) =
95            (self.open, self.high, self.low, self.close, self.volume)
96        {
97            // validate
98            if low <= open
99                && low <= close
100                && low <= high
101                && high >= open
102                && high >= close
103                && volume >= 0.0
104                && low >= 0.0
105            {
106                let item = DataItem {
107                    open,
108                    high,
109                    low,
110                    close,
111                    volume,
112                };
113                Ok(item)
114            } else {
115                Err(Error::from_kind(ErrorKind::DataItemInvalid))
116            }
117        } else {
118            Err(Error::from_kind(ErrorKind::DataItemIncomplete))
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_builder() {
129        fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
130            let result = DataItem::builder()
131                .open(open)
132                .high(high)
133                .low(low)
134                .close(close)
135                .volume(volume)
136                .build();
137
138            assert!(result.is_ok());
139            println!("{:#?}", result.unwrap());
140        }
141
142        fn assert_invalid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
143            let result = DataItem::builder()
144                .open(open)
145                .high(high)
146                .low(low)
147                .close(close)
148                .volume(volume)
149                .build();
150            assert!(result.is_err());
151        }
152
153        let valid_records = vec![
154            // open, high, low , close, volume
155            (20.0, 25.0, 15.0, 21.0, 7500.0),
156            (10.0, 10.0, 10.0, 10.0, 10.0),
157            (0.0, 0.0, 0.0, 0.0, 0.0),
158        ];
159        for record in valid_records {
160            assert_valid(record)
161        }
162
163        let invalid_records = vec![
164            // open, high, low , close, volume
165            (-1.0, 25.0, 15.0, 21.0, 7500.0),
166            (20.0, -1.0, 15.0, 21.0, 7500.0),
167            (20.0, 25.0, -1.0, 21.0, 7500.0),
168            (20.0, 25.0, 15.0, -1.0, 7500.0),
169            (20.0, 25.0, 15.0, 21.0, -1.0),
170            (14.9, 25.0, 15.0, 21.0, 7500.0),
171            (25.1, 25.0, 15.0, 21.0, 7500.0),
172            (20.0, 25.0, 15.0, 14.9, 7500.0),
173            (20.0, 25.0, 15.0, 25.1, 7500.0),
174            (20.0, 15.0, 25.0, 21.0, 7500.0),
175        ];
176        for record in invalid_records {
177            assert_invalid(record)
178        }
179    }
180}