优化等价市值排序的选股路径

This commit is contained in:
boris
2026-08-29 06:01:02 +08:00
parent 257088d7d9
commit c55caaf79b
+51 -18
View File
@@ -9219,6 +9219,15 @@ impl PlatformExprStrategy {
.unwrap_or_else(|| self.field_value(candidate)))
}
fn rank_reuses_market_cap_order(&self) -> bool {
self.config.rank_expr.trim().is_empty()
&& !self.config.rank_desc
&& matches!(
self.config.rank_by.trim(),
"market_cap" | "market_cap_bn"
)
}
fn can_sell_position(&self, ctx: &StrategyContext<'_>, date: NaiveDate, symbol: &str) -> bool {
self.can_sell_position_at_time(ctx, date, symbol, None)
}
@@ -9429,24 +9438,26 @@ impl PlatformExprStrategy {
}
candidates.push((candidate, stock, rank_value));
}
candidates.sort_by(|lhs, rhs| {
let lhs_value = lhs.2;
let rhs_value = rhs.2;
let ordering = if self.config.rank_desc {
rhs_value
.partial_cmp(&lhs_value)
.unwrap_or(std::cmp::Ordering::Equal)
} else {
lhs_value
.partial_cmp(&rhs_value)
.unwrap_or(std::cmp::Ordering::Equal)
};
if ordering == std::cmp::Ordering::Equal {
lhs.0.symbol.cmp(&rhs.0.symbol)
} else {
ordering
}
});
if !self.rank_reuses_market_cap_order() {
candidates.sort_by(|lhs, rhs| {
let lhs_value = lhs.2;
let rhs_value = rhs.2;
let ordering = if self.config.rank_desc {
rhs_value
.partial_cmp(&lhs_value)
.unwrap_or(std::cmp::Ordering::Equal)
} else {
lhs_value
.partial_cmp(&rhs_value)
.unwrap_or(std::cmp::Ordering::Equal)
};
if ordering == std::cmp::Ordering::Equal {
lhs.0.symbol.cmp(&rhs.0.symbol)
} else {
ordering
}
});
}
let mut selected = Vec::new();
for (candidate, stock, _) in candidates {
@@ -13749,6 +13760,28 @@ mod tests {
);
}
#[test]
fn market_cap_ascending_rank_reuses_only_equivalent_universe_order() {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
cfg.rank_by = "market_cap".to_string();
let strategy = PlatformExprStrategy::new(cfg.clone());
assert!(strategy.rank_reuses_market_cap_order());
cfg.rank_by = "market_cap_bn".to_string();
assert!(PlatformExprStrategy::new(cfg.clone()).rank_reuses_market_cap_order());
cfg.rank_desc = true;
assert!(!PlatformExprStrategy::new(cfg.clone()).rank_reuses_market_cap_order());
cfg.rank_desc = false;
cfg.rank_expr = "close".to_string();
assert!(!PlatformExprStrategy::new(cfg.clone()).rank_reuses_market_cap_order());
cfg.rank_expr.clear();
cfg.rank_by = "free_float_cap".to_string();
assert!(!PlatformExprStrategy::new(cfg).rank_reuses_market_cap_order());
}
#[test]
fn current_rolling_helpers_do_not_load_factor_maps_or_decision_rollings() {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();