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 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 (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 (-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}