Add futures depth matching and mod loader
This commit is contained in:
@@ -261,6 +261,43 @@ pub struct IntradayExecutionQuote {
|
||||
pub trading_phase: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct IntradayOrderBookDepthLevel {
|
||||
#[serde(with = "date_format")]
|
||||
pub date: NaiveDate,
|
||||
pub symbol: String,
|
||||
#[serde(with = "datetime_format")]
|
||||
pub timestamp: NaiveDateTime,
|
||||
pub level: u8,
|
||||
pub bid_price: f64,
|
||||
pub bid_volume: u64,
|
||||
pub ask_price: f64,
|
||||
pub ask_volume: u64,
|
||||
}
|
||||
|
||||
impl IntradayOrderBookDepthLevel {
|
||||
pub fn executable_price(&self, side: crate::events::OrderSide) -> Option<f64> {
|
||||
match side {
|
||||
crate::events::OrderSide::Buy if self.ask_price.is_finite() && self.ask_price > 0.0 => {
|
||||
Some(self.ask_price)
|
||||
}
|
||||
crate::events::OrderSide::Sell
|
||||
if self.bid_price.is_finite() && self.bid_price > 0.0 =>
|
||||
{
|
||||
Some(self.bid_price)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn executable_volume(&self, side: crate::events::OrderSide) -> u64 {
|
||||
match side {
|
||||
crate::events::OrderSide::Buy => self.ask_volume,
|
||||
crate::events::OrderSide::Sell => self.bid_volume,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntradayExecutionQuote {
|
||||
pub fn buy_price(&self) -> Option<f64> {
|
||||
if self.ask1.is_finite() && self.ask1 > 0.0 {
|
||||
@@ -661,6 +698,7 @@ pub struct DataSet {
|
||||
candidate_index: HashMap<(NaiveDate, String), CandidateEligibility>,
|
||||
corporate_actions_by_date: BTreeMap<NaiveDate, Vec<CorporateAction>>,
|
||||
execution_quotes_index: HashMap<(NaiveDate, String), Vec<IntradayExecutionQuote>>,
|
||||
order_book_depth_index: HashMap<(NaiveDate, String), Vec<IntradayOrderBookDepthLevel>>,
|
||||
benchmark_by_date: BTreeMap<NaiveDate, BenchmarkSnapshot>,
|
||||
market_series_by_symbol: HashMap<String, SymbolPriceSeries>,
|
||||
benchmark_series_cache: BenchmarkPriceSeries,
|
||||
@@ -694,7 +732,13 @@ impl DataSet {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Self::from_components_with_actions_quotes_and_futures(
|
||||
let order_book_depth_path = path.join("order_book_depth.csv");
|
||||
let order_book_depth = if order_book_depth_path.exists() {
|
||||
read_order_book_depth(&order_book_depth_path)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Self::from_components_with_actions_quotes_futures_and_depth(
|
||||
instruments,
|
||||
market,
|
||||
factors,
|
||||
@@ -703,6 +747,7 @@ impl DataSet {
|
||||
corporate_actions,
|
||||
execution_quotes,
|
||||
futures_params,
|
||||
order_book_depth,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -730,7 +775,13 @@ impl DataSet {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Self::from_components_with_actions_quotes_and_futures(
|
||||
let order_book_depth_dir = path.join("order_book_depth");
|
||||
let order_book_depth = if order_book_depth_dir.exists() {
|
||||
read_partitioned_dir(&order_book_depth_dir, read_order_book_depth)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Self::from_components_with_actions_quotes_futures_and_depth(
|
||||
instruments,
|
||||
market,
|
||||
factors,
|
||||
@@ -739,6 +790,7 @@ impl DataSet {
|
||||
corporate_actions,
|
||||
execution_quotes,
|
||||
futures_params,
|
||||
order_book_depth,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -809,6 +861,30 @@ impl DataSet {
|
||||
corporate_actions: Vec<CorporateAction>,
|
||||
execution_quotes: Vec<IntradayExecutionQuote>,
|
||||
futures_params: Vec<FuturesTradingParameter>,
|
||||
) -> Result<Self, DataSetError> {
|
||||
Self::from_components_with_actions_quotes_futures_and_depth(
|
||||
instruments,
|
||||
market,
|
||||
factors,
|
||||
candidates,
|
||||
benchmarks,
|
||||
corporate_actions,
|
||||
execution_quotes,
|
||||
futures_params,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_components_with_actions_quotes_futures_and_depth(
|
||||
instruments: Vec<Instrument>,
|
||||
market: Vec<DailyMarketSnapshot>,
|
||||
factors: Vec<DailyFactorSnapshot>,
|
||||
candidates: Vec<CandidateEligibility>,
|
||||
benchmarks: Vec<BenchmarkSnapshot>,
|
||||
corporate_actions: Vec<CorporateAction>,
|
||||
execution_quotes: Vec<IntradayExecutionQuote>,
|
||||
futures_params: Vec<FuturesTradingParameter>,
|
||||
order_book_depth: Vec<IntradayOrderBookDepthLevel>,
|
||||
) -> Result<Self, DataSetError> {
|
||||
let benchmark_code = collect_benchmark_code(&benchmarks)?;
|
||||
let calendar = TradingCalendar::new(benchmarks.iter().map(|item| item.date).collect());
|
||||
@@ -837,6 +913,7 @@ impl DataSet {
|
||||
.collect::<HashMap<_, _>>();
|
||||
let corporate_actions_by_date = group_by_date(corporate_actions, |item| item.date);
|
||||
let execution_quotes_index = build_execution_quote_index(execution_quotes);
|
||||
let order_book_depth_index = build_order_book_depth_index(order_book_depth);
|
||||
|
||||
let benchmark_by_date = benchmarks
|
||||
.into_iter()
|
||||
@@ -860,6 +937,7 @@ impl DataSet {
|
||||
candidate_index,
|
||||
corporate_actions_by_date,
|
||||
execution_quotes_index,
|
||||
order_book_depth_index,
|
||||
benchmark_by_date,
|
||||
market_series_by_symbol,
|
||||
benchmark_series_cache,
|
||||
@@ -936,6 +1014,17 @@ impl DataSet {
|
||||
.unwrap_or(&[])
|
||||
}
|
||||
|
||||
pub fn order_book_depth_on(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
) -> &[IntradayOrderBookDepthLevel] {
|
||||
self.order_book_depth_index
|
||||
.get(&(date, symbol.to_string()))
|
||||
.map(Vec::as_slice)
|
||||
.unwrap_or(&[])
|
||||
}
|
||||
|
||||
pub fn execution_quotes_on_date(&self, date: NaiveDate) -> Vec<IntradayExecutionQuote> {
|
||||
let mut quotes = self
|
||||
.execution_quotes_index
|
||||
@@ -1978,6 +2067,27 @@ fn read_execution_quotes(path: &Path) -> Result<Vec<IntradayExecutionQuote>, Dat
|
||||
Ok(quotes)
|
||||
}
|
||||
|
||||
fn read_order_book_depth(path: &Path) -> Result<Vec<IntradayOrderBookDepthLevel>, DataSetError> {
|
||||
let rows = read_rows(path)?;
|
||||
let mut levels = Vec::new();
|
||||
for row in rows {
|
||||
levels.push(IntradayOrderBookDepthLevel {
|
||||
date: row.parse_date(0)?,
|
||||
symbol: row.get(1)?.to_string(),
|
||||
timestamp: row.parse_datetime(2)?,
|
||||
level: row
|
||||
.parse_optional_u32(3)
|
||||
.unwrap_or(1)
|
||||
.clamp(1, u8::MAX as u32) as u8,
|
||||
bid_price: row.parse_optional_f64(4).unwrap_or_default(),
|
||||
bid_volume: row.parse_optional_u64(5).unwrap_or_default(),
|
||||
ask_price: row.parse_optional_f64(6).unwrap_or_default(),
|
||||
ask_volume: row.parse_optional_u64(7).unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
Ok(levels)
|
||||
}
|
||||
|
||||
fn read_futures_trading_parameters(
|
||||
path: &Path,
|
||||
) -> Result<Vec<FuturesTradingParameter>, DataSetError> {
|
||||
@@ -2329,6 +2439,28 @@ fn build_execution_quote_index(
|
||||
grouped
|
||||
}
|
||||
|
||||
fn build_order_book_depth_index(
|
||||
order_book_depth: Vec<IntradayOrderBookDepthLevel>,
|
||||
) -> HashMap<(NaiveDate, String), Vec<IntradayOrderBookDepthLevel>> {
|
||||
let mut grouped = HashMap::<(NaiveDate, String), Vec<IntradayOrderBookDepthLevel>>::new();
|
||||
for level in order_book_depth {
|
||||
grouped
|
||||
.entry((level.date, level.symbol.clone()))
|
||||
.or_default()
|
||||
.push(level);
|
||||
}
|
||||
|
||||
for levels in grouped.values_mut() {
|
||||
levels.sort_by(|left, right| {
|
||||
left.timestamp
|
||||
.cmp(&right.timestamp)
|
||||
.then(left.level.cmp(&right.level))
|
||||
});
|
||||
}
|
||||
|
||||
grouped
|
||||
}
|
||||
|
||||
fn build_eligible_universe(
|
||||
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
||||
candidate_index: &HashMap<(NaiveDate, String), CandidateEligibility>,
|
||||
|
||||
Reference in New Issue
Block a user