修正未成交清仓意图持久化
This commit is contained in:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user