1use crate::errors::*;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct DataItem {
9 open: f64,
10 high: f64,
11 low: f64,
12 close: f64,
13 volume: f64,
14}
15
16impl DataItem {
17 pub fn builder() -> DataItemBuilder {
18 DataItemBuilder::new()
19 }
20
21 pub fn open(&self) -> f64 {
22 self.open
23 }
24
25 pub fn high(&self) -> f64 {
26 self.high
27 }
28
29 pub fn low(&self) -> f64 {
30 self.low
31 }
32
33 pub fn close(&self) -> f64 {
34 self.close
35 }
36
37 pub fn volume(&self) -> f64 {
38 self.volume
39 }
40}
41
42pub trait Open {
44 fn open(&self) -> f64;
45}
46
47pub trait High {
49 fn high(&self) -> f64;
50}
51
52pub trait Low {
54 fn low(&self) -> f64;
55}
56
57pub trait Close {
59 fn close(&self) -> f64;
60}
61
62pub trait Volume {
64 fn volume(&self) -> f64;
65}
66
67impl Open for DataItem {
68 fn open(&self) -> f64 {
69 self.open
70 }
71}
72
73impl High for DataItem {
74 fn high(&self) -> f64 {
75 self.high
76 }
77}
78
79impl Low for DataItem {
80 fn low(&self) -> f64 {
81 self.low
82 }
83}
84
85impl Close for DataItem {
86 fn close(&self) -> f64 {
87 self.close
88 }
89}
90
91impl Volume for DataItem {
92 fn volume(&self) -> f64 {
93 self.volume
94 }
95}
96
97impl<T: Open + ?Sized> Open for &T {
98 fn open(&self) -> f64 {
99 (*self).open()
100 }
101}
102
103impl<T: High + ?Sized> High for &T {
104 fn high(&self) -> f64 {
105 (*self).high()
106 }
107}
108
109impl<T: Low + ?Sized> Low for &T {
110 fn low(&self) -> f64 {
111 (*self).low()
112 }
113}
114
115impl<T: Close + ?Sized> Close for &T {
116 fn close(&self) -> f64 {
117 (*self).close()
118 }
119}
120
121impl<T: Volume + ?Sized> Volume for &T {
122 fn volume(&self) -> f64 {
123 (*self).volume()
124 }
125}
126
127pub struct DataItemBuilder {
128 open: Option<f64>,
129 high: Option<f64>,
130 low: Option<f64>,
131 close: Option<f64>,
132 volume: Option<f64>,
133}
134
135impl DataItemBuilder {
136 pub fn new() -> Self {
137 Self {
138 open: None,
139 high: None,
140 low: None,
141 close: None,
142 volume: None,
143 }
144 }
145
146 pub fn open(mut self, val: f64) -> Self {
147 self.open = Some(val);
148 self
149 }
150
151 pub fn high(mut self, val: f64) -> Self {
152 self.high = Some(val);
153 self
154 }
155
156 pub fn low(mut self, val: f64) -> Self {
157 self.low = Some(val);
158 self
159 }
160
161 pub fn close(mut self, val: f64) -> Self {
162 self.close = Some(val);
163 self
164 }
165
166 pub fn volume(mut self, val: f64) -> Self {
167 self.volume = Some(val);
168 self
169 }
170
171 pub fn build(self) -> Result<DataItem> {
172 if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) =
173 (self.open, self.high, self.low, self.close, self.volume)
174 {
175 if low <= open
177 && low <= close
178 && low <= high
179 && high >= open
180 && high >= close
181 && volume >= 0.0
182 {
183 let item = DataItem {
184 open,
185 high,
186 low,
187 close,
188 volume,
189 };
190 Ok(item)
191 } else {
192 Err(TaError::DataItemInvalid)
193 }
194 } else {
195 Err(TaError::DataItemIncomplete)
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_builder() {
206 fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
207 let result = DataItem::builder()
208 .open(open)
209 .high(high)
210 .low(low)
211 .close(close)
212 .volume(volume)
213 .build();
214 assert!(result.is_ok());
215 }
216
217 fn assert_invalid(record: (f64, f64, f64, f64, f64)) {
218 let (open, high, low, close, volume) = record;
219 let result = DataItem::builder()
220 .open(open)
221 .high(high)
222 .low(low)
223 .close(close)
224 .volume(volume)
225 .build();
226 assert_eq!(result, Err(TaError::DataItemInvalid));
227 }
228
229 let valid_records = vec![
230 (20.0, 25.0, 15.0, 21.0, 7500.0),
232 (10.0, 10.0, 10.0, 10.0, 10.0),
233 (0.0, 0.0, 0.0, 0.0, 0.0),
234 ];
235 for record in valid_records {
236 assert_valid(record)
237 }
238
239 let invalid_records = vec![
240 (-1.0, 25.0, 15.0, 21.0, 7500.0),
242 (20.0, -1.0, 15.0, 21.0, 7500.0),
243 (20.0, 25.0, 15.0, -1.0, 7500.0),
244 (20.0, 25.0, 15.0, 21.0, -1.0),
245 (14.9, 25.0, 15.0, 21.0, 7500.0),
246 (25.1, 25.0, 15.0, 21.0, 7500.0),
247 (20.0, 25.0, 15.0, 14.9, 7500.0),
248 (20.0, 25.0, 15.0, 25.1, 7500.0),
249 (20.0, 15.0, 25.0, 21.0, 7500.0),
250 ];
251 for record in invalid_records {
252 assert_invalid(record)
253 }
254 }
255}