From 3f67ee9134f936f39a2edfa2f90d4e8f79dca4e0 Mon Sep 17 00:00:00 2001 From: boris Date: Fri, 28 Aug 2026 03:17:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=A5=E6=A0=BC=E6=8C=89=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E5=A7=94=E6=89=98=E6=97=B6=E9=97=B4=E9=80=89=E6=8B=A9=E7=9B=98?= =?UTF-8?q?=E5=90=8E=E6=92=AE=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/fidc-core/src/broker.rs | 62 +++++++++++++++++++++++++++------- crates/fidc-core/src/engine.rs | 13 +++++-- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index 57d4739..e5e0e74 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -556,24 +556,23 @@ impl BrokerSimulator { .or(self.intraday_execution_start_time) } - fn execution_phase(&self, date: NaiveDate) -> EquityExecutionPhase { + fn execution_phase_for_submission( + &self, + date: NaiveDate, + order_created_date: Option, + submission_time: Option, + ) -> EquityExecutionPhase { let effective_date = NaiveDate::from_ymd_opt(2026, 7, 6).expect("valid effective date"); let window_start = NaiveTime::from_hms_opt(15, 0, 0).expect("valid window start"); let window_end = NaiveTime::from_hms_opt(15, 30, 0).expect("valid window end"); - let submitted_same_day = self - .runtime_order_created_date - .get() - .is_none_or(|created_date| created_date == date); if date >= effective_date - && submitted_same_day + && order_created_date == Some(date) && !matches!( self.matching_type, MatchingType::OpenAuction | MatchingType::NextBarOpen ) - && self - .submission_time() - .is_some_and(|time| time >= window_start && time <= window_end) + && submission_time.is_some_and(|time| time >= window_start && time <= window_end) { EquityExecutionPhase::PostCloseFixedPrice } else { @@ -581,6 +580,14 @@ impl BrokerSimulator { } } + fn execution_phase(&self, date: NaiveDate) -> EquityExecutionPhase { + self.execution_phase_for_submission( + date, + self.runtime_order_created_date.get(), + self.submission_time(), + ) + } + fn is_post_close_fixed_price(&self, date: NaiveDate) -> bool { self.execution_phase(date) == EquityExecutionPhase::PostCloseFixedPrice } @@ -597,23 +604,44 @@ impl BrokerSimulator { &self, date: NaiveDate, ) -> Option<(NaiveDateTime, NaiveDateTime)> { - self.post_close_execution_quote_window(date) + self.post_close_execution_quote_window_for_submission( + date, + self.runtime_order_created_date.get(), + self.submission_time(), + ) .map(|(start, end)| (date.and_time(start), date.and_time(end))) } - pub(crate) fn post_close_execution_quote_window( + fn post_close_execution_quote_window_for_submission( &self, date: NaiveDate, + order_created_date: Option, + submission_time: Option, ) -> Option<(NaiveTime, NaiveTime)> { - if !self.is_post_close_fixed_price(date) { + if self.execution_phase_for_submission(date, order_created_date, submission_time) + != EquityExecutionPhase::PostCloseFixedPrice + { return None; } let matching_start = NaiveTime::from_hms_opt(15, 5, 0).expect("valid matching start"); let matching_end = NaiveTime::from_hms_opt(15, 30, 0).expect("valid matching end"); - let submitted_at = self.submission_time()?; + let submitted_at = submission_time?; Some((submitted_at.max(matching_start), matching_end)) } + pub(crate) fn post_close_execution_quote_window_for_order( + &self, + execution_date: NaiveDate, + order_created_date: NaiveDate, + submission_time: Option, + ) -> Option<(NaiveTime, NaiveTime)> { + self.post_close_execution_quote_window_for_submission( + execution_date, + Some(order_created_date), + submission_time, + ) + } + fn effective_remainder_policy( &self, date: NaiveDate, @@ -7883,6 +7911,14 @@ mod tests { let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) .with_matching_type(MatchingType::CurrentBarClose) .with_slippage_model(SlippageModel::PriceRatio(0.25)); + broker + .runtime_intraday_start_time + .set(NaiveTime::from_hms_opt(15, 0, 0)); + assert_eq!( + broker.execution_phase(date), + EquityExecutionPhase::ContinuousAuction, + "a configured clock without an actual same-day order creation event must not select the post-close route" + ); broker.runtime_order_created_date.set(Some(date)); let mut snapshot = dated_limit_test_snapshot(date); snapshot.close = 10.0; diff --git a/crates/fidc-core/src/engine.rs b/crates/fidc-core/src/engine.rs index 300c0a6..b0fe21d 100644 --- a/crates/fidc-core/src/engine.rs +++ b/crates/fidc-core/src/engine.rs @@ -629,6 +629,7 @@ where fn ensure_execution_quotes_for_decision( &mut self, execution_date: NaiveDate, + order_created_date: NaiveDate, portfolio: &PortfolioState, open_orders: &[OpenOrderView], decision: &StrategyDecision, @@ -638,9 +639,12 @@ where if self.execution_quote_loader.is_none() { return Ok(()); } - let post_close_window = self - .broker - .post_close_execution_quote_window(execution_date); + let submission_time = start_time.or_else(|| self.broker.intraday_execution_start_time()); + let post_close_window = self.broker.post_close_execution_quote_window_for_order( + execution_date, + order_created_date, + submission_time, + ); if self.broker.execution_price_field() != PriceField::Last && !decision_has_algo_execution(decision) && post_close_window.is_none() @@ -2401,6 +2405,7 @@ where let pre_auction_execution_orders = self.open_order_views(); self.ensure_execution_quotes_for_decision( execution_date, + decision_date, &portfolio, &pre_auction_execution_orders, &auction_decision, @@ -2648,6 +2653,7 @@ where let pre_intraday_execution_orders = self.open_order_views(); self.ensure_execution_quotes_for_decision( execution_date, + decision_date, &portfolio, &pre_intraday_execution_orders, &decision, @@ -2906,6 +2912,7 @@ where let pre_minute_execution_orders = self.open_order_views(); self.ensure_execution_quotes_for_decision( execution_date, + decision_date, &portfolio, &pre_minute_execution_orders, &minute_decision,