diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index f839be0..92c177f 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -8762,21 +8762,7 @@ impl Strategy for PlatformExprStrategy { if !stop_hit && !profit_hit { continue; } - let lower_limit_blocked = match self.stock_state_at_time( - ctx, - projection_date, - &position.symbol, - Some(self.intraday_execution_start_time()), - ) { - Ok(stock) => stock.lower_limit > 0.0 && stock.last <= stock.lower_limit, - Err(BacktestError::Data(crate::data::DataSetError::MissingSnapshot { - .. - })) => false, - Err(error) => return Err(error), - }; - if !lower_limit_blocked { - stop_take_exit_signal_symbols.insert(position.symbol.clone()); - } + stop_take_exit_signal_symbols.insert(position.symbol.clone()); } } @@ -9255,6 +9241,9 @@ impl Strategy for PlatformExprStrategy { let rebalance_value_symbols = rebalance_working_symbols.clone(); let mut rebalance_pending_buy_value = 0.0_f64; for symbol in stock_list.iter().take(selection_limit) { + if exit_symbols.contains(symbol) { + continue; + } let decision_stock = self.stock_state_with_factor_date( ctx, decision_date, @@ -17290,7 +17279,7 @@ mod tests { } #[test] - fn platform_aiquant_allows_target_adjust_when_stop_loss_sell_is_lower_limit_blocked() { + fn platform_aiquant_blocks_target_increase_when_stop_loss_sell_is_lower_limit_blocked() { let prev_date = d(2025, 1, 10); let date = d(2025, 1, 13); let symbol = "603269.SH"; @@ -17420,7 +17409,7 @@ mod tests { reason, } if intent_symbol == symbol && *target_value == 0.0 && reason == "stop_loss_exit" ))); - assert!(decision.order_intents.iter().any(|intent| matches!( + assert!(!decision.order_intents.iter().any(|intent| matches!( intent, OrderIntent::Shares { symbol: intent_symbol, @@ -21189,7 +21178,7 @@ mod tests { } #[test] - fn platform_weak_market_adjusts_before_lower_limit_blocked_stop_loss() { + fn platform_weak_market_skips_target_increase_before_lower_limit_blocked_stop_loss() { let prev_date = d(2025, 2, 2); let date = d(2025, 2, 3); let symbols = ["000001.SZ", "000002.SZ"]; @@ -21315,36 +21304,28 @@ mod tests { let decision = strategy.on_day(&ctx).expect("platform decision"); - let stop_loss_index = decision - .order_intents - .iter() - .position(|intent| { - matches!( - intent, - OrderIntent::TargetValue { - symbol, - target_value, - reason, - } if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit" - ) - }) - .expect("stop-loss exit"); assert!( - decision.order_intents[..stop_loss_index] - .iter() - .any(|intent| matches!( - intent, - OrderIntent::Shares { - symbol, - quantity, - reason, - } if symbol == "000001.SZ" - && reason == "daily_position_target_adjust" - && *quantity > 0 - )), + !decision.order_intents.iter().any(|intent| matches!( + intent, + OrderIntent::Shares { + symbol, + quantity, + reason, + } if symbol == "000001.SZ" + && reason == "daily_position_target_adjust" + && *quantity > 0 + )), "{:?}", decision.order_intents ); + assert!(decision.order_intents.iter().any(|intent| matches!( + intent, + OrderIntent::TargetValue { + symbol, + target_value, + reason, + } if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit" + ))); assert!(decision.order_intents.iter().any(|intent| matches!( intent, OrderIntent::Value { symbol, reason, .. } @@ -21353,7 +21334,7 @@ mod tests { } #[test] - fn platform_weak_market_adjusts_before_intraday_stop_loss_sell_blocked() { + fn platform_weak_market_skips_target_increase_before_intraday_stop_loss_sell_blocked() { let prev_date = d(2025, 2, 2); let date = d(2025, 2, 3); let symbols = ["000001.SZ", "000002.SZ"]; @@ -21497,36 +21478,28 @@ mod tests { let decision = strategy.on_day(&ctx).expect("platform decision"); - let stop_loss_index = decision - .order_intents - .iter() - .position(|intent| { - matches!( - intent, - OrderIntent::TargetValue { - symbol, - target_value, - reason, - } if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit" - ) - }) - .expect("stop-loss exit"); assert!( - decision.order_intents[..stop_loss_index] - .iter() - .any(|intent| matches!( - intent, - OrderIntent::Shares { - symbol, - quantity, - reason, - } if symbol == "000001.SZ" - && reason == "daily_position_target_adjust" - && *quantity > 0 - )), + !decision.order_intents.iter().any(|intent| matches!( + intent, + OrderIntent::Shares { + symbol, + quantity, + reason, + } if symbol == "000001.SZ" + && reason == "daily_position_target_adjust" + && *quantity > 0 + )), "{:?}", decision.order_intents ); + assert!(decision.order_intents.iter().any(|intent| matches!( + intent, + OrderIntent::TargetValue { + symbol, + target_value, + reason, + } if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit" + ))); assert!(decision.order_intents.iter().any(|intent| matches!( intent, OrderIntent::Value { symbol, reason, .. } @@ -24915,7 +24888,7 @@ mod tests { } #[test] - fn platform_periodic_rebalance_can_rebuy_released_unsellable_stop_loss_symbol() { + fn platform_periodic_rebalance_skips_unsellable_stop_loss_symbol() { let prev_date = d(2025, 5, 13); let date = d(2025, 5, 14); let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"]; @@ -25078,7 +25051,7 @@ mod tests { decision.order_intents ); assert!( - decision.order_intents.iter().any(|intent| matches!( + !decision.order_intents.iter().any(|intent| matches!( intent, OrderIntent::Value { symbol, reason, .. } if symbol == "000001.SZ" && reason == "periodic_rebalance_buy" @@ -25086,6 +25059,15 @@ mod tests { "{:?}", decision.order_intents ); + assert!( + decision.order_intents.iter().any(|intent| matches!( + intent, + OrderIntent::Value { symbol, reason, .. } + if symbol == "000003.SZ" && reason == "periodic_rebalance_buy" + )), + "{:?}", + decision.order_intents + ); } #[test]