修正模型排名缺失诊断
This commit is contained in:
@@ -8040,6 +8040,38 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
||||
if !rank_value.is_finite() {
|
||||
// Model-score artifacts intentionally contain only the PIT-eligible
|
||||
// ranked universe. Do not report a missing score for a symbol that
|
||||
// the same signal-day business filter would reject before ranking.
|
||||
if !ctx.is_lagged_execution()
|
||||
&& let Some(reason) = self.stock_selection_limit_rejection_reason(&stock)
|
||||
{
|
||||
if diagnostics.len() < 12 {
|
||||
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if !self.stock_passes_expr(ctx, day, &stock)? {
|
||||
if diagnostics.len() < 12 {
|
||||
diagnostics.push(format!("{} rejected by stock_expr", candidate.symbol));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if self.config.stop_take_reference_price_mode
|
||||
== PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose
|
||||
&& ctx
|
||||
.data
|
||||
.market_latest_back_adjusted_close(date, &candidate.symbol)
|
||||
.is_none()
|
||||
{
|
||||
if diagnostics.len() < 12 {
|
||||
diagnostics.push(format!(
|
||||
"{} rejected by missing signal-day post-adjusted close",
|
||||
candidate.symbol
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
missing_rank_count += 1;
|
||||
if missing_rank_examples.len() < 5 {
|
||||
missing_rank_examples.push(candidate.symbol.clone());
|
||||
@@ -8754,6 +8786,18 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
||||
if !rank_value.is_finite() {
|
||||
// The score universe is already filtered by the signal-day
|
||||
// candidate contract. Missing rank is actionable only when the
|
||||
// daily portion of that same contract still admits the symbol.
|
||||
if !self.stock_passes_quote_plan_filter(ctx, day, &stock, quote_usage)? {
|
||||
if diagnostics.len() < 12 {
|
||||
diagnostics.push(format!(
|
||||
"{} quote_plan rejected by stock_expr",
|
||||
candidate.symbol
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
missing_rank_count += 1;
|
||||
if missing_rank_examples.len() < 5 {
|
||||
missing_rank_examples.push(candidate.symbol.clone());
|
||||
@@ -31687,7 +31731,11 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::new(),
|
||||
extra_factors: if *symbol == "000002.SZ" {
|
||||
BTreeMap::new()
|
||||
} else {
|
||||
BTreeMap::from([("model_score".to_string(), 4.0 - index as f64)])
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
symbols
|
||||
@@ -31736,14 +31784,16 @@ mod tests {
|
||||
};
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.signal_symbol = "000001.SZ".to_string();
|
||||
cfg.max_positions = 2;
|
||||
cfg.max_positions = 3;
|
||||
cfg.benchmark_short_ma_days = 1;
|
||||
cfg.benchmark_long_ma_days = 1;
|
||||
cfg.market_cap_lower_expr = "0".to_string();
|
||||
cfg.market_cap_upper_expr = "100".to_string();
|
||||
cfg.selection_limit_expr = "2".to_string();
|
||||
cfg.selection_limit_expr = "3".to_string();
|
||||
cfg.stock_filter_expr =
|
||||
"prev_close > 5.0 && !at_upper_limit && !at_lower_limit".to_string();
|
||||
cfg.rank_by = "model_score".to_string();
|
||||
cfg.rank_desc = true;
|
||||
cfg.aiquant_transaction_cost = true;
|
||||
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
@@ -31770,6 +31820,27 @@ mod tests {
|
||||
],
|
||||
"quote plan may prefetch a superset; final selection/order risk is checked in the decision path"
|
||||
);
|
||||
assert!(
|
||||
plan.diagnostics
|
||||
.iter()
|
||||
.all(|item| !item.contains("rank_field_unavailable")),
|
||||
"a symbol rejected by the daily candidate filter must not be reported as missing model score"
|
||||
);
|
||||
|
||||
let day = strategy.day_state(&ctx, date).expect("day state");
|
||||
let (selected, diagnostics, _) = strategy
|
||||
.select_symbols(&ctx, date, date, date, &day, 0.0, 100.0, 3)
|
||||
.expect("selection");
|
||||
assert_eq!(
|
||||
selected,
|
||||
vec!["000003.SZ".to_string(), "000004.SZ".to_string()]
|
||||
);
|
||||
assert!(
|
||||
diagnostics
|
||||
.iter()
|
||||
.all(|item| !item.contains("rank_field_unavailable")),
|
||||
"the final selector must apply the same missing-score classification"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user