diff --git a/crates/fidc-core/src/data.rs b/crates/fidc-core/src/data.rs index 65dbcd2..167cd77 100644 --- a/crates/fidc-core/src/data.rs +++ b/crates/fidc-core/src/data.rs @@ -2325,6 +2325,20 @@ impl DataSet { rows_by_symbol.into_values().map(|rows| rows.len()).sum() } + pub fn release_execution_quotes_on_date(&mut self, date: NaiveDate) -> usize { + let row_count = self + .execution_quotes_by_date + .get(&date) + .map(|rows_by_symbol| rows_by_symbol.values().map(Vec::len).sum()) + .unwrap_or(0); + // Run data shares this immutable map with the prepared-data cache. Arc::make_mut here + // would clone every date just to remove one entry and would not release the cached base. + if row_count == 0 || Arc::strong_count(&self.execution_quotes_by_date) > 1 { + return row_count; + } + self.remove_execution_quotes_on_date(date) + } + pub fn snapshot_components(&self) -> DataSetSnapshotComponents { let mut instruments = self.instruments.values().cloned().collect::>(); instruments.sort_by(|left, right| left.symbol.cmp(&right.symbol)); @@ -5705,6 +5719,54 @@ mod tests { assert_eq!(run_data.execution_quote_count(), 0); } + #[test] + fn shared_execution_quote_release_does_not_clone_the_base_map() { + let date = NaiveDate::parse_from_str("2025-01-02", "%Y-%m-%d").unwrap(); + let quote = IntradayExecutionQuote { + date, + timestamp: NaiveDateTime::parse_from_str( + "2025-01-02 10:18:00", + "%Y-%m-%d %H:%M:%S", + ) + .unwrap(), + symbol: "000001.SZ".to_string(), + last_price: 10.0, + bid1: 10.0, + ask1: 10.0, + bid1_volume: 10_000, + ask1_volume: 10_000, + volume_delta: 10_000, + amount_delta: 100_000.0, + trading_phase: Some("continuous".to_string()), + }; + let data = DataSet::from_components_with_actions_and_quotes( + Vec::new(), + vec![market_row("2025-01-02", 10.0, 1_000_000)], + Vec::new(), + Vec::new(), + vec![benchmark_row("2025-01-02", 12.0)], + Vec::new(), + vec![quote], + ) + .unwrap(); + let mut run_data = data.clone(); + + assert!(Arc::ptr_eq( + &data.execution_quotes_by_date, + &run_data.execution_quotes_by_date + )); + assert_eq!(run_data.release_execution_quotes_on_date(date), 1); + assert!(Arc::ptr_eq( + &data.execution_quotes_by_date, + &run_data.execution_quotes_by_date + )); + assert_eq!(run_data.execution_quote_count(), 1); + + drop(data); + assert_eq!(run_data.release_execution_quotes_on_date(date), 1); + assert_eq!(run_data.execution_quote_count(), 0); + } + #[test] fn baseline_selection_uses_structured_instrument_dates_and_status_only() { let date = NaiveDate::parse_from_str("2025-01-02", "%Y-%m-%d").unwrap(); diff --git a/crates/fidc-core/src/engine.rs b/crates/fidc-core/src/engine.rs index 5ea1c5a..3a6e6c5 100644 --- a/crates/fidc-core/src/engine.rs +++ b/crates/fidc-core/src/engine.rs @@ -3053,7 +3053,7 @@ where drop(minute_group); drop(minute_quotes); drop(quote_data); - self.data.remove_execution_quotes_on_date(execution_date); + self.data.release_execution_quotes_on_date(execution_date); } portfolio.update_prices_with_options(