修复股票池卖单回报后未继续执行买入阶段
This commit is contained in:
@@ -427,6 +427,8 @@ pub struct BrokerSimulator<C, R> {
|
||||
verified_etf_minute_absences: RefCell<BTreeSet<(NaiveDate, String)>>,
|
||||
runtime_etf_daily_open: Cell<bool>,
|
||||
deferred_etf_targets: RefCell<crate::etf_execution::DeferredEtfTargets>,
|
||||
deferred_stock_pools: RefCell<BTreeMap<String, stock_pool::DeferredStockPoolExecution>>,
|
||||
runtime_stock_pool_followup: Cell<bool>,
|
||||
cost_model: C,
|
||||
rules: R,
|
||||
board_lot_size: u32,
|
||||
@@ -469,6 +471,8 @@ impl<C, R> BrokerSimulator<C, R> {
|
||||
verified_etf_minute_absences: RefCell::new(BTreeSet::new()),
|
||||
runtime_etf_daily_open: Cell::new(false),
|
||||
deferred_etf_targets: RefCell::new(Default::default()),
|
||||
deferred_stock_pools: RefCell::new(BTreeMap::new()),
|
||||
runtime_stock_pool_followup: Cell::new(false),
|
||||
cost_model,
|
||||
rules,
|
||||
board_lot_size: 100,
|
||||
@@ -515,6 +519,8 @@ impl<C, R> BrokerSimulator<C, R> {
|
||||
verified_etf_minute_absences: RefCell::new(BTreeSet::new()),
|
||||
runtime_etf_daily_open: Cell::new(false),
|
||||
deferred_etf_targets: RefCell::new(Default::default()),
|
||||
deferred_stock_pools: RefCell::new(BTreeMap::new()),
|
||||
runtime_stock_pool_followup: Cell::new(false),
|
||||
cost_model,
|
||||
rules,
|
||||
board_lot_size: 100,
|
||||
@@ -778,7 +784,7 @@ impl<C, R> BrokerSimulator<C, R> {
|
||||
if self.runtime_etf_daily_open.get() { return PriceField::Open; }
|
||||
if self.is_post_close_fixed_price(date) {
|
||||
PriceField::Close
|
||||
} else if self.resting_daily_open_order() {
|
||||
} else if self.resting_daily_open_order() || (self.runtime_stock_pool_followup.get() && self.matching_type == MatchingType::NextBarOpen) {
|
||||
PriceField::Last
|
||||
} else {
|
||||
self.execution_price_field
|
||||
@@ -901,6 +907,12 @@ impl<C, R> BrokerSimulator<C, R> {
|
||||
!self.open_orders.borrow().is_empty()
|
||||
}
|
||||
|
||||
fn new_open_order_submission_time(&self) -> Option<NaiveTime> {
|
||||
if self.matching_type == MatchingType::NextBarOpen && !self.runtime_stock_pool_followup.get() {
|
||||
NaiveTime::from_hms_opt(9, 30, 0)
|
||||
} else { self.order_origin().1 }
|
||||
}
|
||||
|
||||
fn resting_order_session_close(&self, date: NaiveDate, order: &OpenOrder) -> NaiveTime {
|
||||
let post_close = self.execution_phase_for_submission(date, order.order_created_date, order.submission_time)
|
||||
== EquityExecutionPhase::PostCloseFixedPrice;
|
||||
@@ -1479,7 +1491,7 @@ where
|
||||
match algo_request.map(|request| request.style) {
|
||||
Some(AlgoExecutionStyle::Vwap) => MatchingType::Vwap,
|
||||
Some(AlgoExecutionStyle::Twap) => MatchingType::Twap,
|
||||
None if self.resting_daily_open_order() => MatchingType::CurrentBarClose,
|
||||
None if self.resting_daily_open_order() || (self.runtime_stock_pool_followup.get() && self.matching_type == MatchingType::NextBarOpen) => MatchingType::CurrentBarClose,
|
||||
None => self.matching_type,
|
||||
}
|
||||
}
|
||||
@@ -1597,6 +1609,13 @@ where
|
||||
session: &mut BrokerExecutionSession,
|
||||
) -> Result<BrokerExecutionReport, BacktestError> {
|
||||
let mut report = BrokerExecutionReport::default();
|
||||
// A fresh strategy intent supersedes any unsubmitted remainder before
|
||||
// old order reports can resume it. Already submitted orders are kept.
|
||||
for intent in &decision.order_intents {
|
||||
if let OrderIntent::StockPool { contract } = intent.unwrapped() {
|
||||
self.deferred_stock_pools.borrow_mut().remove(&contract.pool_id);
|
||||
}
|
||||
}
|
||||
self.process_open_orders(
|
||||
date,
|
||||
portfolio,
|
||||
@@ -1607,6 +1626,7 @@ where
|
||||
&mut session.commission_state,
|
||||
&mut report,
|
||||
)?;
|
||||
self.resume_stock_pool_executions(date, portfolio, data, session, &mut report)?;
|
||||
if !decision.order_intents.is_empty() {
|
||||
let mut ordered_intents = decision.order_intents.iter().collect::<Vec<_>>();
|
||||
if self.effective_rebalance_cash_mode() != RebalanceCashMode::PreOpenCash
|
||||
@@ -4149,7 +4169,7 @@ where
|
||||
side: OrderSide,
|
||||
algo_request: Option<&AlgoExecutionRequest>,
|
||||
) -> f64 {
|
||||
if self.matching_type == MatchingType::NextBarOpen && !self.resting_daily_open_order() && algo_request.is_none() {
|
||||
if self.matching_type == MatchingType::NextBarOpen && !self.resting_daily_open_order() && !self.runtime_stock_pool_followup.get() && algo_request.is_none() {
|
||||
return self.execution_limit_check_price(snapshot, side);
|
||||
}
|
||||
let matching_type = self.matching_type_for_algo_request(algo_request);
|
||||
@@ -4735,7 +4755,7 @@ where
|
||||
if Self::keeps_remainder_open(remainder_policy) {
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -4826,7 +4846,7 @@ where
|
||||
.unwrap_or("no sellable quantity");
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -4999,7 +5019,7 @@ where
|
||||
{
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -5171,7 +5191,7 @@ where
|
||||
if keep_open {
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -6559,7 +6579,7 @@ where
|
||||
if Self::keeps_remainder_open(remainder_policy) {
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -6799,7 +6819,7 @@ where
|
||||
{
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -6973,7 +6993,7 @@ where
|
||||
if keep_open {
|
||||
self.upsert_open_order(OpenOrder {
|
||||
order_id,
|
||||
submission_time: if self.matching_type == MatchingType::NextBarOpen { NaiveTime::from_hms_opt(9,30,0) } else { self.order_origin().1 },
|
||||
submission_time: self.new_open_order_submission_time(),
|
||||
accepted_date: self.accepted_order_date(date),
|
||||
decision_date: Some(self.current_decision_date(date)),
|
||||
order_created_date: Some(self.current_order_created_date(date)),
|
||||
@@ -8110,6 +8130,7 @@ where
|
||||
|
||||
pub(crate) fn matching_type_uses_intraday_quotes(&self) -> bool {
|
||||
if self.runtime_etf_daily_open.get() { return false; }
|
||||
if self.runtime_stock_pool_followup.get() { return true; }
|
||||
if self.resting_daily_open_order() { return true; }
|
||||
matches!(
|
||||
self.matching_type,
|
||||
@@ -8252,6 +8273,8 @@ mod tests {
|
||||
use crate::rules::ChinaEquityRuleHooks;
|
||||
use crate::strategy::{AlgoOrderStyle, OrderIntent, OrderTimeInForce, StrategyDecision};
|
||||
|
||||
include!("broker_stock_pool_batch_tests.rs");
|
||||
|
||||
fn test_open_order(order_id: u64) -> OpenOrder {
|
||||
OpenOrder {
|
||||
order_id,
|
||||
|
||||
Reference in New Issue
Block a user