补充缺失风控状态拒绝

This commit is contained in:
boris
2026-07-02 09:41:07 +08:00
parent b176d2ff6f
commit baa77c68e0
+100
View File
@@ -300,6 +300,9 @@ impl ChinaAShareRiskControl {
{ {
return Some(reason); return Some(reason);
} }
if Self::missing_risk_state_rejected(candidate, config, scope) {
return Some("missing_risk_state");
}
let reject_paused = match scope { let reject_paused = match scope {
RiskCheckScope::Selection => config.static_rules.reject_paused_selection, RiskCheckScope::Selection => config.static_rules.reject_paused_selection,
RiskCheckScope::Buy => config.static_rules.reject_paused_buy, RiskCheckScope::Buy => config.static_rules.reject_paused_buy,
@@ -343,6 +346,45 @@ impl ChinaAShareRiskControl {
None None
} }
fn missing_risk_state_rejected(
candidate: &CandidateEligibility,
config: &FidcRiskControlConfig,
scope: RiskCheckScope,
) -> bool {
let Some(code) = candidate
.risk_level_code
.as_deref()
.map(str::trim)
.filter(|value| value.starts_with("missing_risk_state"))
else {
return false;
};
if code.is_empty() {
return false;
}
match scope {
RiskCheckScope::Selection => {
config.static_rules.reject_st_selection
|| config.static_rules.reject_paused_selection
|| config.static_rules.reject_inactive_selection
|| config.static_rules.reject_new_listing_selection
|| config.static_rules.reject_kcb_selection
|| config.static_rules.reject_one_yuan_selection
|| config.static_rules.respect_allow_buy_sell
}
RiskCheckScope::Buy => {
config.static_rules.reject_st_buy
|| config.static_rules.reject_paused_buy
|| config.static_rules.reject_inactive_buy
|| config.static_rules.reject_new_listing_buy
|| config.static_rules.reject_kcb_buy
|| config.static_rules.reject_one_yuan_buy
|| config.static_rules.respect_allow_buy_sell
}
RiskCheckScope::Sell => false,
}
}
pub fn buy_rejection_reason( pub fn buy_rejection_reason(
date: NaiveDate, date: NaiveDate,
candidate: &CandidateEligibility, candidate: &CandidateEligibility,
@@ -598,6 +640,64 @@ mod tests {
assert_eq!(configured_reason, None); assert_eq!(configured_reason, None);
} }
#[test]
fn missing_risk_state_rejects_selection_and_buy_when_static_filters_enabled() {
let date = d(2025, 1, 2);
let mut candidate = candidate(date);
candidate.risk_level_code = Some("missing_risk_state:is_st,paused,allow_buy".to_string());
let market = market(date, 6.27, 5.63);
let selection_reason =
ChinaAShareRiskControl::selection_rejection_reason(date, &candidate, &market, None);
let buy_reason =
ChinaAShareRiskControl::buy_rejection_reason(date, &candidate, &market, None, 6.27);
let sell_reason = ChinaAShareRiskControl::sell_rejection_reason(
date,
&candidate,
&market,
None,
Some(&position(d(2024, 12, 31))),
6.27,
);
assert_eq!(selection_reason, Some("missing_risk_state"));
assert_eq!(buy_reason, Some("missing_risk_state"));
assert_eq!(sell_reason, None);
}
#[test]
fn missing_risk_state_can_be_relaxed_by_disabling_static_filters() {
let date = d(2025, 1, 2);
let mut candidate = candidate(date);
candidate.allow_sell = true;
candidate.risk_level_code = Some("missing_risk_state:is_st,paused,allow_buy".to_string());
let market = market(date, 6.27, 5.63);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_st_selection = false;
config.static_rules.reject_st_buy = false;
config.static_rules.reject_paused_selection = false;
config.static_rules.reject_paused_buy = false;
config.static_rules.reject_inactive_selection = false;
config.static_rules.reject_inactive_buy = false;
config.static_rules.reject_new_listing_selection = false;
config.static_rules.reject_new_listing_buy = false;
config.static_rules.reject_kcb_selection = false;
config.static_rules.reject_kcb_buy = false;
config.static_rules.reject_one_yuan_selection = false;
config.static_rules.reject_one_yuan_buy = false;
config.static_rules.respect_allow_buy_sell = false;
let selection_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
let buy_reason = ChinaAShareRiskControl::buy_rejection_reason_with_config(
date, &candidate, &market, None, 6.27, &config,
);
assert_eq!(selection_reason, None);
assert_eq!(buy_reason, None);
}
#[test] #[test]
fn configurable_upper_limit_buy_filter_can_be_disabled() { fn configurable_upper_limit_buy_filter_can_be_disabled() {
let date = d(2025, 1, 2); let date = d(2025, 1, 2);