diff --git a/crates/fidc-core/src/engine.rs b/crates/fidc-core/src/engine.rs index f1f3b1a..e781712 100644 --- a/crates/fidc-core/src/engine.rs +++ b/crates/fidc-core/src/engine.rs @@ -473,7 +473,6 @@ pub struct BacktestEngine { futures_cost_model: FuturesTransactionCostModel, futures_validation_config: FuturesValidationConfig, execution_quote_loader: Option, - preplanned_decision_quote_symbols_by_date: Option>>>, execution_quote_request_cache: BTreeSet<(NaiveDate, String, Option, Option)>, execution_absence_notes: BTreeMap>, @@ -574,7 +573,6 @@ impl BacktestEngine { futures_cost_model: FuturesTransactionCostModel::default(), futures_validation_config: FuturesValidationConfig::default(), execution_quote_loader: None, - preplanned_decision_quote_symbols_by_date: None, execution_quote_request_cache: BTreeSet::new(), execution_absence_notes: BTreeMap::new(), execution_lifecycle_reported: BTreeSet::new(), @@ -601,14 +599,6 @@ impl BacktestEngine { self } - pub fn with_preplanned_decision_quote_symbols_by_date( - mut self, - symbols_by_date: Arc>>, - ) -> Self { - self.preplanned_decision_quote_symbols_by_date = Some(symbols_by_date); - self - } - pub fn with_dividend_reinvestment(mut self, enabled: bool) -> Self { self.dividend_reinvestment = enabled; self @@ -2620,22 +2610,8 @@ where let on_day_open_orders = self.open_order_views(); let decision_quote_times = self.strategy.decision_quote_times(); if self.execution_quote_loader.is_some() && !decision_quote_times.is_empty() { - if let Some(preplanned) = self - .preplanned_decision_quote_symbols_by_date - .as_ref() - .map(Arc::clone) - { - let empty_symbols = BTreeSet::new(); - let decision_quote_symbols = - preplanned.get(&execution_date).unwrap_or(&empty_symbols); - self.ensure_execution_quotes_for_symbols_at_times( - execution_date, - decision_quote_symbols, - &decision_quote_times, - )?; - } else { - let decision_quote_symbols = - self.strategy.decision_quote_symbols(&StrategyContext { + let decision_quote_symbols = + self.strategy.decision_quote_symbols(&StrategyContext { execution_date, decision_date, decision_index, @@ -2653,13 +2629,12 @@ where ), order_events: result.order_events.as_slice(), fills: result.fills.as_slice(), - })?; - self.ensure_execution_quotes_for_symbols_at_times( - execution_date, - &decision_quote_symbols, - &decision_quote_times, - )?; - } + })?; + self.ensure_execution_quotes_for_symbols_at_times( + execution_date, + &decision_quote_symbols, + &decision_quote_times, + )?; } self.ensure_execution_quotes_for_portfolio_times( execution_date, diff --git a/crates/fidc-core/tests/decision_quote_preload.rs b/crates/fidc-core/tests/decision_quote_preload.rs index 5b7c8bf..d39f15b 100644 --- a/crates/fidc-core/tests/decision_quote_preload.rs +++ b/crates/fidc-core/tests/decision_quote_preload.rs @@ -5,7 +5,7 @@ use fidc_core::{ Instrument, IntradayExecutionQuote, MatchingType, OrderIntent, PriceField, Strategy, StrategyContext, StrategyDecision, }; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; use std::sync::{Arc, Mutex}; fn d(year: i32, month: u32, day: u32) -> NaiveDate { @@ -163,7 +163,7 @@ fn single_day_quote_plan_data(date: NaiveDate) -> DataSet { } #[test] -fn runtime_account_dependent_quote_scope_cannot_be_replaced_by_an_empty_preplan() { +fn runtime_account_dependent_quote_scope_uses_the_actual_account() { struct AccountDependentQuoteReader; impl Strategy for AccountDependentQuoteReader { fn name(&self) -> &str { "account_dependent_quote_reader" } @@ -196,13 +196,12 @@ fn runtime_account_dependent_quote_scope_cannot_be_replaced_by_an_empty_preplan( timestamp:request.date.and_time(t(10,17,59)), 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".into()), - }).collect())) - .with_preplanned_decision_quote_symbols_by_date(Arc::new(BTreeMap::new())); + }).collect())); engine.run().expect("account-dependent quote planning"); } #[test] -fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() { +fn engine_resolves_the_runtime_strategy_scope_when_a_loader_exists() { let date = d(2026, 1, 5); let data = single_day_quote_plan_data(date); let broker = BrokerSimulator::new_with_execution_price( @@ -225,10 +224,6 @@ fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() { symbol_plan_calls: Arc::clone(&symbol_plan_calls), }; let captured_loader_calls = Arc::clone(&loader_calls); - let preplanned = Arc::new(BTreeMap::from([( - date, - BTreeSet::from(["000001.SZ".to_string()]), - )])); let mut engine = BacktestEngine::new(data, strategy, broker, config) .with_execution_quote_loader(move |request| { *captured_loader_calls.lock().expect("loader counter mutex") += 1; @@ -249,20 +244,19 @@ fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() { trading_phase: Some("continuous".to_string()), }) .collect()) - }) - .with_preplanned_decision_quote_symbols_by_date(preplanned); + }); engine.run().expect("backtest should run"); assert_eq!( *symbol_plan_calls.lock().expect("symbol plan counter mutex"), - 0, - "the strategy plan must not be recomputed after a complete plan is supplied" + 1, + "quote planning must use the actual run context" ); assert_eq!( *loader_calls.lock().expect("loader counter mutex"), - 1, - "the supplied symbols must still pass through the normal quote loader" + 0, + "an empty runtime scope must not fetch unrequested symbols" ); }