修正退市候选卖出风控边界

This commit is contained in:
boris
2026-08-29 04:02:28 +08:00
parent 56a38accc8
commit ff2844884d
+30
View File
@@ -515,6 +515,13 @@ impl ChinaAShareRiskControl {
) {
return Some(reason);
}
// When instrument metadata is unavailable, an explicit candidate
// lifecycle fact must still protect the sell path. Otherwise a
// `inactive_or_delisted` candidate could fall through to a synthetic
// sell price and violate the unresolved-delisted holding contract.
if let Some(reason) = candidate_active_status_rejection(candidate, config, RiskCheckScope::Sell) {
return Some(reason);
}
if config.static_rules.reject_paused_sell && (market.paused || candidate.is_paused) {
return Some("paused");
}
@@ -1138,6 +1145,29 @@ mod tests {
);
}
#[test]
fn sell_rejects_explicit_candidate_inactive_status_without_instrument_row() {
let date = d(2025, 1, 2);
let mut candidate = candidate(date);
candidate.allow_sell = true;
candidate.risk_level_code = Some("inactive_or_delisted".to_string());
let market = market(date, 6.27, 5.63);
let position = position(d(2024, 12, 31));
assert_eq!(
ChinaAShareRiskControl::sell_rejection_reason_with_config(
date,
&candidate,
&market,
None,
Some(&position),
6.27,
&FidcRiskControlConfig::default(),
),
Some("inactive_or_delisted")
);
}
#[test]
fn missing_risk_state_default_selection_ignores_allow_flags_but_buy_rejects() {
let date = d(2025, 1, 2);