修正未成交清仓意图持久化

This commit is contained in:
boris
2026-07-17 13:34:54 +08:00
parent 5918a03456
commit 7f65fda790
+91 -18
View File
@@ -1444,6 +1444,9 @@ impl PlatformExprStrategy {
continue;
}
let symbol = position.symbol.clone();
if persistent_model_lifecycle && self.pending_full_close_symbols.contains(&symbol) {
continue;
}
self.position_entry_dates
.entry(symbol.clone())
.or_insert(signal_date);
@@ -7150,6 +7153,12 @@ impl PlatformExprStrategy {
if position.quantity == 0 {
continue;
}
if self.uses_persistent_model_lifecycle()
&& self.pending_full_close_symbols.contains(&position.symbol)
{
symbols.insert(position.symbol.clone());
continue;
}
let (stop_hit, profit_hit) =
self.stop_take_action_for_position(ctx, signal_date, day, position)?;
if stop_hit || profit_hit {
@@ -8984,6 +8993,7 @@ impl Strategy for PlatformExprStrategy {
let execution_date = ctx.execution_date;
let decision_date = ctx.decision_date;
let defer_execution_risk = ctx.is_lagged_execution();
let persistent_model_lifecycle = self.uses_persistent_model_lifecycle();
let signal_date = decision_date;
let projection_date = if defer_execution_risk {
signal_date
@@ -9005,7 +9015,7 @@ impl Strategy for PlatformExprStrategy {
})
.cloned()
.collect::<BTreeSet<_>>();
if self.uses_persistent_model_lifecycle() {
if persistent_model_lifecycle {
for symbol in self.position_entry_dates.keys() {
if ctx
.portfolio
@@ -9092,7 +9102,7 @@ impl Strategy for PlatformExprStrategy {
.keys()
.cloned()
.collect::<BTreeSet<_>>();
if self.uses_persistent_model_lifecycle() {
if persistent_model_lifecycle {
held_symbols.extend(self.position_entry_dates.keys().cloned());
}
let selected = Self::buffered_selection(
@@ -9371,7 +9381,7 @@ impl Strategy for PlatformExprStrategy {
.filter(|symbol| !delayed_sold_symbols.contains(*symbol))
.cloned()
.collect::<BTreeSet<_>>();
if self.uses_persistent_model_lifecycle() {
if persistent_model_lifecycle {
slot_working_symbols.extend(self.position_entry_dates.keys().cloned());
}
let daily_top_up_active = self.config.daily_top_up_enabled
@@ -9437,7 +9447,9 @@ impl Strategy for PlatformExprStrategy {
{
same_day_sold_symbols.insert(position.symbol.clone());
slot_working_symbols.remove(&position.symbol);
self.pending_full_close_symbols.remove(&position.symbol);
if !persistent_model_lifecycle {
self.pending_full_close_symbols.remove(&position.symbol);
}
}
if debug_projection {
projection_debug_notes.push(format!(
@@ -9530,7 +9542,9 @@ impl Strategy for PlatformExprStrategy {
{
same_day_sold_symbols.insert(symbol.clone());
slot_working_symbols.remove(&symbol);
self.pending_full_close_symbols.remove(&symbol);
if !persistent_model_lifecycle {
self.pending_full_close_symbols.remove(&symbol);
}
}
}
@@ -9543,8 +9557,7 @@ impl Strategy for PlatformExprStrategy {
&& (self.config.target_portfolio_daily_enabled || trading_ratio < 1.0)
&& selection_limit > 0
&& (!ctx.portfolio.positions().is_empty()
|| (self.uses_persistent_model_lifecycle()
&& !self.position_entry_dates.is_empty()))
|| (persistent_model_lifecycle && !self.position_entry_dates.is_empty()))
{
if aiquant_total_value.is_finite() && aiquant_total_value > 0.0 {
for position in ctx.portfolio.positions().values() {
@@ -9651,9 +9664,7 @@ impl Strategy for PlatformExprStrategy {
}
}
}
if self.uses_persistent_model_lifecycle()
&& self.config.target_portfolio_daily_enabled
{
if persistent_model_lifecycle && self.config.target_portfolio_daily_enabled {
let model_only_symbols = self
.position_entry_dates
.keys()
@@ -9700,6 +9711,7 @@ impl Strategy for PlatformExprStrategy {
target_value,
reason: "model_position_target_retry".to_string(),
});
intraday_attempted_buys.insert(symbol.clone());
if defer_execution_risk {
deferred_daily_target_values.insert(symbol.clone(), target_value);
}
@@ -9808,13 +9820,19 @@ impl Strategy for PlatformExprStrategy {
)? {
continue;
}
let (stop_hit, profit_hit) =
self.stop_take_action_for_position(ctx, signal_date, &day, projected_position)?;
let is_carried_full_close = carried_full_close_symbols.contains(&position.symbol);
let (stop_hit, profit_hit) = if is_carried_full_close && persistent_model_lifecycle {
(true, false)
} else {
self.stop_take_action_for_position(ctx, signal_date, &day, projected_position)?
};
if is_carried_full_close {
if !stop_hit && !profit_hit {
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());
exit_symbols.insert(position.symbol.clone());
if should_block_partial_exit_residual_slot {
slot_blocking_symbols.insert(position.symbol.clone());
@@ -9841,8 +9859,10 @@ impl Strategy for PlatformExprStrategy {
slot_working_symbols.remove(&position.symbol);
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);
if !persistent_model_lifecycle {
pending_full_close_symbols.remove(&position.symbol);
self.pending_full_close_symbols.remove(&position.symbol);
}
} else {
pending_full_close_symbols.insert(position.symbol.clone());
if should_block_partial_exit_residual_slot {
@@ -9893,6 +9913,11 @@ impl Strategy for PlatformExprStrategy {
let can_sell = defer_execution_risk
|| self.can_sell_position(ctx, execution_date, &position.symbol);
if stop_hit {
if persistent_model_lifecycle {
pending_full_close_symbols.insert(position.symbol.clone());
self.pending_full_close_symbols
.insert(position.symbol.clone());
}
exit_symbols.insert(position.symbol.clone());
order_intents.push(OrderIntent::TargetValue {
symbol: position.symbol.clone(),
@@ -9917,8 +9942,10 @@ impl Strategy for PlatformExprStrategy {
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);
if !persistent_model_lifecycle {
pending_full_close_symbols.remove(&position.symbol);
self.pending_full_close_symbols.remove(&position.symbol);
}
} else {
pending_full_close_symbols.insert(position.symbol.clone());
if should_block_partial_exit_residual_slot {
@@ -9967,6 +9994,11 @@ impl Strategy for PlatformExprStrategy {
}
if profit_hit {
if persistent_model_lifecycle {
pending_full_close_symbols.insert(position.symbol.clone());
self.pending_full_close_symbols
.insert(position.symbol.clone());
}
exit_symbols.insert(position.symbol.clone());
order_intents.push(OrderIntent::TargetValue {
symbol: position.symbol.clone(),
@@ -9988,8 +10020,10 @@ impl Strategy for PlatformExprStrategy {
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);
if !persistent_model_lifecycle {
pending_full_close_symbols.remove(&position.symbol);
self.pending_full_close_symbols.remove(&position.symbol);
}
} else {
pending_full_close_symbols.insert(position.symbol.clone());
if should_block_partial_exit_residual_slot {
@@ -24420,6 +24454,45 @@ mod tests {
assert!(decision.exit_symbols.contains(symbol), "{decision:?}");
assert!(!strategy.position_entry_dates.contains_key(symbol));
assert_eq!(strategy.position_holding_days.get(symbol), None);
strategy
.pending_full_close_symbols
.insert(symbol.to_string());
let mut held_portfolio = PortfolioState::new(10_000_000.0);
held_portfolio
.position_mut(symbol)
.buy(entry_date, 1_000, 10.0);
let carried_ctx = StrategyContext {
execution_date: d(2024, 1, 23),
decision_date: signal_date,
decision_index: 2,
data: &data,
portfolio: &held_portfolio,
futures_account: None,
open_orders: &[],
dynamic_universe: None,
subscriptions: &subscriptions,
process_events: &[],
active_process_event: None,
active_datetime: None,
order_events: &[],
fills: &[],
};
let carried_decision = strategy.on_day(&carried_ctx).expect("carried decision");
assert!(carried_decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::TargetValue {
symbol: intent_symbol,
target_value,
reason,
} if intent_symbol == symbol
&& *target_value == 0.0
&& reason == "pending_full_close_exit"
)));
assert!(strategy.pending_full_close_symbols.contains(symbol));
assert!(!strategy.position_entry_dates.contains_key(symbol));
}
#[test]