1use crate::errors::*;
2use crate::traits::{Close, High, Low, Open, Volume};
3use alloc::vec;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[derive(Debug, Clone, PartialEq)]
34pub struct DataItem {
35 open: f64,
36 high: f64,
37 low: f64,
38 close: f64,
39 volume: f64,
40}
41
42impl DataItem {
43 pub fn builder() -> DataItemBuilder {
44 DataItemBuilder::new()
45 }
46}
47
48impl Open for DataItem {
49 fn open(&self) -> f64 {
50 self.open
51 }
52}
53
54impl High for DataItem {
55 fn high(&self) -> f64 {
56 self.high
57 }
58}
59
60impl Low for DataItem {
61 fn low(&self) -> f64 {
62 self.low
63 }
64}
65
66impl Close for DataItem {
67 fn close(&self) -> f64 {
68 self.close
69 }
70}
71
72impl Volume for DataItem {
73 fn volume(&self) -> f64 {
74 self.volume
75 }
76}
77
78pub struct DataItemBuilder {
79 open: Option<f64>,
80 high: Option<f64>,
81 low: Option<f64>,
82 close: Option<f64>,
83 volume: Option<f64>,
84}
85
86impl DataItemBuilder {
87 pub fn new() -> Self {
88 Self {
89 open: None,
90 high: None,
91 low: None,
92 close: None,
93 volume: None,
94 }
95 }
96
97 pub fn open(mut self, val: f64) -> Self {
98 self.open = Some(val);
99 self
100 }
101
102 pub fn high(mut self, val: f64) -> Self {
103 self.high = Some(val);
104 self
105 }
106
107 pub fn low(mut self, val: f64) -> Self {
108 self.low = Some(val);
109 self
110 }
111
112 pub fn close(mut self, val: f64) -> Self {
113 self.close = Some(val);
114 self
115 }
116
117 pub fn volume(mut self, val: f64) -> Self {
118 self.volume = Some(val);
119 self
120 }
121
122 pub fn build(self) -> Result<DataItem> {
123 if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) =
124 (self.open, self.high, self.low, self.close, self.volume)
125 {
126 if low <= open
128 && low <= close
129 && low <= high
130 && high >= open
131 && high >= close
132 && volume >= 0.0
133 {
134 let item = DataItem {
135 open,
136 high,
137 low,
138 close,
139 volume,
140 };
141 Ok(item)
142 } else {
143 Err(TaError::DataItemInvalid)
144 }
145 } else {
146 Err(TaError::DataItemIncomplete)
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_builder() {
157 fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
158 let result = DataItem::builder()
159 .open(open)
160 .high(high)
161 .low(low)
162 .close(close)
163 .volume(volume)
164 .build();
165 assert!(result.is_ok());
166 }
167
168 fn assert_invalid(record: (f64, f64, f64, f64, f64)) {
169 let (open, high, low, close, volume) = record;
170 let result = DataItem::builder()
171 .open(open)
172 .high(high)
173 .low(low)
174 .close(close)
175 .volume(volume)
176 .build();
177 assert_eq!(result, Err(TaError::DataItemInvalid));
178 }
179
180 let valid_records = vec![
181 (20.0, 25.0, 15.0, 21.0, 7500.0),
183 (10.0, 10.0, 10.0, 10.0, 10.0),
184 (0.0, 0.0, 0.0, 0.0, 0.0),
185 ];
186 for record in valid_records {
187 assert_valid(record)
188 }
189
190 let invalid_records = vec![
191 (-1.0, 25.0, 15.0, 21.0, 7500.0),
193 (20.0, -1.0, 15.0, 21.0, 7500.0),
194 (20.0, 25.0, 15.0, -1.0, 7500.0),
195 (20.0, 25.0, 15.0, 21.0, -1.0),
196 (14.9, 25.0, 15.0, 21.0, 7500.0),
197 (25.1, 25.0, 15.0, 21.0, 7500.0),
198 (20.0, 25.0, 15.0, 14.9, 7500.0),
199 (20.0, 25.0, 15.0, 25.1, 7500.0),
200 (20.0, 15.0, 25.0, 21.0, 7500.0),
201 ];
202 for record in invalid_records {
203 assert_invalid(record)
204 }
205 }
206}