修正延迟卖出意图的持仓槽位
This commit is contained in:
@@ -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());
|
||||||
slot_blocking_symbols.insert(position.symbol.clone());
|
if should_block_partial_exit_residual_slot {
|
||||||
|
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user