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

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; continue;
} }
if rebalance_working_symbols.len() >= selection_limit { 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) if ((pre_rebalance_symbols.contains(symbol)
|| projected.positions().contains_key(symbol)) || projected.positions().contains_key(symbol))
@@ -11072,7 +11074,7 @@ mod tests {
let previous_date = d(2025, 1, 2); let previous_date = d(2025, 1, 2);
let execution_date = d(2025, 1, 3); let execution_date = d(2025, 1, 3);
let unresolved_symbol = "000999.SZ"; 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( let data = DataSet::from_components(
std::iter::once(Instrument { std::iter::once(Instrument {
symbol: unresolved_symbol.to_string(), symbol: unresolved_symbol.to_string(),
@@ -11164,6 +11166,9 @@ mod tests {
portfolio portfolio
.position_mut(unresolved_symbol) .position_mut(unresolved_symbol)
.buy(previous_date, 1_000, 10.0); .buy(previous_date, 1_000, 10.0);
portfolio
.position_mut("000003.SZ")
.buy(previous_date, 100, 10.0);
let subscriptions = BTreeSet::new(); let subscriptions = BTreeSet::new();
let before_delisting_ctx = StrategyContext { let before_delisting_ctx = StrategyContext {
execution_date: previous_date, execution_date: previous_date,
@@ -11205,21 +11210,28 @@ mod tests {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
cfg.signal_symbol = "000001.SZ".to_string(); cfg.signal_symbol = "000001.SZ".to_string();
cfg.refresh_rate = 1; cfg.refresh_rate = 1;
cfg.max_positions = 2; cfg.max_positions = 3;
cfg.benchmark_short_ma_days = 1; cfg.benchmark_short_ma_days = 1;
cfg.benchmark_long_ma_days = 1; cfg.benchmark_long_ma_days = 1;
cfg.market_cap_lower_expr = "0".to_string(); cfg.market_cap_lower_expr = "0".to_string();
cfg.market_cap_upper_expr = "100".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.stock_filter_expr = "close > 0".to_string();
cfg.stop_loss_expr.clear(); cfg.stop_loss_expr.clear();
cfg.take_profit_expr.clear(); cfg.take_profit_expr.clear();
cfg.aiquant_transaction_cost = true; cfg.aiquant_transaction_cost = true;
cfg.daily_replacement_limit = 2; cfg.daily_replacement_limit = 3;
cfg.selection_buffer_multiple = 2.0; cfg.selection_buffer_multiple = 2.0;
cfg.rebalance_existing_positions = false; cfg.rebalance_existing_positions = false;
let mut strategy = PlatformExprStrategy::new(cfg); let mut strategy = PlatformExprStrategy::new(cfg);
strategy.rebalance_day_counter = 1; 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"); let decision = strategy.on_day(&ctx).expect("platform decision");
@@ -11233,7 +11245,9 @@ mod tests {
decision decision
.diagnostics .diagnostics
.iter() .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| { assert!(!decision.order_intents.iter().any(|intent| {
matches!( matches!(
@@ -11265,6 +11279,18 @@ mod tests {
&& reason == "periodic_rebalance_buy" && 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] #[test]