perf: reuse immutable fundamental universe

This commit is contained in:
boris
2026-08-29 23:05:20 +08:00
parent 42c92fd184
commit 29550dead2
@@ -8982,6 +8982,28 @@ impl PlatformExprStrategy {
selection_risk_deferral: SelectionRiskDeferral,
collect_risk_decisions: bool,
) -> (Vec<EligibleUniverseSnapshot>, Vec<FidcRiskDecisionAudit>) {
// The immutable DataSet already builds the same stable market-cap
// ordered fundamental universe. Reuse it when this strategy has no
// selection-stage risk or explicit universe exclusion. This preserves
// the full path for every strategy that enables a selection rule while
// avoiding a daily scan, candidate/market lookup, and stable sort for
// the common execution-risk-only contract.
if date == factor_date
&& matches!(selection_risk_deferral, SelectionRiskDeferral::None)
&& self.selection_risk_is_noop()
&& self.normalized_universe_exclude.is_empty()
{
let base = ctx.data.eligible_universe_on(date);
let rows = if ctx.has_dynamic_universe() {
base.iter()
.filter(|row| ctx.dynamic_universe_contains(&row.symbol))
.cloned()
.collect()
} else {
base.to_vec()
};
return (rows, Vec::new());
}
let factor_rows = ctx.data.factor_snapshot_rows_on(factor_date);
let mut rows = Vec::with_capacity(factor_rows.len());
let mut decisions = if collect_risk_decisions {
@@ -9160,6 +9182,21 @@ impl PlatformExprStrategy {
None
}
fn selection_risk_is_noop(&self) -> bool {
let rules = &self.config.risk_config.static_rules;
!rules.reject_st_selection
&& !rules.reject_star_st_selection
&& !rules.reject_paused_selection
&& !rules.reject_inactive_selection
&& !rules.reject_new_listing_selection
&& !rules.reject_kcb_selection
&& !rules.reject_bjse_selection
&& !rules.reject_one_yuan_selection
&& !rules.reject_upper_limit_selection
&& !rules.reject_lower_limit_selection
&& (!rules.blacklist_enabled || rules.blacklisted_symbols.is_empty())
}
fn stock_passes_universe_exclude(
&self,
_candidate: &crate::data::CandidateEligibility,
@@ -13414,6 +13451,26 @@ mod tests {
);
}
#[test]
fn selection_universe_cache_gate_only_accepts_pure_fundamental_contracts() {
let strategy =
PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation());
assert!(strategy.selection_risk_is_noop());
let mut config = PlatformExprStrategyConfig::microcap_rotation();
config.risk_config.static_rules.reject_st_selection = true;
assert!(!PlatformExprStrategy::new(config).selection_risk_is_noop());
let mut config = PlatformExprStrategyConfig::microcap_rotation();
config.risk_config.static_rules.blacklisted_symbols.insert("000001.SZ".to_string());
assert!(!PlatformExprStrategy::new(config).selection_risk_is_noop());
let mut config = PlatformExprStrategyConfig::microcap_rotation();
config.risk_config.static_rules.blacklist_enabled = false;
config.risk_config.static_rules.blacklisted_symbols.insert("000001.SZ".to_string());
assert!(PlatformExprStrategy::new(config).selection_risk_is_noop());
}
#[test]
fn platform_selection_uses_complete_static_risk_policy_before_universe_output() {
let date = d(2025, 1, 2);