隔离退市持仓与模型目标状态
This commit is contained in:
@@ -986,25 +986,6 @@ impl PlatformExprStrategy {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn reserve_unresolved_delisted_slots(
|
||||
ranked_selection: Vec<String>,
|
||||
unresolved_symbols: &BTreeSet<String>,
|
||||
selection_limit: usize,
|
||||
) -> Vec<String> {
|
||||
if selection_limit == 0 || unresolved_symbols.is_empty() {
|
||||
return ranked_selection;
|
||||
}
|
||||
let reserved_count = unresolved_symbols.len().min(selection_limit);
|
||||
let active_limit = selection_limit.saturating_sub(reserved_count);
|
||||
let mut selected = ranked_selection
|
||||
.into_iter()
|
||||
.filter(|symbol| !unresolved_symbols.contains(symbol))
|
||||
.take(active_limit)
|
||||
.collect::<Vec<_>>();
|
||||
selected.extend(unresolved_symbols.iter().take(reserved_count).cloned());
|
||||
selected
|
||||
}
|
||||
|
||||
fn effective_rebalance_cash_mode(&self) -> RebalanceCashMode {
|
||||
if self.config.matching_type == MatchingType::MinuteLast {
|
||||
RebalanceCashMode::SellThenBuy
|
||||
@@ -9262,7 +9243,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
let mut risk_decisions = Vec::new();
|
||||
let mut replacement_reference_source = "disabled";
|
||||
let mut replacement_reference_count = 0usize;
|
||||
let mut stock_list = if self.config.rotation_enabled && !in_skip_window {
|
||||
let stock_list = if self.config.rotation_enabled && !in_skip_window {
|
||||
let selection_buffer_rank =
|
||||
self.selection_candidate_limit(ctx, &day, selection_limit)?;
|
||||
let ranked_selection_limit = if self.config.daily_replacement_limit > 0 {
|
||||
@@ -9319,14 +9300,9 @@ impl Strategy for PlatformExprStrategy {
|
||||
};
|
||||
let unresolved_delisted_symbols = Self::unresolved_delisted_position_symbols(ctx);
|
||||
if !unresolved_delisted_symbols.is_empty() {
|
||||
stock_list = Self::reserve_unresolved_delisted_slots(
|
||||
stock_list,
|
||||
&unresolved_delisted_symbols,
|
||||
selection_limit,
|
||||
);
|
||||
selection_notes.push(format!(
|
||||
"unresolved_delisted_slots reserved={} symbols={} settlement_action=missing valuation_policy=zero no_order=true",
|
||||
unresolved_delisted_symbols.len().min(selection_limit),
|
||||
"unresolved_delisted_actual_slots reserved={} symbols={} settlement_action=missing valuation_policy=zero no_order=true model_targets_unchanged=true",
|
||||
unresolved_delisted_symbols.len(),
|
||||
unresolved_delisted_symbols
|
||||
.iter()
|
||||
.cloned()
|
||||
@@ -11091,40 +11067,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_delisted_position_reserves_target_slot_without_replacement() {
|
||||
let ranked = vec![
|
||||
"000001.SZ".to_string(),
|
||||
"000002.SZ".to_string(),
|
||||
"000003.SZ".to_string(),
|
||||
"000004.SZ".to_string(),
|
||||
];
|
||||
let unresolved = BTreeSet::from(["000999.SZ".to_string()]);
|
||||
|
||||
let selected =
|
||||
PlatformExprStrategy::reserve_unresolved_delisted_slots(ranked, &unresolved, 3);
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
vec![
|
||||
"000001.SZ".to_string(),
|
||||
"000002.SZ".to_string(),
|
||||
"000999.SZ".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_delisted_positions_exhaust_target_slots_before_new_candidates() {
|
||||
let ranked = vec!["000001.SZ".to_string(), "000002.SZ".to_string()];
|
||||
let unresolved = BTreeSet::from(["000998.SZ".to_string(), "000999.SZ".to_string()]);
|
||||
|
||||
let selected =
|
||||
PlatformExprStrategy::reserve_unresolved_delisted_slots(ranked, &unresolved, 1);
|
||||
|
||||
assert_eq!(selected, vec!["000998.SZ".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_rebalance_keeps_unresolved_delisted_position_without_orders_or_replacement() {
|
||||
let previous_date = d(2025, 1, 2);
|
||||
@@ -11282,10 +11224,17 @@ mod tests {
|
||||
let decision = strategy.on_day(&ctx).expect("platform decision");
|
||||
|
||||
assert!(decision.diagnostics.iter().any(|note| {
|
||||
note.contains("unresolved_delisted_slots reserved=1")
|
||||
note.contains("unresolved_delisted_actual_slots reserved=1")
|
||||
&& note.contains(unresolved_symbol)
|
||||
&& note.contains("no_order=true")
|
||||
&& note.contains("model_targets_unchanged=true")
|
||||
}));
|
||||
assert!(
|
||||
decision
|
||||
.diagnostics
|
||||
.iter()
|
||||
.any(|note| note == "selected_symbols=000001.SZ,000002.SZ")
|
||||
);
|
||||
assert!(!decision.order_intents.iter().any(|intent| {
|
||||
matches!(
|
||||
intent,
|
||||
|
||||
Reference in New Issue
Block a user