修正满仓后既有目标调仓中断

This commit is contained in:
boris
2026-07-19 08:23:47 +08:00
parent 24528ecfeb
commit d9ce3eeb5c
+32 -6
View File
@@ -10606,7 +10606,9 @@ impl Strategy for PlatformExprStrategy {
continue;
}
if rebalance_working_symbols.len() >= selection_limit {
break;
// New symbols cannot consume another slot, but later ranked symbols may
// already be held and still require a target-value adjustment.
continue;
}
if ((pre_rebalance_symbols.contains(symbol)
|| projected.positions().contains_key(symbol))
@@ -11072,7 +11074,7 @@ mod tests {
let previous_date = d(2025, 1, 2);
let execution_date = d(2025, 1, 3);
let unresolved_symbol = "000999.SZ";
let active_symbols = ["000001.SZ", "000002.SZ"];
let active_symbols = ["000001.SZ", "000002.SZ", "000003.SZ"];
let data = DataSet::from_components(
std::iter::once(Instrument {
symbol: unresolved_symbol.to_string(),
@@ -11164,6 +11166,9 @@ mod tests {
portfolio
.position_mut(unresolved_symbol)
.buy(previous_date, 1_000, 10.0);
portfolio
.position_mut("000003.SZ")
.buy(previous_date, 100, 10.0);
let subscriptions = BTreeSet::new();
let before_delisting_ctx = StrategyContext {
execution_date: previous_date,
@@ -11205,21 +11210,28 @@ mod tests {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
cfg.signal_symbol = "000001.SZ".to_string();
cfg.refresh_rate = 1;
cfg.max_positions = 2;
cfg.max_positions = 3;
cfg.benchmark_short_ma_days = 1;
cfg.benchmark_long_ma_days = 1;
cfg.market_cap_lower_expr = "0".to_string();
cfg.market_cap_upper_expr = "100".to_string();
cfg.selection_limit_expr = "2".to_string();
cfg.selection_limit_expr = "3".to_string();
cfg.stock_filter_expr = "close > 0".to_string();
cfg.stop_loss_expr.clear();
cfg.take_profit_expr.clear();
cfg.aiquant_transaction_cost = true;
cfg.daily_replacement_limit = 2;
cfg.daily_replacement_limit = 3;
cfg.selection_buffer_multiple = 2.0;
cfg.rebalance_existing_positions = false;
let mut strategy = PlatformExprStrategy::new(cfg);
strategy.rebalance_day_counter = 1;
strategy.last_trading_ratio = Some(0.1);
strategy.last_target_selection = Some(
active_symbols
.iter()
.map(|symbol| (*symbol).to_string())
.collect(),
);
let decision = strategy.on_day(&ctx).expect("platform decision");
@@ -11233,7 +11245,9 @@ mod tests {
decision
.diagnostics
.iter()
.any(|note| note == "selected_symbols=000001.SZ,000002.SZ")
.any(|note| note == "selected_symbols=000001.SZ,000002.SZ,000003.SZ"),
"{:?}",
decision.diagnostics
);
assert!(!decision.order_intents.iter().any(|intent| {
matches!(
@@ -11265,6 +11279,18 @@ mod tests {
&& reason == "periodic_rebalance_buy"
)
}));
assert!(decision.order_intents.iter().any(|intent| {
matches!(
intent,
OrderIntent::TargetValue {
symbol,
target_value,
reason,
} if symbol == "000003.SZ"
&& *target_value > 0.0
&& reason == "periodic_rebalance_target_adjust"
)
}));
}
#[test]