修正同批卖出资金复用

This commit is contained in:
boris
2026-07-17 21:46:23 +08:00
parent 6e8eeb984f
commit b6df63c79e
+62 -21
View File
@@ -958,6 +958,16 @@ impl PlatformExprStrategy {
} }
} }
fn refresh_available_cash_after_projected_sell(
&self,
available_cash: &mut f64,
projected: &PortfolioState,
) {
if self.effective_rebalance_cash_mode() != RebalanceCashMode::PreOpenCash {
*available_cash = projected.cash().max(0.0);
}
}
pub fn new(config: PlatformExprStrategyConfig) -> Self { pub fn new(config: PlatformExprStrategyConfig) -> Self {
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_fn("round", |value: f64| value.round()); engine.register_fn("round", |value: f64| value.round());
@@ -9515,8 +9525,8 @@ impl Strategy for PlatformExprStrategy {
self.forget_position_entry_date(&position.symbol); self.forget_position_entry_date(&position.symbol);
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 can_sell if can_sell {
&& self let close_submitted = self
.project_target_zero( .project_target_zero(
ctx, ctx,
&mut projected, &mut projected,
@@ -9524,13 +9534,19 @@ impl Strategy for PlatformExprStrategy {
&position.symbol, &position.symbol,
&mut projected_execution_state, &mut projected_execution_state,
) )
.is_some() .is_some();
&& Self::projected_position_is_flat(&projected, &position.symbol) if close_submitted {
{ self.refresh_available_cash_after_projected_sell(
same_day_sold_symbols.insert(position.symbol.clone()); &mut aiquant_available_cash,
slot_working_symbols.remove(&position.symbol); &projected,
if !persistent_model_lifecycle { );
self.pending_full_close_symbols.remove(&position.symbol); if Self::projected_position_is_flat(&projected, &position.symbol) {
same_day_sold_symbols.insert(position.symbol.clone());
slot_working_symbols.remove(&position.symbol);
if !persistent_model_lifecycle {
self.pending_full_close_symbols.remove(&position.symbol);
}
}
} }
} }
if debug_projection { if debug_projection {
@@ -9610,7 +9626,7 @@ impl Strategy for PlatformExprStrategy {
reason: "risk_forced_exit".to_string(), reason: "risk_forced_exit".to_string(),
}); });
self.forget_position_entry_date(&symbol); self.forget_position_entry_date(&symbol);
if self let close_submitted = self
.project_target_zero_at_time( .project_target_zero_at_time(
ctx, ctx,
&mut projected, &mut projected,
@@ -9619,13 +9635,18 @@ impl Strategy for PlatformExprStrategy {
&mut projected_execution_state, &mut projected_execution_state,
Some(risk_level_forced_exit_time), Some(risk_level_forced_exit_time),
) )
.is_some() .is_some();
&& Self::projected_position_is_flat(&projected, &symbol) if close_submitted {
{ self.refresh_available_cash_after_projected_sell(
same_day_sold_symbols.insert(symbol.clone()); &mut aiquant_available_cash,
slot_working_symbols.remove(&symbol); &projected,
if !persistent_model_lifecycle { );
self.pending_full_close_symbols.remove(&symbol); if Self::projected_position_is_flat(&projected, &symbol) {
same_day_sold_symbols.insert(symbol.clone());
slot_working_symbols.remove(&symbol);
if !persistent_model_lifecycle {
self.pending_full_close_symbols.remove(&symbol);
}
} }
} }
} }
@@ -9834,6 +9855,10 @@ impl Strategy for PlatformExprStrategy {
) )
.is_some(); .is_some();
if close_submitted { if close_submitted {
self.refresh_available_cash_after_projected_sell(
&mut aiquant_available_cash,
&projected,
);
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());
@@ -9938,6 +9963,10 @@ impl Strategy for PlatformExprStrategy {
) )
.is_some(); .is_some();
if close_submitted { if close_submitted {
self.refresh_available_cash_after_projected_sell(
&mut aiquant_available_cash,
&projected,
);
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());
@@ -10022,6 +10051,10 @@ impl Strategy for PlatformExprStrategy {
) )
.is_some(); .is_some();
if close_submitted { if close_submitted {
self.refresh_available_cash_after_projected_sell(
&mut aiquant_available_cash,
&projected,
);
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());
if !persistent_model_lifecycle { if !persistent_model_lifecycle {
@@ -10100,6 +10133,10 @@ impl Strategy for PlatformExprStrategy {
) )
.is_some(); .is_some();
if close_submitted { if close_submitted {
self.refresh_available_cash_after_projected_sell(
&mut aiquant_available_cash,
&projected,
);
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());
if !persistent_model_lifecycle { if !persistent_model_lifecycle {
@@ -23911,12 +23948,16 @@ mod tests {
) )
.expect("dataset"); .expect("dataset");
let mut portfolio = PortfolioState::new(5_000.0); let mut portfolio = PortfolioState::new(0.0);
portfolio.position_mut(keep_first).buy(prev_date, 100, 10.0); portfolio
portfolio.position_mut(take_profit).buy(prev_date, 100, 8.0); .position_mut(keep_first)
.buy(prev_date, 1_000, 10.0);
portfolio
.position_mut(take_profit)
.buy(prev_date, 1_000, 8.0);
portfolio portfolio
.position_mut(keep_second) .position_mut(keep_second)
.buy(prev_date, 100, 10.0); .buy(prev_date, 1_000, 10.0);
let subscriptions = BTreeSet::new(); let subscriptions = BTreeSet::new();
let ctx = StrategyContext { let ctx = StrategyContext {
execution_date: date, execution_date: date,