修正未成交清仓意图持久化
This commit is contained in:
@@ -1444,6 +1444,9 @@ impl PlatformExprStrategy {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let symbol = position.symbol.clone();
|
let symbol = position.symbol.clone();
|
||||||
|
if persistent_model_lifecycle && self.pending_full_close_symbols.contains(&symbol) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
self.position_entry_dates
|
self.position_entry_dates
|
||||||
.entry(symbol.clone())
|
.entry(symbol.clone())
|
||||||
.or_insert(signal_date);
|
.or_insert(signal_date);
|
||||||
@@ -7150,6 +7153,12 @@ impl PlatformExprStrategy {
|
|||||||
if position.quantity == 0 {
|
if position.quantity == 0 {
|
||||||
continue;
|
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) =
|
let (stop_hit, profit_hit) =
|
||||||
self.stop_take_action_for_position(ctx, signal_date, day, position)?;
|
self.stop_take_action_for_position(ctx, signal_date, day, position)?;
|
||||||
if stop_hit || profit_hit {
|
if stop_hit || profit_hit {
|
||||||
@@ -8984,6 +8993,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let execution_date = ctx.execution_date;
|
let execution_date = ctx.execution_date;
|
||||||
let decision_date = ctx.decision_date;
|
let decision_date = ctx.decision_date;
|
||||||
let defer_execution_risk = ctx.is_lagged_execution();
|
let defer_execution_risk = ctx.is_lagged_execution();
|
||||||
|
let persistent_model_lifecycle = self.uses_persistent_model_lifecycle();
|
||||||
let signal_date = decision_date;
|
let signal_date = decision_date;
|
||||||
let projection_date = if defer_execution_risk {
|
let projection_date = if defer_execution_risk {
|
||||||
signal_date
|
signal_date
|
||||||
@@ -9005,7 +9015,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
if self.uses_persistent_model_lifecycle() {
|
if persistent_model_lifecycle {
|
||||||
for symbol in self.position_entry_dates.keys() {
|
for symbol in self.position_entry_dates.keys() {
|
||||||
if ctx
|
if ctx
|
||||||
.portfolio
|
.portfolio
|
||||||
@@ -9092,7 +9102,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.keys()
|
.keys()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
if self.uses_persistent_model_lifecycle() {
|
if persistent_model_lifecycle {
|
||||||
held_symbols.extend(self.position_entry_dates.keys().cloned());
|
held_symbols.extend(self.position_entry_dates.keys().cloned());
|
||||||
}
|
}
|
||||||
let selected = Self::buffered_selection(
|
let selected = Self::buffered_selection(
|
||||||
@@ -9371,7 +9381,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.filter(|symbol| !delayed_sold_symbols.contains(*symbol))
|
.filter(|symbol| !delayed_sold_symbols.contains(*symbol))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
if self.uses_persistent_model_lifecycle() {
|
if persistent_model_lifecycle {
|
||||||
slot_working_symbols.extend(self.position_entry_dates.keys().cloned());
|
slot_working_symbols.extend(self.position_entry_dates.keys().cloned());
|
||||||
}
|
}
|
||||||
let daily_top_up_active = self.config.daily_top_up_enabled
|
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());
|
same_day_sold_symbols.insert(position.symbol.clone());
|
||||||
slot_working_symbols.remove(&position.symbol);
|
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 {
|
if debug_projection {
|
||||||
projection_debug_notes.push(format!(
|
projection_debug_notes.push(format!(
|
||||||
@@ -9530,7 +9542,9 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
{
|
{
|
||||||
same_day_sold_symbols.insert(symbol.clone());
|
same_day_sold_symbols.insert(symbol.clone());
|
||||||
slot_working_symbols.remove(&symbol);
|
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)
|
&& (self.config.target_portfolio_daily_enabled || trading_ratio < 1.0)
|
||||||
&& selection_limit > 0
|
&& selection_limit > 0
|
||||||
&& (!ctx.portfolio.positions().is_empty()
|
&& (!ctx.portfolio.positions().is_empty()
|
||||||
|| (self.uses_persistent_model_lifecycle()
|
|| (persistent_model_lifecycle && !self.position_entry_dates.is_empty()))
|
||||||
&& !self.position_entry_dates.is_empty()))
|
|
||||||
{
|
{
|
||||||
if aiquant_total_value.is_finite() && aiquant_total_value > 0.0 {
|
if aiquant_total_value.is_finite() && aiquant_total_value > 0.0 {
|
||||||
for position in ctx.portfolio.positions().values() {
|
for position in ctx.portfolio.positions().values() {
|
||||||
@@ -9651,9 +9664,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.uses_persistent_model_lifecycle()
|
if persistent_model_lifecycle && self.config.target_portfolio_daily_enabled {
|
||||||
&& self.config.target_portfolio_daily_enabled
|
|
||||||
{
|
|
||||||
let model_only_symbols = self
|
let model_only_symbols = self
|
||||||
.position_entry_dates
|
.position_entry_dates
|
||||||
.keys()
|
.keys()
|
||||||
@@ -9700,6 +9711,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
target_value,
|
target_value,
|
||||||
reason: "model_position_target_retry".to_string(),
|
reason: "model_position_target_retry".to_string(),
|
||||||
});
|
});
|
||||||
|
intraday_attempted_buys.insert(symbol.clone());
|
||||||
if defer_execution_risk {
|
if defer_execution_risk {
|
||||||
deferred_daily_target_values.insert(symbol.clone(), target_value);
|
deferred_daily_target_values.insert(symbol.clone(), target_value);
|
||||||
}
|
}
|
||||||
@@ -9808,13 +9820,19 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
)? {
|
)? {
|
||||||
continue;
|
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 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 is_carried_full_close {
|
||||||
if !stop_hit && !profit_hit {
|
if !stop_hit && !profit_hit {
|
||||||
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());
|
||||||
|
self.pending_full_close_symbols
|
||||||
|
.insert(position.symbol.clone());
|
||||||
exit_symbols.insert(position.symbol.clone());
|
exit_symbols.insert(position.symbol.clone());
|
||||||
if should_block_partial_exit_residual_slot {
|
if should_block_partial_exit_residual_slot {
|
||||||
slot_blocking_symbols.insert(position.symbol.clone());
|
slot_blocking_symbols.insert(position.symbol.clone());
|
||||||
@@ -9841,8 +9859,10 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
slot_working_symbols.remove(&position.symbol);
|
slot_working_symbols.remove(&position.symbol);
|
||||||
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
||||||
same_day_sold_symbols.insert(position.symbol.clone());
|
same_day_sold_symbols.insert(position.symbol.clone());
|
||||||
pending_full_close_symbols.remove(&position.symbol);
|
if !persistent_model_lifecycle {
|
||||||
self.pending_full_close_symbols.remove(&position.symbol);
|
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 {
|
if should_block_partial_exit_residual_slot {
|
||||||
@@ -9893,6 +9913,11 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let can_sell = defer_execution_risk
|
let can_sell = defer_execution_risk
|
||||||
|| self.can_sell_position(ctx, execution_date, &position.symbol);
|
|| self.can_sell_position(ctx, execution_date, &position.symbol);
|
||||||
if stop_hit {
|
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());
|
exit_symbols.insert(position.symbol.clone());
|
||||||
order_intents.push(OrderIntent::TargetValue {
|
order_intents.push(OrderIntent::TargetValue {
|
||||||
symbol: position.symbol.clone(),
|
symbol: position.symbol.clone(),
|
||||||
@@ -9917,8 +9942,10 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if close_submitted {
|
if close_submitted {
|
||||||
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
||||||
same_day_sold_symbols.insert(position.symbol.clone());
|
same_day_sold_symbols.insert(position.symbol.clone());
|
||||||
pending_full_close_symbols.remove(&position.symbol);
|
if !persistent_model_lifecycle {
|
||||||
self.pending_full_close_symbols.remove(&position.symbol);
|
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 {
|
if should_block_partial_exit_residual_slot {
|
||||||
@@ -9967,6 +9994,11 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if profit_hit {
|
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());
|
exit_symbols.insert(position.symbol.clone());
|
||||||
order_intents.push(OrderIntent::TargetValue {
|
order_intents.push(OrderIntent::TargetValue {
|
||||||
symbol: position.symbol.clone(),
|
symbol: position.symbol.clone(),
|
||||||
@@ -9988,8 +10020,10 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if close_submitted {
|
if close_submitted {
|
||||||
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
if Self::projected_position_is_flat(&projected, &position.symbol) {
|
||||||
same_day_sold_symbols.insert(position.symbol.clone());
|
same_day_sold_symbols.insert(position.symbol.clone());
|
||||||
pending_full_close_symbols.remove(&position.symbol);
|
if !persistent_model_lifecycle {
|
||||||
self.pending_full_close_symbols.remove(&position.symbol);
|
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 {
|
if should_block_partial_exit_residual_slot {
|
||||||
@@ -24420,6 +24454,45 @@ mod tests {
|
|||||||
assert!(decision.exit_symbols.contains(symbol), "{decision:?}");
|
assert!(decision.exit_symbols.contains(symbol), "{decision:?}");
|
||||||
assert!(!strategy.position_entry_dates.contains_key(symbol));
|
assert!(!strategy.position_entry_dates.contains_key(symbol));
|
||||||
assert_eq!(strategy.position_holding_days.get(symbol), None);
|
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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user