engine/data_source/
historical.rs1use super::DataSource;
2use crate::types::{FillEvent, MarketState, Side};
3use parquet::file::reader::{FileReader, SerializedFileReader};
4use parquet::record::RowAccessor;
5use std::fs::File;
6use std::path::Path;
7
8pub struct ParquetDataSource {
9 reader: SerializedFileReader<File>,
10 current_row: usize,
11 total_rows: usize,
12 cumulative_impact: f64,
13 impact_factor: f64,
14}
15
16impl ParquetDataSource {
17 pub fn new<P: AsRef<Path>>(path: P, impact_factor: f64) -> Self {
18 let file = File::open(path).expect("Failed to open parquet file");
19 let reader = SerializedFileReader::new(file).expect("Failed to create parquet reader");
20 let total_rows = reader.metadata().file_metadata().num_rows() as usize;
21
22 Self {
23 reader,
24 current_row: 0,
25 total_rows,
26 cumulative_impact: 0.0,
27 impact_factor,
28 }
29 }
30}
31
32impl DataSource for ParquetDataSource {
33 fn next_quote(&mut self) -> Option<MarketState> {
34 if self.current_row >= self.total_rows {
35 return None;
36 }
37
38 let row_result = self.reader.get_row_iter(None).ok()?.nth(self.current_row)?;
40 let row = row_result.ok()?;
41 self.current_row += 1;
42
43 let timestamp_val = row.get_long(0).ok()?; let bid = row.get_double(1).ok()?; let ask = row.get_double(2).ok()?; let timestamp = timestamp_val as f64 / 1_000_000.0; Some(MarketState {
53 timestamp,
54 best_bid: bid + self.cumulative_impact,
55 best_ask: ask + self.cumulative_impact,
56 last_price: None,
57 true_volatility: None,
58 true_drift: None,
59 parameters: None,
60 })
61 }
62
63 fn apply_impact(&mut self, fills: &[FillEvent]) {
64 if self.impact_factor.abs() < f64::EPSILON {
65 return;
66 }
67
68 let net_quantity: f64 = fills
69 .iter()
70 .map(|f| match f.side {
71 Side::Buy => f.quantity,
72 Side::Sell => -f.quantity,
73 })
74 .sum();
75
76 self.cumulative_impact += self.impact_factor * net_quantity;
78 }
79}