sdb_core/postprocessing/candle/
dollar_bars.rs1use super::{Candle, Sampler};
2use crate::dtf::update::Update;
3
4pub struct DollarSampler {
6 interval: f32,
7 elapsed: f32,
8}
9
10impl DollarSampler {
11 pub fn new(interval: f32) -> Self {
13 Self {
14 elapsed: 0.,
15 interval,
16 }
17 }
18}
19
20impl Sampler for DollarSampler {
21 fn reset(&mut self) {
22 self.elapsed = 0.;
23 }
24
25 fn is_sample(&mut self, trade: &Update) -> bool {
26 self.elapsed += trade.price * trade.size;
27
28 if self.elapsed > self.interval {
29 self.elapsed = 0.;
30 true
31 } else {
32 false
33 }
34 }
35}
36
37pub struct DollarBarsIter<I:Iterator<Item=Update>> {
39 it: I,
40 current_candle: Option<Candle>,
41 sampler: DollarSampler,
42}
43
44impl<I:Iterator<Item=Update>> DollarBarsIter<I> {
45 pub fn new(it: I, dollar_interval: f32) -> Self {
47 Self {
48 it,
49 current_candle: None,
50 sampler: DollarSampler::new(dollar_interval),
51 }
52 }
53}
54
55fn new_candle(trade: Update) -> Candle {
56 Candle {
57 start: trade.ts,
58 end: trade.ts,
59 volume: trade.size,
60 high: trade.price,
61 low: trade.price,
62 close: trade.price,
63 open: trade.price,
64 }
65}
66
67impl<I:Iterator<Item=Update>> Iterator for DollarBarsIter<I> {
68 type Item = Candle;
69 fn next(&mut self) -> Option<Self::Item> {
70 while let Some(trade) = self.it.next() {
71 if !trade.is_trade {
72 continue;
73 }
74
75 if let Some(c) = self.current_candle {
76 if self.sampler.is_sample(&trade) {
77 self.current_candle = Some(new_candle(trade));
78 return Some(c);
79 };
80 }
81
82 self.current_candle = if let Some(c) = self.current_candle {
83 Some(Candle {
84 start: c.start,
85 end: trade.ts,
86 volume: c.volume + trade.size,
87 high: trade.price.max(c.high),
88 low: trade.price.min(c.low),
89 close: trade.price,
90 open: c.open,
91 })
92 } else {
93 Some(new_candle(trade))
94 };
95 }
96 if let Some(x) = self.current_candle {
97 self.current_candle = None;
98 return Some(x)
99 } else {
100 None
101 }
102 }
103}
104
105
106#[derive(Clone, Debug, PartialEq)]
107pub struct DollarBars {
109 v: Vec<Candle>,
110}
111
112impl DollarBars {
113
114 pub fn from_updates(ups: &[Update], dollar_interval: f32) -> DollarBars {
116 let v = DollarBarsIter::new(ups.iter().copied(), dollar_interval).collect();
117 DollarBars { v }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use std::f32;
125 #[test]
126 fn test_dollar_bar() {
127 let trades = (0..10).map(|i| Update {
128 is_trade: true,
129 is_bid: true,
130 price: i as f32,
131 size: f32::abs(i as f32),
132 ts: i,
133 seq: 0,
134 })
135 .collect::<Vec<_>>();
136
137 let ret = DollarBars::from_updates(&trades, 100.);
138
139 assert_eq!(DollarBars {v: vec![Candle {
140 start: 0,
141 end: 6,
142 open: 0.0,
143 high: 6.0,
144 low: 0.0,
145 close: 6.0,
146 volume: 21.0,
147 }, Candle {
148 start: 7,
149 end: 8,
150 open: 7.0,
151 high: 8.0,
152 low: 7.0,
153 close: 8.0,
154 volume: 15.0,
155 }, Candle {
156 start: 9,
157 end: 9,
158 open: 9.0,
159 high: 9.0,
160 low: 9.0,
161 close: 9.0,
162 volume: 9.0,
163 }]}, ret);
164 }
165}