diff --git a/crates/fidc-core/src/data.rs b/crates/fidc-core/src/data.rs index 9ae5455..69f10de 100644 --- a/crates/fidc-core/src/data.rs +++ b/crates/fidc-core/src/data.rs @@ -1335,13 +1335,6 @@ pub struct DataSet { futures_params_by_symbol: Arc>>, } -#[derive(Debug, Clone, Copy)] -pub(crate) struct SymbolSnapshotRefs<'a> { - pub market: Option<&'a DailyMarketSnapshot>, - pub factor: Option<&'a DailyFactorSnapshot>, - pub candidate: Option<&'a CandidateEligibility>, -} - #[derive(Debug, Clone, Copy)] pub(crate) struct StandardRollingMeans { pub close: [Option; 7], @@ -1963,68 +1956,6 @@ impl DataSet { ) } - pub(crate) fn symbol_snapshots_by_id( - &self, - date: NaiveDate, - symbol_id: u32, - ) -> SymbolSnapshotRefs<'_> { - let market_rows = self.market_by_date.get(&date).map(Vec::as_slice); - let market_symbol_ids = self - .market_symbol_ids_by_date - .get(&date) - .map(Vec::as_slice); - let market_index = dense_row_position( - &self.market_row_positions_by_date, - date, - symbol_id, - ) - .or_else(|| { - market_rows - .zip(market_symbol_ids) - .and_then(|(rows, symbol_ids)| symbol_id_index(rows.len(), symbol_ids, symbol_id)) - }); - let market = market_index.and_then(|index| market_rows?.get(index)); - - let factor = self.factor_by_date.get(&date).and_then(|rows| { - dense_row_position(&self.factor_row_positions_by_date, date, symbol_id) - .and_then(|index| rows.get(index)) - .or_else(|| { - self.factor_symbol_ids_by_date - .get(&date) - .and_then(|symbol_ids| { - find_by_symbol_id_with_preferred_index( - rows, - symbol_ids, - symbol_id, - market_index, - ) - }) - }) - }); - let candidate = self.candidate_by_date.get(&date).and_then(|rows| { - dense_row_position(&self.candidate_row_positions_by_date, date, symbol_id) - .and_then(|index| rows.get(index)) - .or_else(|| { - self.candidate_symbol_ids_by_date - .get(&date) - .and_then(|symbol_ids| { - find_by_symbol_id_with_preferred_index( - rows, - symbol_ids, - symbol_id, - market_index, - ) - }) - }) - }); - - SymbolSnapshotRefs { - market, - factor, - candidate, - } - } - pub(crate) fn market_standard_rolling_means_by_symbol_id( &self, date: NaiveDate, @@ -4155,13 +4086,6 @@ fn find_by_symbol_id<'a, T>(rows: &'a [T], symbol_ids: &[u32], symbol_id: u32) - find_by_symbol_id_with_preferred_index(rows, symbol_ids, symbol_id, None) } -fn symbol_id_index(rows_len: usize, symbol_ids: &[u32], symbol_id: u32) -> Option { - if rows_len != symbol_ids.len() { - return None; - } - symbol_ids.binary_search(&symbol_id).ok() -} - fn find_by_symbol_id_with_preferred_index<'a, T>( rows: &'a [T], symbol_ids: &[u32], @@ -4786,7 +4710,7 @@ mod tests { } #[test] - fn combined_symbol_snapshot_lookup_uses_alignment_and_falls_back_for_sparse_rows() { + fn direct_symbol_id_snapshot_lookups_preserve_alignment_for_sparse_rows() { let date = NaiveDate::parse_from_str("2025-01-02", "%Y-%m-%d").unwrap(); let instrument = |symbol: &str| Instrument { symbol: symbol.to_string(), @@ -4848,29 +4772,30 @@ mod tests { for symbol in ["000001.SZ", "600000.SH"] { let symbol_id = data.symbol_id(symbol).unwrap(); - let combined = data.symbol_snapshots_by_id(date, symbol_id); assert_eq!( - combined.market.map(|row| row.symbol.as_str()), data.market_by_symbol_id(date, symbol_id) - .map(|row| row.symbol.as_str()) + .map(|row| row.symbol.as_str()), + Some(symbol) ); assert_eq!( - combined.factor.map(|row| row.symbol.as_str()), data.factor_by_symbol_id(date, symbol_id) - .map(|row| row.symbol.as_str()) + .map(|row| row.symbol.as_str()), + Some(symbol) ); assert_eq!( - combined.candidate.map(|row| row.symbol.as_str()), data.candidate_by_symbol_id(date, symbol_id) - .map(|row| row.symbol.as_str()) + .map(|row| row.symbol.as_str()), + Some(symbol) ); } let signal_id = data.symbol_id("000300.SH").unwrap(); - let signal = data.symbol_snapshots_by_id(date, signal_id); - assert_eq!(signal.market.map(|row| row.symbol.as_str()), Some("000300.SH")); - assert!(signal.factor.is_none()); - assert!(signal.candidate.is_none()); + assert_eq!( + data.market_by_symbol_id(date, signal_id).map(|row| row.symbol.as_str()), + Some("000300.SH") + ); + assert!(data.factor_by_symbol_id(date, signal_id).is_none()); + assert!(data.candidate_by_symbol_id(date, signal_id).is_none()); } #[test] diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 7eb9739..20fcabf 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -3900,28 +3900,28 @@ impl PlatformExprStrategy { return Ok(Arc::clone(state)); } - let execution_snapshots = ctx.data.symbol_snapshots_by_id(date, symbol_id); - let market = execution_snapshots.market.ok_or_else(|| { + let market = ctx.data.market_by_symbol_id(date, symbol_id).ok_or_else(|| { BacktestError::Data(crate::data::DataSetError::MissingSnapshot { kind: "market", date, symbol: symbol.to_string(), }) })?; - let candidate = execution_snapshots.candidate.ok_or_else(|| { + let candidate = ctx.data.candidate_by_symbol_id(date, symbol_id).ok_or_else(|| { BacktestError::Data(crate::data::DataSetError::MissingSnapshot { kind: "candidate", date, symbol: symbol.to_string(), }) })?; - let factor_snapshots = if factor_date == date { - execution_snapshots + let feature_market = if factor_date == date { + market } else { - ctx.data.symbol_snapshots_by_id(factor_date, symbol_id) + ctx.data + .market_by_symbol_id(factor_date, symbol_id) + .unwrap_or(market) }; - let feature_market = factor_snapshots.market.unwrap_or(market); - let factor = factor_snapshots.factor.ok_or_else(|| { + let factor = ctx.data.factor_by_symbol_id(factor_date, symbol_id).ok_or_else(|| { BacktestError::Data(crate::data::DataSetError::MissingSnapshot { kind: "factor", date: factor_date,