修正延迟卖出意图的持仓槽位

This commit is contained in:
boris
2026-07-17 12:54:33 +08:00
parent 81ac623fca
commit 4d7245d8b0
+24 -4
View File
@@ -2830,6 +2830,13 @@ impl PlatformExprStrategy {
working_symbols.union(slot_blocking_symbols).count() working_symbols.union(slot_blocking_symbols).count()
} }
fn should_block_partial_exit_residual_slot(
trading_ratio: f64,
defer_execution_risk: bool,
) -> bool {
trading_ratio < 1.0 && !defer_execution_risk
}
fn remaining_buy_cash_active_value( fn remaining_buy_cash_active_value(
&self, &self,
ctx: &StrategyContext<'_>, ctx: &StrategyContext<'_>,
@@ -9276,7 +9283,11 @@ impl Strategy for PlatformExprStrategy {
let mut deferred_daily_target_values = BTreeMap::<String, f64>::new(); let mut deferred_daily_target_values = BTreeMap::<String, f64>::new();
let mut pending_full_close_symbols = BTreeSet::<String>::new(); let mut pending_full_close_symbols = BTreeSet::<String>::new();
let mut slot_blocking_symbols = BTreeSet::<String>::new(); let mut slot_blocking_symbols = BTreeSet::<String>::new();
let should_block_partial_exit_residual_slot = trading_ratio < 1.0; // Lagged execution only freezes the T-day intent. Whether an exit leaves a
// residual position is an execution-day fact and must not consume a T+1
// replacement slot before the sell leg is actually matched.
let should_block_partial_exit_residual_slot =
Self::should_block_partial_exit_residual_slot(trading_ratio, defer_execution_risk);
let interleaved_pending_full_close_symbols = BTreeSet::<String>::new(); let interleaved_pending_full_close_symbols = BTreeSet::<String>::new();
let risk_level_forced_exit_time = self.risk_level_forced_exit_time(); let risk_level_forced_exit_time = self.risk_level_forced_exit_time();
self.pending_full_close_symbols.retain(|symbol| { self.pending_full_close_symbols.retain(|symbol| {
@@ -9552,7 +9563,7 @@ impl Strategy for PlatformExprStrategy {
target_value: 0.0, target_value: 0.0,
reason: "pending_full_close_exit".to_string(), reason: "pending_full_close_exit".to_string(),
}); });
if daily_top_up_pending_buy_value > 0.0 { if daily_top_up_pending_buy_value > 0.0 && should_block_partial_exit_residual_slot {
slot_blocking_symbols.insert(position.symbol.clone()); slot_blocking_symbols.insert(position.symbol.clone());
} }
self.forget_position_entry_date(&position.symbol); self.forget_position_entry_date(&position.symbol);
@@ -9576,7 +9587,9 @@ impl Strategy for PlatformExprStrategy {
self.pending_full_close_symbols.remove(&position.symbol); self.pending_full_close_symbols.remove(&position.symbol);
} else { } else {
pending_full_close_symbols.insert(position.symbol.clone()); pending_full_close_symbols.insert(position.symbol.clone());
if should_block_partial_exit_residual_slot {
slot_blocking_symbols.insert(position.symbol.clone()); slot_blocking_symbols.insert(position.symbol.clone());
}
self.pending_full_close_symbols self.pending_full_close_symbols
.insert(position.symbol.clone()); .insert(position.symbol.clone());
} }
@@ -9643,7 +9656,7 @@ impl Strategy for PlatformExprStrategy {
self.pending_full_close_symbols.remove(&position.symbol); self.pending_full_close_symbols.remove(&position.symbol);
} else { } else {
exit_symbols.insert(position.symbol.clone()); exit_symbols.insert(position.symbol.clone());
if trading_ratio < 1.0 { if should_block_partial_exit_residual_slot {
slot_blocking_symbols.insert(position.symbol.clone()); slot_blocking_symbols.insert(position.symbol.clone());
} }
order_intents.push(OrderIntent::TargetValue { order_intents.push(OrderIntent::TargetValue {
@@ -9726,7 +9739,7 @@ impl Strategy for PlatformExprStrategy {
target_value: 0.0, target_value: 0.0,
reason: "stop_loss_exit".to_string(), reason: "stop_loss_exit".to_string(),
}); });
if daily_top_up_pending_buy_value > 0.0 && trading_ratio < 1.0 { if daily_top_up_pending_buy_value > 0.0 && should_block_partial_exit_residual_slot {
slot_blocking_symbols.insert(position.symbol.clone()); slot_blocking_symbols.insert(position.symbol.clone());
} }
self.forget_position_entry_date(&position.symbol); self.forget_position_entry_date(&position.symbol);
@@ -21900,6 +21913,13 @@ mod tests {
); );
} }
#[test]
fn platform_lagged_exit_does_not_reserve_replacement_slot_before_execution() {
assert!(PlatformExprStrategy::should_block_partial_exit_residual_slot(0.5, false));
assert!(!PlatformExprStrategy::should_block_partial_exit_residual_slot(0.5, true));
assert!(!PlatformExprStrategy::should_block_partial_exit_residual_slot(1.0, false));
}
#[test] #[test]
fn platform_aiquant_daily_top_up_uses_remaining_slot_budget() { fn platform_aiquant_daily_top_up_uses_remaining_slot_budget() {
let prev_date = d(2025, 2, 2); let prev_date = d(2025, 2, 2);