拆分ST与星号ST风控语义
This commit is contained in:
@@ -15,6 +15,8 @@ pub struct ChinaAShareRiskControl;
|
||||
pub struct StaticRiskRuleConfig {
|
||||
pub reject_st_selection: bool,
|
||||
pub reject_st_buy: bool,
|
||||
pub reject_star_st_selection: bool,
|
||||
pub reject_star_st_buy: bool,
|
||||
pub reject_paused_selection: bool,
|
||||
pub reject_paused_buy: bool,
|
||||
pub reject_paused_sell: bool,
|
||||
@@ -45,6 +47,8 @@ impl Default for StaticRiskRuleConfig {
|
||||
Self {
|
||||
reject_st_selection: true,
|
||||
reject_st_buy: true,
|
||||
reject_star_st_selection: true,
|
||||
reject_star_st_buy: true,
|
||||
reject_paused_selection: true,
|
||||
reject_paused_buy: true,
|
||||
reject_paused_sell: true,
|
||||
@@ -346,6 +350,14 @@ impl ChinaAShareRiskControl {
|
||||
if reject_st && candidate.is_st {
|
||||
return Some("st");
|
||||
}
|
||||
let reject_star_st = match scope {
|
||||
RiskCheckScope::Selection => config.static_rules.reject_star_st_selection,
|
||||
RiskCheckScope::Buy => config.static_rules.reject_star_st_buy,
|
||||
RiskCheckScope::Sell => false,
|
||||
};
|
||||
if reject_star_st && candidate.is_star_st {
|
||||
return Some("star_st");
|
||||
}
|
||||
let reject_new_listing = match scope {
|
||||
RiskCheckScope::Selection => config.static_rules.reject_new_listing_selection,
|
||||
RiskCheckScope::Buy => config.static_rules.reject_new_listing_buy,
|
||||
@@ -547,6 +559,7 @@ fn missing_selection_risk_state_rejected(code: &str, config: &FidcRiskControlCon
|
||||
let fields = missing_risk_state_fields(code);
|
||||
if fields.is_empty() {
|
||||
return config.static_rules.reject_st_selection
|
||||
|| config.static_rules.reject_star_st_selection
|
||||
|| config.static_rules.reject_paused_selection
|
||||
|| config.static_rules.reject_inactive_selection
|
||||
|| config.static_rules.reject_new_listing_selection
|
||||
@@ -564,6 +577,7 @@ fn missing_buy_risk_state_rejected(code: &str, config: &FidcRiskControlConfig) -
|
||||
let fields = missing_risk_state_fields(code);
|
||||
if fields.is_empty() {
|
||||
return config.static_rules.reject_st_buy
|
||||
|| config.static_rules.reject_star_st_buy
|
||||
|| config.static_rules.reject_paused_buy
|
||||
|| config.static_rules.reject_inactive_buy
|
||||
|| config.static_rules.reject_new_listing_buy
|
||||
@@ -603,11 +617,16 @@ fn missing_single_field_rejected(
|
||||
scope: RiskCheckScope,
|
||||
) -> bool {
|
||||
match field {
|
||||
"is_st" | "is_star_st" | "st" | "star_st" | "star_st_status" => match scope {
|
||||
"is_st" | "st" => match scope {
|
||||
RiskCheckScope::Selection => config.static_rules.reject_st_selection,
|
||||
RiskCheckScope::Buy => config.static_rules.reject_st_buy,
|
||||
RiskCheckScope::Sell => false,
|
||||
},
|
||||
"is_star_st" | "star_st" | "star_st_status" => match scope {
|
||||
RiskCheckScope::Selection => config.static_rules.reject_star_st_selection,
|
||||
RiskCheckScope::Buy => config.static_rules.reject_star_st_buy,
|
||||
RiskCheckScope::Sell => false,
|
||||
},
|
||||
"paused" | "is_paused" | "suspended" | "is_suspended" => match scope {
|
||||
RiskCheckScope::Selection => config.static_rules.reject_paused_selection,
|
||||
RiskCheckScope::Buy => config.static_rules.reject_paused_buy,
|
||||
@@ -669,6 +688,7 @@ fn missing_single_field_rejected(
|
||||
_ => match scope {
|
||||
RiskCheckScope::Selection => {
|
||||
config.static_rules.reject_st_selection
|
||||
|| config.static_rules.reject_star_st_selection
|
||||
|| config.static_rules.reject_paused_selection
|
||||
|| config.static_rules.reject_inactive_selection
|
||||
|| config.static_rules.reject_new_listing_selection
|
||||
@@ -681,6 +701,7 @@ fn missing_single_field_rejected(
|
||||
}
|
||||
RiskCheckScope::Buy => {
|
||||
config.static_rules.reject_st_buy
|
||||
|| config.static_rules.reject_star_st_buy
|
||||
|| config.static_rules.reject_paused_buy
|
||||
|| config.static_rules.reject_inactive_buy
|
||||
|| config.static_rules.reject_new_listing_buy
|
||||
@@ -721,6 +742,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "002633.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -844,6 +866,74 @@ mod tests {
|
||||
assert_eq!(configured_reason, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn st_and_star_st_filters_are_independent() {
|
||||
let date = d(2025, 1, 2);
|
||||
let market = market(date, 6.27, 5.63);
|
||||
|
||||
let mut st_candidate = candidate(date);
|
||||
st_candidate.is_st = true;
|
||||
st_candidate.allow_sell = true;
|
||||
let mut star_st_candidate = candidate(date);
|
||||
star_st_candidate.is_star_st = true;
|
||||
star_st_candidate.allow_sell = true;
|
||||
|
||||
let mut config = FidcRiskControlConfig::default();
|
||||
config.static_rules.reject_st_selection = false;
|
||||
config.static_rules.reject_st_buy = false;
|
||||
config.static_rules.reject_star_st_selection = true;
|
||||
config.static_rules.reject_star_st_buy = true;
|
||||
|
||||
assert_eq!(
|
||||
ChinaAShareRiskControl::selection_rejection_reason_with_config(
|
||||
date,
|
||||
&st_candidate,
|
||||
&market,
|
||||
None,
|
||||
&config,
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
ChinaAShareRiskControl::selection_rejection_reason_with_config(
|
||||
date,
|
||||
&star_st_candidate,
|
||||
&market,
|
||||
None,
|
||||
&config,
|
||||
),
|
||||
Some("star_st")
|
||||
);
|
||||
|
||||
config.static_rules.reject_st_selection = true;
|
||||
config.static_rules.reject_st_buy = true;
|
||||
config.static_rules.reject_star_st_selection = false;
|
||||
config.static_rules.reject_star_st_buy = false;
|
||||
|
||||
assert_eq!(
|
||||
ChinaAShareRiskControl::buy_rejection_reason_with_config(
|
||||
date,
|
||||
&st_candidate,
|
||||
&market,
|
||||
None,
|
||||
6.27,
|
||||
&config,
|
||||
),
|
||||
Some("st")
|
||||
);
|
||||
assert_eq!(
|
||||
ChinaAShareRiskControl::buy_rejection_reason_with_config(
|
||||
date,
|
||||
&star_st_candidate,
|
||||
&market,
|
||||
None,
|
||||
6.27,
|
||||
&config,
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configurable_bjse_filter_can_be_disabled() {
|
||||
let date = d(2025, 1, 2);
|
||||
|
||||
Reference in New Issue
Block a user