修复FIDC选股阶段风控语义

This commit is contained in:
boris
2026-07-05 14:51:54 +08:00
parent d3c986e1f2
commit 00c9042c15
8 changed files with 307 additions and 88 deletions
+45 -26
View File
@@ -45,27 +45,27 @@ pub struct StaticRiskRuleConfig {
impl Default for StaticRiskRuleConfig {
fn default() -> Self {
Self {
reject_st_selection: true,
reject_st_selection: false,
reject_st_buy: true,
reject_star_st_selection: true,
reject_star_st_selection: false,
reject_star_st_buy: true,
reject_paused_selection: true,
reject_paused_selection: false,
reject_paused_buy: true,
reject_paused_sell: true,
reject_inactive_selection: true,
reject_inactive_selection: false,
reject_inactive_buy: true,
reject_inactive_sell: true,
reject_new_listing_selection: true,
reject_new_listing_selection: false,
reject_new_listing_buy: true,
reject_kcb_selection: true,
reject_kcb_selection: false,
reject_kcb_buy: true,
reject_bjse_selection: true,
reject_bjse_selection: false,
reject_bjse_buy: true,
reject_one_yuan_selection: true,
reject_one_yuan_selection: false,
reject_one_yuan_buy: true,
respect_allow_buy_sell: true,
reject_upper_limit_selection: true,
reject_lower_limit_selection: true,
reject_upper_limit_selection: false,
reject_lower_limit_selection: false,
reject_upper_limit_buy: true,
reject_lower_limit_sell: true,
forbid_same_day_rebuy_after_sell: true,
@@ -255,11 +255,6 @@ impl ChinaAShareRiskControl {
) {
return Some(reason);
}
if config.static_rules.respect_allow_buy_sell
&& (!candidate.allow_buy || !candidate.allow_sell)
{
return Some("trade_disabled");
}
let selection_price = market.price(PriceField::Last);
if config.static_rules.reject_upper_limit_selection
&& market.is_at_upper_limit_price(selection_price)
@@ -719,7 +714,6 @@ fn missing_single_field_rejected(
|| config.static_rules.reject_one_yuan_selection
|| config.static_rules.reject_upper_limit_selection
|| config.static_rules.reject_lower_limit_selection
|| config.static_rules.respect_allow_buy_sell
}
RiskCheckScope::Buy => {
config.static_rules.reject_st_buy
@@ -893,13 +887,19 @@ mod tests {
let default_reason =
ChinaAShareRiskControl::selection_rejection_reason(date, &candidate, &market, None);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_kcb_selection = true;
let enabled_selection_reason =
ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
config.static_rules.reject_kcb_selection = false;
config.static_rules.reject_kcb_buy = false;
let configured_reason = ChinaAShareRiskControl::buy_rejection_reason_with_config(
date, &candidate, &market, None, 6.27, &config,
);
assert_eq!(default_reason, Some("kcb"));
assert_eq!(default_reason, None);
assert_eq!(enabled_selection_reason, Some("kcb"));
assert_eq!(configured_reason, None);
}
@@ -1008,6 +1008,10 @@ mod tests {
let default_buy =
ChinaAShareRiskControl::buy_rejection_reason(date, &candidate, &market, None, 6.27);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_bjse_selection = true;
let enabled_selection = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
config.static_rules.reject_bjse_selection = false;
config.static_rules.reject_bjse_buy = false;
let configured_selection = ChinaAShareRiskControl::selection_rejection_reason_with_config(
@@ -1017,8 +1021,9 @@ mod tests {
date, &candidate, &market, None, 6.27, &config,
);
assert_eq!(default_selection, Some("bjse"));
assert_eq!(default_selection, None);
assert_eq!(default_buy, Some("bjse"));
assert_eq!(enabled_selection, Some("bjse"));
assert_eq!(configured_selection, None);
assert_eq!(configured_buy, None);
}
@@ -1030,13 +1035,11 @@ mod tests {
candidate.symbol = "688506.SH".to_string();
candidate.risk_level_code = Some("missing_risk_state".to_string());
let market = market(date, 6.27, 5.63);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_kcb_selection = true;
let decision = ChinaAShareRiskControl::selection_rejection_decision_with_config(
date,
&candidate,
&market,
None,
&FidcRiskControlConfig::default(),
date, &candidate, &market, None, &config,
)
.expect("kcb selection rejection");
@@ -1051,9 +1054,13 @@ mod tests {
candidate.allow_sell = true;
candidate.risk_level_code = Some("inactive_or_delisted".to_string());
let market = market(date, 6.27, 5.63);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_inactive_selection = true;
assert_eq!(
ChinaAShareRiskControl::selection_rejection_reason(date, &candidate, &market, None),
ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config
),
Some("inactive_or_delisted")
);
assert_eq!(
@@ -1189,6 +1196,8 @@ mod tests {
Some("missing_risk_state:upper_limit_price,lower_limit_price".to_string());
let market = market(date, 6.27, 5.63);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_upper_limit_selection = true;
config.static_rules.reject_lower_limit_selection = true;
config.static_rules.reject_upper_limit_selection = false;
let upper_disabled_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
@@ -1282,12 +1291,17 @@ mod tests {
let default_reason =
ChinaAShareRiskControl::selection_rejection_reason(date, &candidate, &market, None);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_upper_limit_selection = true;
let enabled_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
config.static_rules.reject_upper_limit_selection = false;
let configured_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
assert_eq!(default_reason, Some("upper_limit"));
assert_eq!(default_reason, None);
assert_eq!(enabled_reason, Some("upper_limit"));
assert_eq!(configured_reason, None);
}
@@ -1300,12 +1314,17 @@ mod tests {
let default_reason =
ChinaAShareRiskControl::selection_rejection_reason(date, &candidate, &market, None);
let mut config = FidcRiskControlConfig::default();
config.static_rules.reject_lower_limit_selection = true;
let enabled_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
config.static_rules.reject_lower_limit_selection = false;
let configured_reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
date, &candidate, &market, None, &config,
);
assert_eq!(default_reason, Some("lower_limit"));
assert_eq!(default_reason, None);
assert_eq!(enabled_reason, Some("lower_limit"));
assert_eq!(configured_reason, None);
}
}