修复ALV日内补仓循环次数

This commit is contained in:
boris
2026-07-08 01:48:15 +08:00
parent 89f8bb32d0
commit 8c4156948e
+133 -122
View File
@@ -2241,7 +2241,7 @@ impl PlatformExprStrategy {
}
#[allow(clippy::too_many_arguments)]
fn try_daily_top_up_once(
fn try_daily_top_up_at_position(
&mut self,
ctx: &StrategyContext<'_>,
day: &DayExpressionState,
@@ -2274,136 +2274,147 @@ impl PlatformExprStrategy {
return Ok(false);
}
let mut budget_working_symbols = slot_working_symbols.clone();
budget_working_symbols.extend(same_bar_buy_symbols.iter().cloned());
if Self::effective_slot_count(&budget_working_symbols, pending_full_close_symbols)
>= selection_limit
{
return Ok(false);
}
let value_symbols = budget_working_symbols.clone();
let active_value = if debug_daily_top_up {
self.remaining_buy_cash_active_value(
let mut submitted_any = false;
loop {
let mut budget_working_symbols = slot_working_symbols.clone();
budget_working_symbols.extend(same_bar_buy_symbols.iter().cloned());
if Self::effective_slot_count(&budget_working_symbols, pending_full_close_symbols)
>= selection_limit
{
break;
}
let value_symbols = budget_working_symbols.clone();
let active_value = if debug_daily_top_up {
self.remaining_buy_cash_active_value(
ctx,
projected,
projection_date,
&budget_working_symbols,
&value_symbols,
)
} else {
0.0
};
let slot_buy_cash = self.remaining_buy_cash_per_slot(
ctx,
projected,
projection_date,
target_budget,
selection_limit,
&budget_working_symbols,
&value_symbols,
)
} else {
0.0
};
let slot_buy_cash = self.remaining_buy_cash_per_slot(
ctx,
projected,
projection_date,
target_budget,
selection_limit,
&budget_working_symbols,
&value_symbols,
*pending_buy_value,
pending_full_close_symbols,
);
let available_buy_cash = slot_buy_cash.min(*aiquant_available_cash);
if debug_daily_top_up {
daily_top_up_debug_notes.push(format!(
"daily_top_up_budget date={} working={} value_symbols={} same_bar_buys={} active_value={:.4} pending_buy_value={:.4} target_budget={:.4} slot_buy_cash={:.4} available_cash={:.4} portfolio_cash={:.4}",
execution_date,
budget_working_symbols.len(),
value_symbols.len(),
same_bar_buy_symbols.len(),
active_value,
*pending_buy_value,
target_budget,
slot_buy_cash,
available_buy_cash,
*aiquant_available_cash
));
}
if slot_buy_cash <= 0.0 || available_buy_cash < slot_buy_cash * 0.5 {
return Ok(false);
}
for symbol in stock_list {
if current_position_symbol
.map(|current| current == symbol)
.unwrap_or(false)
{
continue;
}
let released_exit_position = projected.positions().contains_key(symbol)
&& !slot_working_symbols.contains(symbol)
&& !same_day_sold_symbols.contains(symbol)
&& !pending_full_close_symbols.contains(symbol);
if (projected.positions().contains_key(symbol) && !released_exit_position)
|| same_day_sold_symbols.contains(symbol)
|| (exit_symbols.contains(symbol) && !released_exit_position)
|| pending_full_close_symbols.contains(symbol)
|| delayed_sold_symbols.contains(symbol)
|| intraday_attempted_buys.contains(symbol)
{
continue;
}
if !defer_execution_risk
&& self
.buy_rejection_reason(
ctx,
execution_date,
symbol,
&self.stock_state(ctx, execution_date, symbol)?,
)?
.is_some()
{
continue;
}
let decision_stock = self.stock_state_with_factor_date(
ctx,
decision_date,
selection_factor_date,
symbol,
)?;
let buy_cash = available_buy_cash * self.buy_scale(ctx, day, &decision_stock)?;
if buy_cash <= 0.0 {
return Ok(false);
}
intraday_attempted_buys.insert(symbol.clone());
let cash_before_buy = projected.cash();
let order_result = self.project_order_value(
ctx,
projected,
projection_date,
symbol,
buy_cash,
projected_execution_state,
pending_full_close_symbols,
);
if order_result.was_submitted() {
order_intents.push(OrderIntent::Value {
symbol: symbol.clone(),
value: buy_cash,
reason: "daily_top_up_buy".to_string(),
});
self.remember_position_entry_date(symbol, signal_date);
let spent = (cash_before_buy - projected.cash()).max(0.0);
*aiquant_available_cash = (*aiquant_available_cash - spent).max(0.0);
slot_working_symbols.insert(symbol.clone());
same_bar_buy_symbols.insert(symbol.clone());
*pending_buy_value += available_buy_cash;
if debug_daily_top_up {
daily_top_up_debug_notes.push(format!(
"daily_top_up_fill date={} symbol={} requested_cash={:.4} filled_qty={} spent={:.4} remaining_cash={:.4}",
execution_date,
symbol,
buy_cash,
order_result.filled_quantity,
spent,
*aiquant_available_cash
));
let available_buy_cash = slot_buy_cash.min(*aiquant_available_cash);
if debug_daily_top_up {
daily_top_up_debug_notes.push(format!(
"daily_top_up_budget date={} working={} value_symbols={} same_bar_buys={} active_value={:.4} pending_buy_value={:.4} target_budget={:.4} slot_buy_cash={:.4} available_cash={:.4} portfolio_cash={:.4}",
execution_date,
budget_working_symbols.len(),
value_symbols.len(),
same_bar_buy_symbols.len(),
active_value,
*pending_buy_value,
target_budget,
slot_buy_cash,
available_buy_cash,
*aiquant_available_cash
));
}
if slot_buy_cash <= 0.0 || available_buy_cash < slot_buy_cash * 0.5 {
break;
}
let mut attempted_any = false;
let mut filled_any = false;
for symbol in stock_list {
if current_position_symbol
.map(|current| current == symbol)
.unwrap_or(false)
{
continue;
}
return Ok(true);
let released_exit_position = projected.positions().contains_key(symbol)
&& !slot_working_symbols.contains(symbol)
&& !same_day_sold_symbols.contains(symbol)
&& !pending_full_close_symbols.contains(symbol);
if (projected.positions().contains_key(symbol) && !released_exit_position)
|| same_day_sold_symbols.contains(symbol)
|| (exit_symbols.contains(symbol) && !released_exit_position)
|| pending_full_close_symbols.contains(symbol)
|| delayed_sold_symbols.contains(symbol)
|| intraday_attempted_buys.contains(symbol)
{
continue;
}
if !defer_execution_risk
&& self
.buy_rejection_reason(
ctx,
execution_date,
symbol,
&self.stock_state(ctx, execution_date, symbol)?,
)?
.is_some()
{
continue;
}
let decision_stock = self.stock_state_with_factor_date(
ctx,
decision_date,
selection_factor_date,
symbol,
)?;
let buy_cash = available_buy_cash * self.buy_scale(ctx, day, &decision_stock)?;
if buy_cash <= 0.0 {
return Ok(submitted_any);
}
intraday_attempted_buys.insert(symbol.clone());
attempted_any = true;
let cash_before_buy = projected.cash();
let order_result = self.project_order_value(
ctx,
projected,
projection_date,
symbol,
buy_cash,
projected_execution_state,
);
if order_result.was_submitted() {
order_intents.push(OrderIntent::Value {
symbol: symbol.clone(),
value: buy_cash,
reason: "daily_top_up_buy".to_string(),
});
self.remember_position_entry_date(symbol, signal_date);
let spent = (cash_before_buy - projected.cash()).max(0.0);
*aiquant_available_cash = (*aiquant_available_cash - spent).max(0.0);
slot_working_symbols.insert(symbol.clone());
same_bar_buy_symbols.insert(symbol.clone());
*pending_buy_value += available_buy_cash;
if debug_daily_top_up {
daily_top_up_debug_notes.push(format!(
"daily_top_up_fill date={} symbol={} requested_cash={:.4} filled_qty={} spent={:.4} remaining_cash={:.4}",
execution_date,
symbol,
buy_cash,
order_result.filled_quantity,
spent,
*aiquant_available_cash
));
}
submitted_any = true;
filled_any = true;
break;
}
}
if !attempted_any || !filled_any {
break;
}
}
Ok(false)
Ok(submitted_any)
}
fn effective_slot_count(
@@ -8697,7 +8708,7 @@ impl Strategy for PlatformExprStrategy {
unresolved_stop_loss_symbols.insert(position.symbol.clone());
}
if daily_top_up_active {
self.try_daily_top_up_once(
self.try_daily_top_up_at_position(
ctx,
&day,
&stock_list,
@@ -8762,7 +8773,7 @@ impl Strategy for PlatformExprStrategy {
}
}
if daily_top_up_active {
self.try_daily_top_up_once(
self.try_daily_top_up_at_position(
ctx,
&day,
&stock_list,