perf: cache symbol board classification

This commit is contained in:
boris
2026-09-05 03:09:15 +08:00
parent df52f90d46
commit eb8b14602a
3 changed files with 33 additions and 25 deletions
+25
View File
@@ -1326,6 +1326,7 @@ pub struct DataSet {
benchmark_series_cache: Arc<BenchmarkPriceSeries>, benchmark_series_cache: Arc<BenchmarkPriceSeries>,
symbol_id_by_code: Arc<AHashMap<String, u32>>, symbol_id_by_code: Arc<AHashMap<String, u32>>,
symbol_by_id: Arc<Vec<Arc<str>>>, symbol_by_id: Arc<Vec<Arc<str>>>,
symbol_is_bjse_by_id: Arc<Vec<bool>>,
eligible_universe_by_date: Arc<OnceLock<BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>>>, eligible_universe_by_date: Arc<OnceLock<BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>>>,
benchmark_code: String, benchmark_code: String,
futures_params_by_symbol: Arc<HashMap<String, Vec<FuturesTradingParameter>>>, futures_params_by_symbol: Arc<HashMap<String, Vec<FuturesTradingParameter>>>,
@@ -1774,6 +1775,10 @@ impl DataSet {
for (symbol, symbol_id) in &symbol_id_by_code { for (symbol, symbol_id) in &symbol_id_by_code {
symbol_by_id[*symbol_id as usize] = Arc::<str>::from(symbol.as_str()); symbol_by_id[*symbol_id as usize] = Arc::<str>::from(symbol.as_str());
} }
let symbol_is_bjse_by_id = symbol_by_id
.iter()
.map(|symbol| symbol_is_bjse(symbol))
.collect::<Vec<_>>();
let mut instruments_by_symbol_id = vec![None; symbol_id_by_code.len()]; let mut instruments_by_symbol_id = vec![None; symbol_id_by_code.len()];
for (symbol, instrument) in &instruments { for (symbol, instrument) in &instruments {
if let Some(symbol_id) = symbol_id_by_code.get(symbol).copied() { if let Some(symbol_id) = symbol_id_by_code.get(symbol).copied() {
@@ -1863,6 +1868,7 @@ impl DataSet {
benchmark_series_cache: Arc::new(benchmark_series_cache), benchmark_series_cache: Arc::new(benchmark_series_cache),
symbol_id_by_code: Arc::new(symbol_id_by_code), symbol_id_by_code: Arc::new(symbol_id_by_code),
symbol_by_id: Arc::new(symbol_by_id), symbol_by_id: Arc::new(symbol_by_id),
symbol_is_bjse_by_id: Arc::new(symbol_is_bjse_by_id),
eligible_universe_by_date: Arc::new(OnceLock::new()), eligible_universe_by_date: Arc::new(OnceLock::new()),
benchmark_code, benchmark_code,
futures_params_by_symbol: Arc::new(futures_params_by_symbol), futures_params_by_symbol: Arc::new(futures_params_by_symbol),
@@ -1921,6 +1927,13 @@ impl DataSet {
(!symbol.is_empty()).then(|| Arc::clone(symbol)) (!symbol.is_empty()).then(|| Arc::clone(symbol))
} }
pub(crate) fn symbol_is_bjse_by_id(&self, symbol_id: u32) -> bool {
self.symbol_is_bjse_by_id
.get(symbol_id as usize)
.copied()
.unwrap_or(false)
}
pub(crate) fn symbol_count(&self) -> usize { pub(crate) fn symbol_count(&self) -> usize {
self.symbol_id_by_code.len() self.symbol_id_by_code.len()
} }
@@ -4087,6 +4100,18 @@ fn normalized_field(field: &str) -> Cow<'_, str> {
} }
} }
pub(crate) fn symbol_is_bjse(symbol: &str) -> bool {
let normalized = symbol.trim();
normalized
.get(normalized.len().saturating_sub(3)..)
.is_some_and(|suffix| {
suffix.eq_ignore_ascii_case(".BJ") || suffix.eq_ignore_ascii_case(".BE")
})
|| normalized
.get(normalized.len().saturating_sub(4)..)
.is_some_and(|suffix| suffix.eq_ignore_ascii_case(".BSE"))
}
fn normalize_factor_snapshots(factors: Vec<DailyFactorSnapshot>) -> Vec<DailyFactorSnapshot> { fn normalize_factor_snapshots(factors: Vec<DailyFactorSnapshot>) -> Vec<DailyFactorSnapshot> {
factors factors
.into_iter() .into_iter()
+7 -19
View File
@@ -4284,7 +4284,7 @@ impl PlatformExprStrategy {
is_st: candidate.is_st, is_st: candidate.is_st,
is_star_st: candidate.is_star_st, is_star_st: candidate.is_star_st,
is_kcb: candidate.is_kcb, is_kcb: candidate.is_kcb,
is_bjse: Self::symbol_is_bjse(symbol), is_bjse: ctx.data.symbol_is_bjse_by_id(symbol_id),
is_one_yuan: candidate.is_one_yuan || market.day_open <= 1.0, is_one_yuan: candidate.is_one_yuan || market.day_open <= 1.0,
is_new_listing: candidate.is_new_listing, is_new_listing: candidate.is_new_listing,
allow_buy: candidate.allow_buy, allow_buy: candidate.allow_buy,
@@ -9533,18 +9533,6 @@ impl PlatformExprStrategy {
None None
} }
fn symbol_is_bjse(symbol: &str) -> bool {
let normalized = symbol.trim();
normalized
.get(normalized.len().saturating_sub(3)..)
.is_some_and(|suffix| {
suffix.eq_ignore_ascii_case(".BJ") || suffix.eq_ignore_ascii_case(".BE")
})
|| normalized
.get(normalized.len().saturating_sub(4)..)
.is_some_and(|suffix| suffix.eq_ignore_ascii_case(".BSE"))
}
fn stock_numeric_field_value( fn stock_numeric_field_value(
&self, &self,
candidate: &EligibleUniverseSnapshot, candidate: &EligibleUniverseSnapshot,
@@ -12912,12 +12900,12 @@ mod tests {
#[test] #[test]
fn bjse_symbol_detection_is_case_insensitive_without_normalizing_all_symbols() { fn bjse_symbol_detection_is_case_insensitive_without_normalizing_all_symbols() {
assert!(PlatformExprStrategy::symbol_is_bjse("920508.BJ")); assert!(crate::data::symbol_is_bjse("920508.BJ"));
assert!(PlatformExprStrategy::symbol_is_bjse(" 920508.bj ")); assert!(crate::data::symbol_is_bjse(" 920508.bj "));
assert!(PlatformExprStrategy::symbol_is_bjse("430001.BSE")); assert!(crate::data::symbol_is_bjse("430001.BSE"));
assert!(PlatformExprStrategy::symbol_is_bjse("430001.be")); assert!(crate::data::symbol_is_bjse("430001.be"));
assert!(!PlatformExprStrategy::symbol_is_bjse("688001.SH")); assert!(!crate::data::symbol_is_bjse("688001.SH"));
assert!(!PlatformExprStrategy::symbol_is_bjse("BJ")); assert!(!crate::data::symbol_is_bjse("BJ"));
} }
#[test] #[test]
+1 -6
View File
@@ -4,7 +4,7 @@ use chrono::NaiveDate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::OrderSide; use crate::OrderSide;
use crate::data::{CandidateEligibility, DailyMarketSnapshot, PriceField}; use crate::data::{CandidateEligibility, DailyMarketSnapshot, PriceField, symbol_is_bjse};
use crate::instrument::Instrument; use crate::instrument::Instrument;
use crate::portfolio::Position; use crate::portfolio::Position;
@@ -602,11 +602,6 @@ fn symbol_is_kcb(symbol: &str) -> bool {
(normalized.starts_with("688") || normalized.starts_with("689")) && normalized.ends_with(".SH") (normalized.starts_with("688") || normalized.starts_with("689")) && normalized.ends_with(".SH")
} }
fn symbol_is_bjse(symbol: &str) -> bool {
let normalized = symbol.trim().to_ascii_uppercase();
normalized.ends_with(".BJ") || normalized.ends_with(".BSE") || normalized.ends_with(".BE")
}
fn candidate_active_status_rejection( fn candidate_active_status_rejection(
candidate: &CandidateEligibility, candidate: &CandidateEligibility,
config: &FidcRiskControlConfig, config: &FidcRiskControlConfig,