拆分ST与星号ST风控语义
This commit is contained in:
@@ -5734,6 +5734,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy,
|
||||
@@ -6208,6 +6209,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -202,6 +202,8 @@ pub struct CandidateEligibility {
|
||||
pub date: NaiveDate,
|
||||
pub symbol: String,
|
||||
pub is_st: bool,
|
||||
#[serde(default)]
|
||||
pub is_star_st: bool,
|
||||
pub is_new_listing: bool,
|
||||
pub is_paused: bool,
|
||||
pub allow_buy: bool,
|
||||
@@ -215,6 +217,7 @@ pub struct CandidateEligibility {
|
||||
impl CandidateEligibility {
|
||||
pub fn eligible_for_selection(&self) -> bool {
|
||||
!self.is_st
|
||||
&& !self.is_star_st
|
||||
&& !self.is_new_listing
|
||||
&& !self.is_paused
|
||||
&& !self.is_kcb
|
||||
@@ -3128,15 +3131,16 @@ fn read_candidates(path: &Path) -> Result<Vec<CandidateEligibility>, DataSetErro
|
||||
date: row.parse_date(0)?,
|
||||
symbol: row.get(1)?.to_string(),
|
||||
is_st: row.parse_bool(2)?,
|
||||
is_new_listing: row.parse_bool(3)?,
|
||||
is_paused: row.parse_bool(4)?,
|
||||
allow_buy: row.parse_bool(5)?,
|
||||
allow_sell: row.parse_bool(6)?,
|
||||
is_kcb: row.parse_optional_bool(7).unwrap_or(false),
|
||||
is_one_yuan: row.parse_optional_bool(8).unwrap_or(false),
|
||||
is_star_st: row.parse_bool(3)?,
|
||||
is_new_listing: row.parse_bool(4)?,
|
||||
is_paused: row.parse_bool(5)?,
|
||||
allow_buy: row.parse_bool(6)?,
|
||||
allow_sell: row.parse_bool(7)?,
|
||||
is_kcb: row.parse_optional_bool(8).unwrap_or(false),
|
||||
is_one_yuan: row.parse_optional_bool(9).unwrap_or(false),
|
||||
risk_level_code: row
|
||||
.fields
|
||||
.get(9)
|
||||
.get(10)
|
||||
.map(String::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
@@ -4003,6 +4007,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -4093,6 +4098,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -4024,6 +4024,7 @@ mod tests {
|
||||
date,
|
||||
symbol: SYMBOL.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -8512,6 +8512,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -8637,6 +8638,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -8825,6 +8827,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -9146,6 +9149,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -9287,6 +9291,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -9433,6 +9438,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -9529,6 +9535,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -9642,6 +9649,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -9760,6 +9768,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -9911,6 +9920,7 @@ mod tests {
|
||||
date: first_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -9923,6 +9933,7 @@ mod tests {
|
||||
date: second_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -10194,6 +10205,7 @@ mod tests {
|
||||
date,
|
||||
symbol: delayed_symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10206,6 +10218,7 @@ mod tests {
|
||||
date,
|
||||
symbol: other_symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10485,6 +10498,7 @@ mod tests {
|
||||
date,
|
||||
symbol: delayed_symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10497,6 +10511,7 @@ mod tests {
|
||||
date,
|
||||
symbol: other_symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10716,6 +10731,7 @@ mod tests {
|
||||
date: first_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10728,6 +10744,7 @@ mod tests {
|
||||
date: second_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -10926,6 +10943,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -11044,6 +11062,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -11182,6 +11201,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -11288,6 +11308,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -11422,6 +11443,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -11524,6 +11546,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -11654,6 +11677,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -11788,6 +11812,7 @@ mod tests {
|
||||
date: current,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -11896,6 +11921,7 @@ mod tests {
|
||||
date: current,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -12008,6 +12034,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -12122,6 +12149,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -12235,6 +12263,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -12379,6 +12408,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -12534,6 +12564,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -12690,6 +12721,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -12846,6 +12878,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13013,6 +13046,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13171,6 +13205,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13319,6 +13354,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13458,6 +13494,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: true,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13578,6 +13615,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13728,6 +13766,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -13825,6 +13864,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -14048,6 +14088,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -14239,6 +14280,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -14338,6 +14380,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -14453,6 +14496,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: false,
|
||||
@@ -14556,6 +14600,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -14655,6 +14700,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: true,
|
||||
allow_buy: true,
|
||||
@@ -14837,6 +14883,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15046,6 +15093,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15215,6 +15263,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15357,6 +15406,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15516,6 +15566,7 @@ mod tests {
|
||||
date: decision_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15528,6 +15579,7 @@ mod tests {
|
||||
date: execution_date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15671,6 +15723,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "301001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: true,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15683,6 +15736,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15803,6 +15857,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -15946,6 +16001,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16094,6 +16150,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16229,6 +16286,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16365,6 +16423,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16522,6 +16581,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16657,6 +16717,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16814,6 +16875,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -16974,6 +17036,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -17123,6 +17186,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -17297,6 +17361,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -17570,6 +17635,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -17730,6 +17796,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -17875,6 +17942,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18056,6 +18124,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18234,6 +18303,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18456,6 +18526,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18606,6 +18677,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18758,6 +18830,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -18999,6 +19072,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.clone(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19186,6 +19260,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19331,6 +19406,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19470,6 +19546,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19606,6 +19683,7 @@ mod tests {
|
||||
date,
|
||||
symbol: item.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19735,6 +19813,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: true,
|
||||
allow_buy: false,
|
||||
@@ -19837,6 +19916,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -19990,6 +20070,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -20162,6 +20243,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -20311,6 +20393,7 @@ mod tests {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -20445,6 +20528,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_kcb: false,
|
||||
is_paused: false,
|
||||
@@ -20565,6 +20649,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_kcb: false,
|
||||
is_paused: false,
|
||||
@@ -21250,6 +21335,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -21374,6 +21460,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -21489,6 +21576,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -21612,6 +21700,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -21753,6 +21842,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -21881,6 +21971,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -22004,6 +22095,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -22121,6 +22213,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -22260,6 +22353,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -22467,6 +22561,7 @@ mod tests {
|
||||
date,
|
||||
symbol: (*symbol).to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -154,6 +154,7 @@ const RESERVED_SCOPE_NAMES: &[&str] = &[
|
||||
"round_lot",
|
||||
"paused",
|
||||
"is_st",
|
||||
"is_star_st",
|
||||
"is_kcb",
|
||||
"is_one_yuan",
|
||||
"is_new_listing",
|
||||
|
||||
@@ -182,20 +182,14 @@ pub struct StrategyEngineConfig {
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StrategyRiskPolicySpec {
|
||||
#[serde(
|
||||
default,
|
||||
alias = "reject_st_selection",
|
||||
alias = "reject_star_st_selection",
|
||||
alias = "rejectStarStSelection"
|
||||
)]
|
||||
#[serde(default, alias = "reject_st_selection", alias = "rejectStSelection")]
|
||||
pub reject_st_selection: Option<bool>,
|
||||
#[serde(
|
||||
default,
|
||||
alias = "reject_st_buy",
|
||||
alias = "reject_star_st_buy",
|
||||
alias = "rejectStarStBuy"
|
||||
)]
|
||||
#[serde(default, alias = "reject_st_buy", alias = "rejectStBuy")]
|
||||
pub reject_st_buy: Option<bool>,
|
||||
#[serde(default, alias = "reject_star_st_selection")]
|
||||
pub reject_star_st_selection: Option<bool>,
|
||||
#[serde(default, alias = "reject_star_st_buy")]
|
||||
pub reject_star_st_buy: Option<bool>,
|
||||
#[serde(default, alias = "reject_paused_selection")]
|
||||
pub reject_paused_selection: Option<bool>,
|
||||
#[serde(default, alias = "reject_paused_buy")]
|
||||
@@ -264,18 +258,10 @@ pub struct StrategyRiskPolicySpec {
|
||||
}
|
||||
|
||||
const RISK_POLICY_BOOL_ALIAS_GROUPS: &[(&str, &[&str])] = &[
|
||||
(
|
||||
"rejectStSelection",
|
||||
&[
|
||||
"reject_st_selection",
|
||||
"rejectStarStSelection",
|
||||
"reject_star_st_selection",
|
||||
],
|
||||
),
|
||||
(
|
||||
"rejectStBuy",
|
||||
&["reject_st_buy", "rejectStarStBuy", "reject_star_st_buy"],
|
||||
),
|
||||
("rejectStSelection", &["reject_st_selection"]),
|
||||
("rejectStarStSelection", &["reject_star_st_selection"]),
|
||||
("rejectStBuy", &["reject_st_buy"]),
|
||||
("rejectStarStBuy", &["reject_star_st_buy"]),
|
||||
("rejectPausedSelection", &["reject_paused_selection"]),
|
||||
("rejectPausedBuy", &["reject_paused_buy"]),
|
||||
("rejectPausedSell", &["reject_paused_sell"]),
|
||||
@@ -770,6 +756,12 @@ fn apply_risk_policy_overrides(
|
||||
if let Some(value) = policy.reject_st_buy {
|
||||
static_rules.reject_st_buy = value;
|
||||
}
|
||||
if let Some(value) = policy.reject_star_st_selection {
|
||||
static_rules.reject_star_st_selection = value;
|
||||
}
|
||||
if let Some(value) = policy.reject_star_st_buy {
|
||||
static_rules.reject_star_st_buy = value;
|
||||
}
|
||||
if let Some(value) = policy.reject_paused_selection {
|
||||
static_rules.reject_paused_selection = value;
|
||||
}
|
||||
@@ -2246,7 +2238,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_snake_case_and_star_st_risk_policy_aliases_into_platform_config() {
|
||||
fn parses_st_and_star_st_risk_policy_switches_into_platform_config() {
|
||||
let spec = serde_json::json!({
|
||||
"engine_config": {
|
||||
"risk_policy": {
|
||||
@@ -2276,8 +2268,10 @@ mod tests {
|
||||
|
||||
let cfg = platform_expr_config_from_value("", "", &spec).expect("config");
|
||||
|
||||
assert!(cfg.risk_config.static_rules.reject_st_selection);
|
||||
assert!(cfg.risk_config.static_rules.reject_st_buy);
|
||||
assert!(!cfg.risk_config.static_rules.reject_st_selection);
|
||||
assert!(!cfg.risk_config.static_rules.reject_st_buy);
|
||||
assert!(cfg.risk_config.static_rules.reject_star_st_selection);
|
||||
assert!(cfg.risk_config.static_rules.reject_star_st_buy);
|
||||
assert!(!cfg.risk_config.static_rules.reject_paused_selection);
|
||||
assert!(!cfg.risk_config.static_rules.reject_kcb_buy);
|
||||
assert!(!cfg.risk_config.static_rules.reject_bjse_selection);
|
||||
|
||||
@@ -1080,6 +1080,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
@@ -1167,6 +1168,7 @@ mod tests {
|
||||
date,
|
||||
symbol: "000001.SZ".to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3140,6 +3140,7 @@ mod tests {
|
||||
date: *date,
|
||||
symbol: symbol.to_string(),
|
||||
is_st: false,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
@@ -277,7 +277,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
|
||||
ManualField { name: "turnover_ratio/effective_turnover_ratio".to_string(), field_type: "float".to_string(), detail: "换手率标准字段和有效换手率。".to_string() },
|
||||
ManualField { name: "open/high/low/close/last/last_price/prev_close/amount".to_string(), field_type: "float".to_string(), detail: "开盘、最高、最低、收盘、盘中价、昨收和成交额。".to_string() },
|
||||
ManualField { name: "upper_limit/lower_limit/price_tick/round_lot/minimum_order_quantity/order_step_size".to_string(), field_type: "float/int".to_string(), detail: "涨跌停、最小价位、整手、最小下单量和数量步长。KSH/BJSE 等板块可与 round_lot 不同。".to_string() },
|
||||
ManualField { name: "paused/is_st/is_kcb/is_one_yuan/is_new_listing".to_string(), field_type: "bool".to_string(), detail: "可交易性与板块标志。".to_string() },
|
||||
ManualField { name: "paused/is_st/is_star_st/is_kcb/is_one_yuan/is_new_listing".to_string(), field_type: "bool".to_string(), detail: "可交易性与板块标志,ST 与 *ST 是独立字段。".to_string() },
|
||||
ManualField { name: "allow_buy/allow_sell/at_upper_limit/at_lower_limit".to_string(), field_type: "bool".to_string(), detail: "盘中买卖与涨跌停状态。".to_string() },
|
||||
ManualField { name: "touched_upper_limit/touched_lower_limit/hit_upper_limit/hit_lower_limit".to_string(), field_type: "bool".to_string(), detail: "当日分钟执行价曾经触达涨跌停。".to_string() },
|
||||
ManualField { name: "symbol_open_order_count/symbol_open_buy_qty/symbol_open_sell_qty/latest_symbol_open_order_id".to_string(), field_type: "int".to_string(), detail: "当前证券在挂单簿中的未成交挂单摘要和最近挂单 id。".to_string() },
|
||||
|
||||
@@ -347,6 +347,7 @@ mod tests {
|
||||
date: d(),
|
||||
symbol: symbol.to_string(),
|
||||
is_st,
|
||||
is_star_st: false,
|
||||
is_new_listing: false,
|
||||
is_paused: false,
|
||||
allow_buy: true,
|
||||
|
||||
Reference in New Issue
Block a user