From e677a73f95efd6c1fc7289652b05a1ecfc126f2f Mon Sep 17 00:00:00 2001 From: boris Date: Fri, 28 Aug 2026 08:38:22 +0800 Subject: [PATCH] perf(core): specialize market candidate snapshot lookup --- crates/fidc-core/src/data.rs | 55 +++++++++++++++++++ .../fidc-core/src/platform_expr_strategy.rs | 4 +- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/fidc-core/src/data.rs b/crates/fidc-core/src/data.rs index 4725d40..c550005 100644 --- a/crates/fidc-core/src/data.rs +++ b/crates/fidc-core/src/data.rs @@ -1246,6 +1246,12 @@ pub(crate) struct SymbolSnapshotRefs<'a> { pub candidate: Option<&'a CandidateEligibility>, } +#[derive(Debug, Clone, Copy)] +pub(crate) struct MarketCandidateSnapshotRefs<'a> { + pub market: Option<&'a DailyMarketSnapshot>, + pub candidate: Option<&'a CandidateEligibility>, +} + impl DataSet { pub fn with_additional_trading_dates( mut self, @@ -1711,6 +1717,40 @@ impl DataSet { } } + pub(crate) fn market_candidate_snapshots_by_id( + &self, + date: NaiveDate, + symbol_id: u32, + ) -> MarketCandidateSnapshotRefs<'_> { + 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 = 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 candidate = self + .candidate_by_date + .get(&date) + .map(Vec::as_slice) + .zip( + self.candidate_symbol_ids_by_date + .get(&date) + .map(Vec::as_slice), + ) + .and_then(|(rows, symbol_ids)| { + find_by_symbol_id_with_preferred_index( + rows, + symbol_ids, + symbol_id, + market_index, + ) + }); + MarketCandidateSnapshotRefs { market, candidate } + } + pub fn benchmark(&self, date: NaiveDate) -> Option<&BenchmarkSnapshot> { self.benchmark_by_date.get(&date) } @@ -4108,11 +4148,16 @@ 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); + let market_candidate = data.market_candidate_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()) ); + assert_eq!( + market_candidate.market.map(|row| row.symbol.as_str()), + combined.market.map(|row| row.symbol.as_str()) + ); assert_eq!( combined.factor.map(|row| row.symbol.as_str()), data.factor_by_symbol_id(date, symbol_id) @@ -4123,6 +4168,10 @@ mod tests { data.candidate_by_symbol_id(date, symbol_id) .map(|row| row.symbol.as_str()) ); + assert_eq!( + market_candidate.candidate.map(|row| row.symbol.as_str()), + combined.candidate.map(|row| row.symbol.as_str()) + ); } let signal_id = data.symbol_id("000300.SH").unwrap(); @@ -4130,6 +4179,12 @@ mod tests { assert_eq!(signal.market.map(|row| row.symbol.as_str()), Some("000300.SH")); assert!(signal.factor.is_none()); assert!(signal.candidate.is_none()); + let signal_market_candidate = data.market_candidate_snapshots_by_id(date, signal_id); + assert_eq!( + signal_market_candidate.market.map(|row| row.symbol.as_str()), + Some("000300.SH") + ); + assert!(signal_market_candidate.candidate.is_none()); } #[test] diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 0665c17..d0a5694 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -8889,7 +8889,9 @@ impl PlatformExprStrategy { if ctx.has_dynamic_universe() && !ctx.dynamic_universe_contains(&factor.symbol) { continue; } - let snapshots = ctx.data.symbol_snapshots_by_id(date, symbol_id); + let snapshots = ctx + .data + .market_candidate_snapshots_by_id(date, symbol_id); let synthetic_candidate; let candidate = if let Some(candidate) = snapshots.candidate { candidate