严格按实际委托时间选择盘后撮合

This commit is contained in:
boris
2026-08-28 03:17:21 +08:00
parent 1a2e247c8d
commit 3f67ee9134
2 changed files with 59 additions and 16 deletions
+49 -13
View File
@@ -556,24 +556,23 @@ impl<C, R> BrokerSimulator<C, R> {
.or(self.intraday_execution_start_time) .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<NaiveDate>,
submission_time: Option<NaiveTime>,
) -> EquityExecutionPhase {
let effective_date = NaiveDate::from_ymd_opt(2026, 7, 6).expect("valid effective date"); 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_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 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 if date >= effective_date
&& submitted_same_day && order_created_date == Some(date)
&& !matches!( && !matches!(
self.matching_type, self.matching_type,
MatchingType::OpenAuction | MatchingType::NextBarOpen 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 EquityExecutionPhase::PostCloseFixedPrice
} else { } else {
@@ -581,6 +580,14 @@ impl<C, R> BrokerSimulator<C, R> {
} }
} }
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 { fn is_post_close_fixed_price(&self, date: NaiveDate) -> bool {
self.execution_phase(date) == EquityExecutionPhase::PostCloseFixedPrice self.execution_phase(date) == EquityExecutionPhase::PostCloseFixedPrice
} }
@@ -597,23 +604,44 @@ impl<C, R> BrokerSimulator<C, R> {
&self, &self,
date: NaiveDate, date: NaiveDate,
) -> Option<(NaiveDateTime, NaiveDateTime)> { ) -> 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))) .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, &self,
date: NaiveDate, date: NaiveDate,
order_created_date: Option<NaiveDate>,
submission_time: Option<NaiveTime>,
) -> Option<(NaiveTime, NaiveTime)> { ) -> 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; return None;
} }
let matching_start = NaiveTime::from_hms_opt(15, 5, 0).expect("valid matching start"); 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 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)) 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<NaiveTime>,
) -> Option<(NaiveTime, NaiveTime)> {
self.post_close_execution_quote_window_for_submission(
execution_date,
Some(order_created_date),
submission_time,
)
}
fn effective_remainder_policy( fn effective_remainder_policy(
&self, &self,
date: NaiveDate, date: NaiveDate,
@@ -7883,6 +7911,14 @@ mod tests {
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
.with_matching_type(MatchingType::CurrentBarClose) .with_matching_type(MatchingType::CurrentBarClose)
.with_slippage_model(SlippageModel::PriceRatio(0.25)); .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)); broker.runtime_order_created_date.set(Some(date));
let mut snapshot = dated_limit_test_snapshot(date); let mut snapshot = dated_limit_test_snapshot(date);
snapshot.close = 10.0; snapshot.close = 10.0;
+10 -3
View File
@@ -629,6 +629,7 @@ where
fn ensure_execution_quotes_for_decision( fn ensure_execution_quotes_for_decision(
&mut self, &mut self,
execution_date: NaiveDate, execution_date: NaiveDate,
order_created_date: NaiveDate,
portfolio: &PortfolioState, portfolio: &PortfolioState,
open_orders: &[OpenOrderView], open_orders: &[OpenOrderView],
decision: &StrategyDecision, decision: &StrategyDecision,
@@ -638,9 +639,12 @@ where
if self.execution_quote_loader.is_none() { if self.execution_quote_loader.is_none() {
return Ok(()); return Ok(());
} }
let post_close_window = self let submission_time = start_time.or_else(|| self.broker.intraday_execution_start_time());
.broker let post_close_window = self.broker.post_close_execution_quote_window_for_order(
.post_close_execution_quote_window(execution_date); execution_date,
order_created_date,
submission_time,
);
if self.broker.execution_price_field() != PriceField::Last if self.broker.execution_price_field() != PriceField::Last
&& !decision_has_algo_execution(decision) && !decision_has_algo_execution(decision)
&& post_close_window.is_none() && post_close_window.is_none()
@@ -2401,6 +2405,7 @@ where
let pre_auction_execution_orders = self.open_order_views(); let pre_auction_execution_orders = self.open_order_views();
self.ensure_execution_quotes_for_decision( self.ensure_execution_quotes_for_decision(
execution_date, execution_date,
decision_date,
&portfolio, &portfolio,
&pre_auction_execution_orders, &pre_auction_execution_orders,
&auction_decision, &auction_decision,
@@ -2648,6 +2653,7 @@ where
let pre_intraday_execution_orders = self.open_order_views(); let pre_intraday_execution_orders = self.open_order_views();
self.ensure_execution_quotes_for_decision( self.ensure_execution_quotes_for_decision(
execution_date, execution_date,
decision_date,
&portfolio, &portfolio,
&pre_intraday_execution_orders, &pre_intraday_execution_orders,
&decision, &decision,
@@ -2906,6 +2912,7 @@ where
let pre_minute_execution_orders = self.open_order_views(); let pre_minute_execution_orders = self.open_order_views();
self.ensure_execution_quotes_for_decision( self.ensure_execution_quotes_for_decision(
execution_date, execution_date,
decision_date,
&portfolio, &portfolio,
&pre_minute_execution_orders, &pre_minute_execution_orders,
&minute_decision, &minute_decision,