Construct standard bars (tick/volume/dollar) from a stream of trades using a static threshold. This mirrors the mlfinlab behavior of emitting a bar whenever the chosen metric crosses the threshold and starting accumulation fresh afterward. Any trailing partial bar that does not satisfy the threshold is dropped.
(
trades: &[Trade],
threshold: f64,
bar_type: StandardBarType,
)
| 48 | /// crosses the threshold and starting accumulation fresh afterward. Any trailing |
| 49 | /// partial bar that does not satisfy the threshold is dropped. |
| 50 | pub fn standard_bars( |
| 51 | trades: &[Trade], |
| 52 | threshold: f64, |
| 53 | bar_type: StandardBarType, |
| 54 | ) -> Vec<StandardBar> { |
| 55 | assert!(threshold.is_sign_positive(), "threshold must be positive"); |
| 56 | |
| 57 | let mut bars = Vec::new(); |
| 58 | if trades.is_empty() { |
| 59 | return bars; |
| 60 | } |
| 61 | |
| 62 | let mut start_idx = 0; |
| 63 | let mut tick_count = 0usize; |
| 64 | let mut volume = 0.0; |
| 65 | let mut dollar_value = 0.0; |
| 66 | |
| 67 | for (i, trade) in trades.iter().enumerate() { |
| 68 | tick_count += 1; |
| 69 | volume += trade.volume; |
| 70 | dollar_value += trade.price * trade.volume; |
| 71 | |
| 72 | let reached = match bar_type { |
| 73 | StandardBarType::Tick => (tick_count as f64) >= threshold, |
| 74 | StandardBarType::Volume => volume >= threshold, |
| 75 | StandardBarType::Dollar => dollar_value >= threshold, |
| 76 | }; |
| 77 | |
| 78 | if reached { |
| 79 | bars.push(build_bar(&trades[start_idx..=i])); |
| 80 | start_idx = i + 1; |
| 81 | tick_count = 0; |
| 82 | volume = 0.0; |
| 83 | dollar_value = 0.0; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | bars |
| 88 | } |
| 89 | |
| 90 | /// Construct time bars using a fixed interval. The interval applies from the start |
| 91 | /// timestamp of the current bar; the trade that crosses the interval boundary is |