1use super::bar::Bar;
6
7#[derive(Debug, Clone, Default)]
20pub struct BarData {
21 pub bars: Vec<Bar>,
23}
24
25impl BarData {
26 pub fn new() -> Self {
28 Self { bars: Vec::new() }
29 }
30
31 pub fn with_capacity(capacity: usize) -> Self {
33 Self {
34 bars: Vec::with_capacity(capacity),
35 }
36 }
37
38 pub fn from_bars(bars: Vec<Bar>) -> Self {
40 Self { bars }
41 }
42
43 pub fn push(&mut self, bar: Bar) {
45 self.bars.push(bar);
46 }
47
48 pub fn len(&self) -> usize {
50 self.bars.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.bars.is_empty()
56 }
57
58 pub fn clear(&mut self) {
60 self.bars.clear();
61 }
62
63 pub fn first(&self) -> Option<&Bar> {
65 self.bars.first()
66 }
67
68 pub fn last(&self) -> Option<&Bar> {
70 self.bars.last()
71 }
72
73 pub fn get(&self, index: usize) -> Option<&Bar> {
75 self.bars.get(index)
76 }
77
78 pub fn get_mut(&mut self, index: usize) -> Option<&mut Bar> {
80 self.bars.get_mut(index)
81 }
82
83 pub fn iter(&self) -> impl Iterator<Item = &Bar> {
85 self.bars.iter()
86 }
87
88 pub fn min_price(&self) -> Option<f64> {
94 self.bars
95 .iter()
96 .map(|c| c.low)
97 .filter(|v| v.is_finite())
98 .min_by(f64::total_cmp)
99 }
100
101 pub fn max_price(&self) -> Option<f64> {
105 self.bars
106 .iter()
107 .map(|c| c.high)
108 .filter(|v| v.is_finite())
109 .max_by(f64::total_cmp)
110 }
111
112 pub fn price_range(&self) -> Option<(f64, f64)> {
114 let min = self.min_price()?;
115 let max = self.max_price()?;
116 Some((min, max))
117 }
118
119 pub fn max_volume(&self) -> Option<f64> {
123 self.bars
124 .iter()
125 .map(|c| c.volume)
126 .filter(|v| v.is_finite())
127 .max_by(f64::total_cmp)
128 }
129
130 pub fn total_volume(&self) -> f64 {
132 self.bars.iter().map(|c| c.volume).sum()
133 }
134
135 pub fn avg_volume(&self) -> Option<f64> {
137 if self.bars.is_empty() {
138 return None;
139 }
140 Some(self.total_volume() / self.bars.len() as f64)
141 }
142
143 pub fn to_heikin_ashi(&self) -> Self {
156 if self.bars.is_empty() {
157 return Self::new();
158 }
159
160 let mut ha_bars = Vec::with_capacity(self.bars.len());
161
162 let first = &self.bars[0];
164 let mut prev_ha_open = (first.open + first.close) / 2.0;
165 let mut prev_ha_close = (first.open + first.high + first.low + first.close) / 4.0;
166
167 ha_bars.push(Bar::new(
168 first.time,
169 prev_ha_open,
170 first.high,
171 first.low,
172 prev_ha_close,
173 first.volume,
174 ));
175
176 for bar in self.bars.iter().skip(1) {
178 let ha_close = (bar.open + bar.high + bar.low + bar.close) / 4.0;
179 let ha_open = (prev_ha_open + prev_ha_close) / 2.0;
180 let ha_high = bar.high.max(ha_open).max(ha_close);
181 let ha_low = bar.low.min(ha_open).min(ha_close);
182
183 ha_bars.push(Bar::new(
184 bar.time, ha_open, ha_high, ha_low, ha_close, bar.volume,
185 ));
186
187 prev_ha_open = ha_open;
188 prev_ha_close = ha_close;
189 }
190
191 Self::from_bars(ha_bars)
192 }
193
194 pub fn to_regular(&self) -> Self {
198 self.clone()
199 }
200
201 pub fn slice(&self, start: usize, end: usize) -> &[Bar] {
203 let end = end.min(self.bars.len());
204 let start = start.min(end);
205 &self.bars[start..end]
206 }
207}
208
209impl IntoIterator for BarData {
210 type Item = Bar;
211 type IntoIter = std::vec::IntoIter<Bar>;
212
213 fn into_iter(self) -> Self::IntoIter {
214 self.bars.into_iter()
215 }
216}
217
218impl<'a> IntoIterator for &'a BarData {
219 type Item = &'a Bar;
220 type IntoIter = std::slice::Iter<'a, Bar>;
221
222 fn into_iter(self) -> Self::IntoIter {
223 self.bars.iter()
224 }
225}
226
227impl FromIterator<Bar> for BarData {
228 fn from_iter<I: IntoIterator<Item = Bar>>(iter: I) -> Self {
229 Self {
230 bars: iter.into_iter().collect(),
231 }
232 }
233}
234
235impl std::ops::Index<usize> for BarData {
236 type Output = Bar;
237
238 fn index(&self, index: usize) -> &Self::Output {
239 &self.bars[index]
240 }
241}
242
243impl std::ops::IndexMut<usize> for BarData {
244 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
245 &mut self.bars[index]
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use chrono::Utc;
253
254 fn create_test_bars() -> BarData {
255 let now = Utc::now();
256 BarData::from_bars(vec![
257 Bar::new(now, 100.0, 110.0, 95.0, 105.0, 1000.0),
258 Bar::new(now, 105.0, 115.0, 100.0, 110.0, 1500.0),
259 Bar::new(now, 110.0, 120.0, 105.0, 115.0, 1200.0),
260 ])
261 }
262
263 #[test]
264 fn test_aggregations() {
265 let (nan, inf) = (f64::NAN, f64::INFINITY);
266 let cases = [
268 (
269 "finite only",
270 vec![
271 (95.0, 110.0, 1000.0),
272 (100.0, 115.0, 1500.0),
273 (105.0, 120.0, 1200.0),
274 ],
275 (Some(95.0), Some(120.0), Some(1500.0)),
276 ),
277 (
278 "NaN first",
279 vec![(nan, nan, nan), (95.0, 110.0, 1000.0)],
280 (Some(95.0), Some(110.0), Some(1000.0)),
281 ),
282 (
283 "NaN last",
284 vec![(95.0, 110.0, 1000.0), (nan, nan, nan)],
285 (Some(95.0), Some(110.0), Some(1000.0)),
286 ),
287 (
288 "infinite high",
289 vec![(95.0, inf, 1000.0), (100.0, 110.0, 1500.0)],
290 (Some(95.0), Some(110.0), Some(1500.0)),
291 ),
292 ("all NaN", vec![(nan, nan, nan)], (None, None, None)),
293 ];
294 let now = Utc::now();
295 for (case, rows, expected) in cases {
296 let data = BarData::from_bars(
297 rows.iter()
298 .map(|&(low, high, volume)| Bar::new(now, low, high, low, low, volume))
299 .collect(),
300 );
301 let actual = (data.min_price(), data.max_price(), data.max_volume());
302 assert_eq!(actual, expected, "{case}");
303 }
304 assert_eq!(create_test_bars().total_volume(), 3700.0);
305 }
306
307 #[test]
308 fn test_heikin_ashi() {
309 let data = create_test_bars();
310 let ha = data.to_heikin_ashi();
311
312 let actual: Vec<_> = ha.iter().map(|bar| (bar.open, bar.close)).collect();
314 assert_eq!(actual, [(102.5, 102.5), (102.5, 107.5), (105.0, 112.5)]);
315 }
316}