diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index 9dfb001..0de0aab 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -2295,10 +2295,7 @@ where position: &crate::portfolio::Position, algo_request: Option<&AlgoExecutionRequest>, ) -> RuleCheck { - if self.risk_config.static_rules.respect_allow_buy_sell - && !self.aiquant_execution_rules - && !candidate.allow_sell - { + if self.risk_config.static_rules.respect_allow_buy_sell && !candidate.allow_sell { return RuleCheck::reject("sell_disabled"); } let check_price = if self.aiquant_execution_rules { diff --git a/crates/fidc-core/src/risk_control.rs b/crates/fidc-core/src/risk_control.rs index 64613b3..16092c7 100644 --- a/crates/fidc-core/src/risk_control.rs +++ b/crates/fidc-core/src/risk_control.rs @@ -506,10 +506,9 @@ impl ChinaAShareRiskControl { if config.static_rules.reject_paused_sell && (market.paused || candidate.is_paused) { return Some("paused"); } - // `allow_sell` is derived from the daily candidate snapshot and may - // reflect an open/close fallback rather than the actual execution price. - // A sell order must be blocked by the execution price lower-limit check - // below, while suspension and delisting are handled above. + if config.static_rules.respect_allow_buy_sell && !candidate.allow_sell { + return Some("sell_disabled"); + } if config.static_rules.reject_lower_limit_sell && market.is_at_lower_limit_price(check_price) { @@ -809,7 +808,7 @@ mod tests { } #[test] - fn sell_rejection_uses_execution_price_not_stale_allow_sell() { + fn sell_rejection_respects_allow_sell_policy_on_execution_day() { let prev_date = d(2024, 4, 16); let date = d(2024, 4, 17); let candidate = candidate(date); @@ -825,7 +824,21 @@ mod tests { 6.27, ); - assert_eq!(reason, None); + assert_eq!(reason, Some("sell_disabled")); + + let mut relaxed = FidcRiskControlConfig::default(); + relaxed.static_rules.respect_allow_buy_sell = false; + let relaxed_reason = ChinaAShareRiskControl::sell_rejection_reason_with_config( + date, + &candidate, + &market, + None, + Some(&position), + 6.27, + &relaxed, + ); + + assert_eq!(relaxed_reason, None); } #[test]