严格按实际委托时间选择盘后撮合
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user