修正跌停卖出未成交仓位释放

This commit is contained in:
boris
2026-06-19 14:25:10 +08:00
parent 57aebe97ec
commit c3a5161db1
+37 -65
View File
@@ -1671,27 +1671,6 @@ impl PlatformExprStrategy {
.unwrap_or(true)
}
fn projected_target_zero_would_fill(
&self,
ctx: &StrategyContext<'_>,
projected: &PortfolioState,
date: NaiveDate,
symbol: &str,
execution_state: &ProjectedExecutionState,
) -> bool {
let mut trial_projected = projected.clone();
let mut trial_execution_state = execution_state.clone();
self.project_target_zero(
ctx,
&mut trial_projected,
date,
symbol,
&mut trial_execution_state,
)
.is_some()
&& Self::projected_position_is_flat(&trial_projected, symbol)
}
fn projected_position_value_at_execution_price(
&self,
ctx: &StrategyContext<'_>,
@@ -6693,15 +6672,11 @@ impl Strategy for PlatformExprStrategy {
let (stop_hit, profit_hit) =
self.stop_take_action_for_position(ctx, execution_date, &day, position)?;
if stop_hit {
if self.projected_target_zero_would_fill(
ctx,
&projected,
execution_date,
&position.symbol,
&projected_execution_state,
) {
pending_full_close_symbols.insert(position.symbol.clone());
continue;
}
if profit_hit {
pending_full_close_symbols.insert(position.symbol.clone());
continue;
}
let can_sell = self.can_sell_position(ctx, execution_date, &position.symbol);
@@ -6725,17 +6700,6 @@ impl Strategy for PlatformExprStrategy {
continue;
}
}
if profit_hit {
if self.projected_target_zero_would_fill(
ctx,
&projected,
execution_date,
&position.symbol,
&projected_execution_state,
) {
pending_full_close_symbols.insert(position.symbol.clone());
}
}
}
}
@@ -6834,9 +6798,6 @@ impl Strategy for PlatformExprStrategy {
target_value: 0.0,
reason: "stop_loss_exit".to_string(),
});
if self.config.release_slot_on_exit_signal {
same_day_sold_symbols.insert(position.symbol.clone());
}
if can_sell {
if self
.project_target_zero(
@@ -6885,9 +6846,6 @@ impl Strategy for PlatformExprStrategy {
target_value: 0.0,
reason: "take_profit_exit".to_string(),
});
if self.config.release_slot_on_exit_signal {
same_day_sold_symbols.insert(position.symbol.clone());
}
if can_sell
&& self
.project_target_zero(
@@ -14196,7 +14154,7 @@ mod tests {
}
#[test]
fn platform_weak_market_keeps_positive_adjust_when_stop_loss_sell_blocked() {
fn platform_weak_market_skips_positive_adjust_when_stop_loss_sell_blocked() {
let prev_date = d(2025, 2, 2);
let date = d(2025, 2, 3);
let symbols = ["000001.SZ", "000002.SZ"];
@@ -14319,7 +14277,8 @@ mod tests {
let decision = strategy.on_day(&ctx).expect("platform decision");
assert!(decision.order_intents.iter().any(|intent| matches!(
assert!(
!decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::Shares {
symbol,
@@ -14328,7 +14287,10 @@ mod tests {
} if symbol == "000001.SZ"
&& reason == "daily_position_target_adjust"
&& *quantity > 0
)));
)),
"{:?}",
decision.order_intents
);
assert!(decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::TargetValue {
@@ -14340,7 +14302,7 @@ mod tests {
}
#[test]
fn platform_weak_market_keeps_positive_adjust_when_intraday_stop_loss_sell_blocked() {
fn platform_weak_market_skips_positive_adjust_when_intraday_stop_loss_sell_blocked() {
let prev_date = d(2025, 2, 2);
let date = d(2025, 2, 3);
let symbols = ["000001.SZ", "000002.SZ"];
@@ -14482,7 +14444,8 @@ mod tests {
let decision = strategy.on_day(&ctx).expect("platform decision");
assert!(decision.order_intents.iter().any(|intent| matches!(
assert!(
!decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::Shares {
symbol,
@@ -14491,7 +14454,10 @@ mod tests {
} if symbol == "000001.SZ"
&& reason == "daily_position_target_adjust"
&& *quantity > 0
)));
)),
"{:?}",
decision.order_intents
);
assert!(decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::TargetValue {
@@ -15547,7 +15513,7 @@ mod tests {
}
#[test]
fn platform_daily_top_up_can_release_unsellable_exit_signal_slot() {
fn platform_daily_top_up_does_not_release_unsellable_exit_signal_slot() {
let prev_date = d(2026, 3, 31);
let date = d(2026, 4, 1);
let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"];
@@ -15687,14 +15653,10 @@ mod tests {
decision.order_intents
);
assert!(
decision.order_intents.iter().any(|intent| matches!(
intent,
OrderIntent::Value {
symbol,
reason,
..
} if symbol == "000003.SZ" && reason == "daily_top_up_buy"
)),
!decision
.order_intents
.iter()
.any(|intent| matches!(intent, OrderIntent::Value { .. })),
"{:?}",
decision.order_intents
);
@@ -16695,10 +16657,15 @@ mod tests {
"{:?}",
decision.diagnostics
);
assert!(matches!(
assert!(
matches!(
decision.order_intents.first(),
Some(crate::strategy::OrderIntent::Value { symbol, .. }) if symbol == "300002.SZ"
));
),
"intents={:?} diagnostics={:?}",
decision.order_intents,
decision.diagnostics
);
}
#[test]
@@ -16870,10 +16837,15 @@ mod tests {
"{:?}",
decision.diagnostics
);
assert!(matches!(
assert!(
matches!(
decision.order_intents.first(),
Some(crate::strategy::OrderIntent::Value { symbol, .. }) if symbol == "300002.SZ"
));
Some(crate::strategy::OrderIntent::Value { symbol, .. }) if symbol == "300001.SZ"
),
"intents={:?} diagnostics={:?}",
decision.order_intents,
decision.diagnostics
);
}
#[test]