修正模型排名缺失诊断
This commit is contained in:
@@ -8040,6 +8040,38 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
||||||
if !rank_value.is_finite() {
|
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;
|
missing_rank_count += 1;
|
||||||
if missing_rank_examples.len() < 5 {
|
if missing_rank_examples.len() < 5 {
|
||||||
missing_rank_examples.push(candidate.symbol.clone());
|
missing_rank_examples.push(candidate.symbol.clone());
|
||||||
@@ -8754,6 +8786,18 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
let rank_value = self.rank_value(ctx, day, &candidate, &stock)?;
|
||||||
if !rank_value.is_finite() {
|
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;
|
missing_rank_count += 1;
|
||||||
if missing_rank_examples.len() < 5 {
|
if missing_rank_examples.len() < 5 {
|
||||||
missing_rank_examples.push(candidate.symbol.clone());
|
missing_rank_examples.push(candidate.symbol.clone());
|
||||||
@@ -31687,7 +31731,11 @@ mod tests {
|
|||||||
pe_ttm: 8.0,
|
pe_ttm: 8.0,
|
||||||
turnover_ratio: Some(1.0),
|
turnover_ratio: Some(1.0),
|
||||||
effective_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(),
|
.collect(),
|
||||||
symbols
|
symbols
|
||||||
@@ -31736,14 +31784,16 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
cfg.signal_symbol = "000001.SZ".to_string();
|
cfg.signal_symbol = "000001.SZ".to_string();
|
||||||
cfg.max_positions = 2;
|
cfg.max_positions = 3;
|
||||||
cfg.benchmark_short_ma_days = 1;
|
cfg.benchmark_short_ma_days = 1;
|
||||||
cfg.benchmark_long_ma_days = 1;
|
cfg.benchmark_long_ma_days = 1;
|
||||||
cfg.market_cap_lower_expr = "0".to_string();
|
cfg.market_cap_lower_expr = "0".to_string();
|
||||||
cfg.market_cap_upper_expr = "100".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 =
|
cfg.stock_filter_expr =
|
||||||
"prev_close > 5.0 && !at_upper_limit && !at_lower_limit".to_string();
|
"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.aiquant_transaction_cost = true;
|
||||||
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
|
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
|
||||||
let strategy = PlatformExprStrategy::new(cfg);
|
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"
|
"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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user