修正调仓后剩余买入预算

This commit is contained in:
boris
2026-07-17 12:32:25 +08:00
parent 60457389a3
commit bdd5a41106
+29 -17
View File
@@ -2540,28 +2540,31 @@ impl PlatformExprStrategy {
self.projected_position_value_at_execution_price(ctx, projected, date, symbol) self.projected_position_value_at_execution_price(ctx, projected, date, symbol)
} }
fn context_position_value_for_remaining_buy_cash( fn projected_strategy_visible_position_value_for_remaining_buy_cash(
&self, &self,
ctx: &StrategyContext<'_>, ctx: &StrategyContext<'_>,
projected: &PortfolioState,
date: NaiveDate, date: NaiveDate,
symbol: &str, symbol: &str,
) -> f64 { ) -> f64 {
let Some(position) = ctx.portfolio.position(symbol) else { let Some(projected_position) = projected.position(symbol) else {
return 0.0; return 0.0;
}; };
if position.quantity == 0 { if projected_position.quantity == 0 {
return 0.0; return 0.0;
} }
let context_position = ctx.portfolio.position(symbol);
let mark_price = self let mark_price = self
.aiquant_scheduled_last_price(ctx, date, symbol) .aiquant_scheduled_last_price(ctx, date, symbol)
.or_else(|| ctx.data.price(date, symbol, PriceField::Last)) .or_else(|| ctx.data.price(date, symbol, PriceField::Last))
.or_else(|| ctx.data.price_on_or_before(date, symbol, PriceField::Last)) .or_else(|| ctx.data.price_on_or_before(date, symbol, PriceField::Last))
.or_else(|| context_position.map(|position| position.last_price))
.filter(|price| price.is_finite() && *price > 0.0) .filter(|price| price.is_finite() && *price > 0.0)
.unwrap_or(position.last_price); .unwrap_or(projected_position.last_price);
if mark_price.is_finite() && mark_price > 0.0 { if mark_price.is_finite() && mark_price > 0.0 {
mark_price * position.quantity as f64 mark_price * projected_position.quantity as f64
} else { } else {
position.market_value() projected_position.market_value()
} }
} }
@@ -2775,7 +2778,9 @@ impl PlatformExprStrategy {
*aiquant_available_cash = (*aiquant_available_cash - spent).max(0.0); *aiquant_available_cash = (*aiquant_available_cash - spent).max(0.0);
slot_working_symbols.insert(symbol.clone()); slot_working_symbols.insert(symbol.clone());
same_bar_buy_symbols.insert(symbol.clone()); same_bar_buy_symbols.insert(symbol.clone());
*pending_buy_value += available_buy_cash; if order_result.filled_quantity == 0 {
*pending_buy_value += available_buy_cash;
}
if debug_daily_top_up { if debug_daily_top_up {
daily_top_up_debug_notes.push(format!( daily_top_up_debug_notes.push(format!(
"daily_top_up_fill date={} symbol={} requested_cash={:.4} filled_qty={} spent={:.4} remaining_cash={:.4}", "daily_top_up_fill date={} symbol={} requested_cash={:.4} filled_qty={} spent={:.4} remaining_cash={:.4}",
@@ -2833,7 +2838,9 @@ impl PlatformExprStrategy {
.filter(|symbol| value_symbols.contains(*symbol)) .filter(|symbol| value_symbols.contains(*symbol))
.map(|symbol| { .map(|symbol| {
if self.config.aiquant_transaction_cost { if self.config.aiquant_transaction_cost {
self.context_position_value_for_remaining_buy_cash(ctx, date, symbol) self.projected_strategy_visible_position_value_for_remaining_buy_cash(
ctx, projected, date, symbol,
)
} else { } else {
self.projected_position_value_at_execution_price(ctx, projected, date, symbol) self.projected_position_value_at_execution_price(ctx, projected, date, symbol)
} }
@@ -17610,7 +17617,7 @@ mod tests {
} }
#[test] #[test]
fn platform_aiquant_weak_market_threshold_top_up_uses_remaining_budget() { fn platform_aiquant_weak_market_threshold_top_up_uses_projected_remaining_budget() {
let prev_date = d(2023, 5, 4); let prev_date = d(2023, 5, 4);
let date = d(2023, 5, 5); let date = d(2023, 5, 5);
let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"]; let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"];
@@ -17766,10 +17773,15 @@ mod tests {
"{:?}", "{:?}",
decision.order_intents decision.order_intents
); );
assert!(!decision.order_intents.iter().any(|intent| matches!( assert!(
intent, decision.order_intents.iter().any(|intent| matches!(
OrderIntent::Value { reason, .. } if reason == "daily_top_up_buy" intent,
))); OrderIntent::Value { value, reason, .. }
if reason == "daily_top_up_buy" && (*value - 13_000.0).abs() < 1e-6
)),
"{:?}",
decision.order_intents
);
} }
#[test] #[test]
@@ -21713,7 +21725,7 @@ mod tests {
} }
#[test] #[test]
fn platform_aiquant_remaining_buy_cash_uses_strategy_visible_positions() { fn platform_aiquant_remaining_buy_cash_uses_strategy_visible_price_and_projected_quantity() {
let prev_date = d(2025, 2, 2); let prev_date = d(2025, 2, 2);
let date = d(2025, 2, 3); let date = d(2025, 2, 3);
let symbols = ["000001.SZ", "000002.SZ"]; let symbols = ["000001.SZ", "000002.SZ"];
@@ -21842,8 +21854,8 @@ mod tests {
); );
assert!( assert!(
(cash - 1_000.0).abs() < 1e-6, (cash - 5_000.0).abs() < 1e-6,
"remaining cash should use strategy-visible 900-share position, got {cash}" "remaining cash should use strategy-visible price and projected 500-share quantity, got {cash}"
); );
} }
@@ -22000,7 +22012,7 @@ mod tests {
reason, reason,
} if symbol == "000002.SZ" } if symbol == "000002.SZ"
&& reason == "daily_top_up_buy" && reason == "daily_top_up_buy"
&& (*value - 1_500.0).abs() < 1e-6 && (*value - 4_500.0).abs() < 1e-6
)), )),
"{:?}", "{:?}",
decision.order_intents decision.order_intents