统一策略成交保护与锁定周期并修正日期条件覆盖

This commit is contained in:
boris
2026-09-11 09:52:04 +08:00
parent 6684f48f95
commit 33924b1fba
9 changed files with 1008 additions and 11 deletions
+32
View File
@@ -381,6 +381,8 @@ pub struct BrokerSimulator<C, R> {
runtime_intraday_end_time: Cell<Option<NaiveTime>>,
runtime_decision_date: Cell<Option<NaiveDate>>,
runtime_buy_denials: RefCell<BTreeMap<String, String>>,
runtime_auto_buy_denials: RefCell<BTreeMap<String, String>>,
runtime_auto_sell_denials: RefCell<BTreeMap<String, String>>,
runtime_order_created_date: Cell<Option<NaiveDate>>,
runtime_decision_total_equity: Cell<Option<f64>>,
runtime_target_position_limit: Cell<Option<usize>>,
@@ -414,6 +416,8 @@ impl<C, R> BrokerSimulator<C, R> {
runtime_intraday_end_time: Cell::new(None),
runtime_decision_date: Cell::new(None),
runtime_buy_denials: RefCell::new(BTreeMap::new()),
runtime_auto_buy_denials: RefCell::new(BTreeMap::new()),
runtime_auto_sell_denials: RefCell::new(BTreeMap::new()),
runtime_order_created_date: Cell::new(None),
runtime_decision_total_equity: Cell::new(None),
runtime_target_position_limit: Cell::new(None),
@@ -451,6 +455,8 @@ impl<C, R> BrokerSimulator<C, R> {
runtime_intraday_end_time: Cell::new(None),
runtime_decision_date: Cell::new(None),
runtime_buy_denials: RefCell::new(BTreeMap::new()),
runtime_auto_buy_denials: RefCell::new(BTreeMap::new()),
runtime_auto_sell_denials: RefCell::new(BTreeMap::new()),
runtime_order_created_date: Cell::new(None),
runtime_decision_total_equity: Cell::new(None),
runtime_target_position_limit: Cell::new(None),
@@ -1389,6 +1395,11 @@ where
) -> Result<BrokerExecutionReport, BacktestError> {
let previous_decision_date = self.runtime_decision_date.get();
let previous_buy_denials = self.runtime_buy_denials.replace(decision.buy_denials.clone());
let protection_denials = |scope| decision.risk_decisions.iter()
.filter(|row| !row.accepted && row.stage == "automatic_trade_protection" && row.scope == scope)
.map(|row| (row.symbol.clone(), row.reason.clone())).collect();
let previous_auto_buy_denials = self.runtime_auto_buy_denials.replace(protection_denials(crate::risk_control::RiskCheckScope::Buy));
let previous_auto_sell_denials = self.runtime_auto_sell_denials.replace(protection_denials(crate::risk_control::RiskCheckScope::Sell));
let previous_order_created_date = self.runtime_order_created_date.get();
let previous_decision_total_equity = self.runtime_decision_total_equity.get();
self.runtime_decision_date.set(Some(decision_date));
@@ -1398,6 +1409,8 @@ where
.set(decision_total_equity.filter(|equity| equity.is_finite() && *equity >= 0.0));
let result = self.execute_with_runtime_dates(date, portfolio, data, decision);
self.runtime_buy_denials.replace(previous_buy_denials);
self.runtime_auto_buy_denials.replace(previous_auto_buy_denials);
self.runtime_auto_sell_denials.replace(previous_auto_sell_denials);
self.runtime_decision_date.set(previous_decision_date);
self.runtime_order_created_date
.set(previous_order_created_date);
@@ -2850,6 +2863,15 @@ where
return;
}
let protection = match existing.side {
OrderSide::Buy => self.runtime_auto_buy_denials.borrow().get(&existing.symbol).cloned(),
OrderSide::Sell => self.runtime_auto_sell_denials.borrow().get(&existing.symbol).cloned(),
};
if let Some(denial) = protection
&& (target_limit_price.to_bits() != existing.limit_price.to_bits() || target_total_quantity != existing.requested_quantity) {
Self::emit_open_order_update_rejected(report, date, order_id, Some(&existing.symbol), Some(existing.side), reason, &denial);
return;
}
let resets_queue_priority = target_limit_price.to_bits() != existing.limit_price.to_bits()
|| target_total_quantity > existing.requested_quantity;
{
@@ -4139,6 +4161,9 @@ where
minimum_order_quantity: u32,
order_step_size: u32,
) -> Option<String> {
if let Some(reason) = self.runtime_auto_sell_denials.borrow().get(symbol) {
return Some(reason.clone());
}
if current_qty == 0 {
return None;
}
@@ -4299,6 +4324,10 @@ where
algo_request: Option<&AlgoExecutionRequest>,
report: &mut BrokerExecutionReport,
) -> Result<(), BacktestError> {
// Existing accepted orders are not canceled by a subsequently enabled lock.
if emit_creation_events && self.runtime_auto_sell_denials.borrow().contains_key(symbol) {
return Ok(());
}
let remainder_policy = self.effective_remainder_policy(date, allow_pending_limit);
let Some(position) = portfolio.position(symbol) else {
return Ok(());
@@ -6074,6 +6103,9 @@ where
algo_request: Option<&AlgoExecutionRequest>,
report: &mut BrokerExecutionReport,
) -> Result<(), BacktestError> {
if emit_creation_events && self.runtime_auto_buy_denials.borrow().contains_key(symbol) {
return Ok(());
}
let remainder_policy = self.effective_remainder_policy(date, allow_pending_limit);
if portfolio
.position(symbol)