统一策略成交保护与锁定周期并修正日期条件覆盖
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user