1use crate::SymInfo;
4
5#[derive(Debug, Clone, Default)]
7pub struct Bar {
8 pub open: f64,
9 pub high: f64,
10 pub low: f64,
11 pub close: f64,
12 pub volume: f64,
13 pub index: u64,
14 pub time: i64,
17 pub is_first: bool,
20 pub is_last: bool,
22 pub is_new: bool,
24 pub is_confirmed: bool,
26 pub is_history: bool,
28 pub is_realtime: bool,
30 pub is_last_confirmed_history: bool,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq)]
36pub struct Ohlcv {
37 pub time: i64,
39 pub open: f64,
40 pub high: f64,
41 pub low: f64,
42 pub close: f64,
43 pub volume: f64,
44}
45
46#[derive(Debug, Clone, Default)]
53pub struct Data {
54 pub syminfo: SymInfo,
56 pub bars: Vec<Bar>,
58}
59
60impl Data {
61 pub fn new(bars: Vec<Bar>) -> Self {
64 Self {
65 syminfo: SymInfo::default(),
66 bars,
67 }
68 }
69
70 pub fn from_ohlcv(rows: impl IntoIterator<Item = Ohlcv>) -> Self {
76 let rows: Vec<Ohlcv> = rows.into_iter().collect();
77 let last = rows.len().saturating_sub(1);
78
79 let bars = rows
80 .into_iter()
81 .enumerate()
82 .map(|(index, row)| Bar {
83 open: row.open,
84 high: row.high,
85 low: row.low,
86 close: row.close,
87 volume: row.volume,
88 index: index as u64,
89 time: row.time,
90 is_first: index == 0,
91 is_last: index == last,
92 is_new: true,
93 is_confirmed: true,
94 is_history: true,
95 is_realtime: false,
96 is_last_confirmed_history: index == last,
97 })
98 .collect();
99
100 Self { ..Self::new(bars) }
101 }
102
103 pub fn with_syminfo(mut self, syminfo: SymInfo) -> Self {
104 self.syminfo = syminfo;
105 self
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 fn row(time: i64) -> Ohlcv {
114 Ohlcv {
115 time,
116 open: 1.0,
117 high: 2.0,
118 low: 0.5,
119 close: 1.5,
120 volume: 10.0,
121 }
122 }
123
124 #[test]
125 fn flags_mark_the_ends_of_the_series() {
126 let data = Data::from_ohlcv([row(0), row(1), row(2)]);
127
128 assert_eq!(data.bars.len(), 3);
129 assert!(data.bars[0].is_first && !data.bars[0].is_last);
130 assert!(!data.bars[1].is_first && !data.bars[1].is_last);
131 assert!(!data.bars[2].is_first && data.bars[2].is_last);
132 assert_eq!(data.bars[2].index, 2);
133 assert!(data
134 .bars
135 .iter()
136 .all(|bar| bar.is_history && bar.is_confirmed));
137 }
138
139 #[test]
140 fn a_single_bar_is_both_ends() {
141 let data = Data::from_ohlcv([row(0)]);
142 assert!(data.bars[0].is_first && data.bars[0].is_last);
143 }
144
145 #[test]
146 fn an_empty_series_has_no_bars() {
147 assert!(Data::from_ohlcv([]).bars.is_empty());
148 }
149}