修正延迟卖出意图的持仓槽位
This commit is contained in:
@@ -2830,6 +2830,13 @@ impl PlatformExprStrategy {
|
||||
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(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
@@ -9276,7 +9283,11 @@ impl Strategy for PlatformExprStrategy {
|
||||
let mut deferred_daily_target_values = BTreeMap::<String, f64>::new();
|
||||
let mut pending_full_close_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 risk_level_forced_exit_time = self.risk_level_forced_exit_time();
|
||||
self.pending_full_close_symbols.retain(|symbol| {
|
||||
@@ -9552,7 +9563,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
target_value: 0.0,
|
||||
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());
|
||||
}
|
||||
self.forget_position_entry_date(&position.symbol);
|
||||
@@ -9576,7 +9587,9 @@ impl Strategy for PlatformExprStrategy {
|
||||
self.pending_full_close_symbols.remove(&position.symbol);
|
||||
} else {
|
||||
pending_full_close_symbols.insert(position.symbol.clone());
|
||||
if should_block_partial_exit_residual_slot {
|
||||
slot_blocking_symbols.insert(position.symbol.clone());
|
||||
}
|
||||
self.pending_full_close_symbols
|
||||
.insert(position.symbol.clone());
|
||||
}
|
||||
@@ -9643,7 +9656,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
self.pending_full_close_symbols.remove(&position.symbol);
|
||||
} else {
|
||||
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());
|
||||
}
|
||||
order_intents.push(OrderIntent::TargetValue {
|
||||
@@ -9726,7 +9739,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
target_value: 0.0,
|
||||
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());
|
||||
}
|
||||
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]
|
||||
fn platform_aiquant_daily_top_up_uses_remaining_slot_budget() {
|
||||
let prev_date = d(2025, 2, 2);
|
||||
|
||||
Reference in New Issue
Block a user