diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index df391f2..e3d53ab 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -2214,14 +2214,19 @@ impl PlatformExprStrategy { working_symbols: &BTreeSet, value_symbols: &BTreeSet, pending_buy_value: f64, + slot_blocking_symbols: &BTreeSet, ) -> f64 { if selection_limit == 0 || !target_budget.is_finite() || target_budget <= 0.0 { return 0.0; } - if working_symbols.len() >= selection_limit { + let effective_working_count = + Self::effective_slot_count(working_symbols, slot_blocking_symbols); + if effective_working_count >= selection_limit { return 0.0; } - let slots_remaining = selection_limit.saturating_sub(working_symbols.len()).max(1); + let slots_remaining = selection_limit + .saturating_sub(effective_working_count) + .max(1); let active_value = self.remaining_buy_cash_active_value( ctx, projected, @@ -2235,6 +2240,13 @@ impl PlatformExprStrategy { per_slot_budget.min(single_slot_cap) } + fn effective_slot_count( + working_symbols: &BTreeSet, + slot_blocking_symbols: &BTreeSet, + ) -> usize { + working_symbols.union(slot_blocking_symbols).count() + } + fn remaining_buy_cash_active_value( &self, ctx: &StrategyContext<'_>, @@ -8483,7 +8495,7 @@ impl Strategy for PlatformExprStrategy { self.forget_position_entry_date(&position.symbol); slot_working_symbols.remove(&position.symbol); if can_sell { - if self + let close_submitted = self .project_target_zero( ctx, &mut projected, @@ -8491,11 +8503,17 @@ impl Strategy for PlatformExprStrategy { &position.symbol, &mut projected_execution_state, ) - .is_some() - && Self::projected_position_is_flat(&projected, &position.symbol) - { - same_day_sold_symbols.insert(position.symbol.clone()); - self.pending_full_close_symbols.remove(&position.symbol); + .is_some(); + if close_submitted { + if Self::projected_position_is_flat(&projected, &position.symbol) { + same_day_sold_symbols.insert(position.symbol.clone()); + pending_full_close_symbols.remove(&position.symbol); + self.pending_full_close_symbols.remove(&position.symbol); + } else { + pending_full_close_symbols.insert(position.symbol.clone()); + self.pending_full_close_symbols + .insert(position.symbol.clone()); + } } } else { unresolved_stop_loss_symbols.insert(position.symbol.clone()); @@ -8512,8 +8530,8 @@ impl Strategy for PlatformExprStrategy { }); self.forget_position_entry_date(&position.symbol); slot_working_symbols.remove(&position.symbol); - if can_sell - && self + if can_sell { + let close_submitted = self .project_target_zero( ctx, &mut projected, @@ -8521,11 +8539,18 @@ impl Strategy for PlatformExprStrategy { &position.symbol, &mut projected_execution_state, ) - .is_some() - && Self::projected_position_is_flat(&projected, &position.symbol) - { - same_day_sold_symbols.insert(position.symbol.clone()); - self.pending_full_close_symbols.remove(&position.symbol); + .is_some(); + if close_submitted { + if Self::projected_position_is_flat(&projected, &position.symbol) { + same_day_sold_symbols.insert(position.symbol.clone()); + pending_full_close_symbols.remove(&position.symbol); + self.pending_full_close_symbols.remove(&position.symbol); + } else { + pending_full_close_symbols.insert(position.symbol.clone()); + self.pending_full_close_symbols + .insert(position.symbol.clone()); + } + } } } } @@ -8546,7 +8571,9 @@ impl Strategy for PlatformExprStrategy { let value_symbols = working_symbols.clone(); let mut pending_buy_value = 0.0_f64; loop { - if working_symbols.len() >= selection_limit { + if Self::effective_slot_count(&working_symbols, &pending_full_close_symbols) + >= selection_limit + { break; } let active_value = if debug_daily_top_up { @@ -8569,6 +8596,7 @@ impl Strategy for PlatformExprStrategy { &working_symbols, &value_symbols, pending_buy_value, + &pending_full_close_symbols, ); let available_buy_cash = slot_buy_cash.min(aiquant_available_cash); if debug_daily_top_up { @@ -8834,6 +8862,7 @@ impl Strategy for PlatformExprStrategy { &rebalance_working_symbols, &rebalance_value_symbols, rebalance_pending_buy_value, + &pending_full_close_symbols, ); slot_buy_cash * stock_scale }; @@ -19760,6 +19789,7 @@ mod tests { &working_symbols, &value_symbols, 0.0, + &BTreeSet::new(), ); assert!(