修正动态排名替换目标状态
This commit is contained in:
@@ -799,6 +799,7 @@ pub struct PlatformExprStrategy {
|
||||
engine: Engine,
|
||||
rebalance_day_counter: usize,
|
||||
last_rebalance_date: Option<NaiveDate>,
|
||||
last_target_selection: Option<BTreeSet<String>>,
|
||||
last_trading_ratio: Option<f64>,
|
||||
portfolio_drawdown_controller: Option<PlatformPortfolioDrawdownController>,
|
||||
pending_highlimit_holdings: BTreeSet<String>,
|
||||
@@ -882,6 +883,18 @@ impl PlatformExprStrategy {
|
||||
defer_execution || projected_after_qty != before_qty
|
||||
}
|
||||
|
||||
fn replacement_reference_symbols(
|
||||
&self,
|
||||
actual_held_symbols: BTreeSet<String>,
|
||||
) -> (&'static str, BTreeSet<String>) {
|
||||
if self.config.daily_replacement_limit > 0 {
|
||||
if let Some(last_target_selection) = &self.last_target_selection {
|
||||
return ("previous_target", last_target_selection.clone());
|
||||
}
|
||||
}
|
||||
("actual_holdings", actual_held_symbols)
|
||||
}
|
||||
|
||||
fn buffered_selection(
|
||||
ranked_symbols: &[String],
|
||||
held_symbols: &BTreeSet<String>,
|
||||
@@ -1093,6 +1106,7 @@ impl PlatformExprStrategy {
|
||||
engine,
|
||||
rebalance_day_counter: 0,
|
||||
last_rebalance_date: None,
|
||||
last_target_selection: None,
|
||||
last_trading_ratio: None,
|
||||
portfolio_drawdown_controller,
|
||||
pending_highlimit_holdings: BTreeSet::new(),
|
||||
@@ -9172,6 +9186,8 @@ impl Strategy for PlatformExprStrategy {
|
||||
0
|
||||
};
|
||||
let mut risk_decisions = Vec::new();
|
||||
let mut replacement_reference_source = "disabled";
|
||||
let mut replacement_reference_count = 0usize;
|
||||
let stock_list = if self.config.rotation_enabled && !in_skip_window {
|
||||
let selection_buffer_rank =
|
||||
self.selection_candidate_limit(ctx, &day, selection_limit)?;
|
||||
@@ -9192,18 +9208,22 @@ impl Strategy for PlatformExprStrategy {
|
||||
)?;
|
||||
selection_notes = notes;
|
||||
risk_decisions = selection_risk_decisions;
|
||||
let mut held_symbols = ctx
|
||||
let mut actual_held_symbols = ctx
|
||||
.portfolio
|
||||
.positions()
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<BTreeSet<_>>();
|
||||
if persistent_model_lifecycle {
|
||||
held_symbols.extend(self.position_entry_dates.keys().cloned());
|
||||
actual_held_symbols.extend(self.position_entry_dates.keys().cloned());
|
||||
}
|
||||
let (reference_source, replacement_reference_symbols) =
|
||||
self.replacement_reference_symbols(actual_held_symbols);
|
||||
replacement_reference_source = reference_source;
|
||||
replacement_reference_count = replacement_reference_symbols.len();
|
||||
let selected = Self::buffered_selection(
|
||||
&ranked_stock_list,
|
||||
&held_symbols,
|
||||
&replacement_reference_symbols,
|
||||
selection_limit,
|
||||
selection_buffer_rank,
|
||||
self.config.daily_replacement_limit,
|
||||
@@ -10738,6 +10758,10 @@ impl Strategy for PlatformExprStrategy {
|
||||
self.rebalance_day_counter = self.rebalance_day_counter.saturating_add(1);
|
||||
}
|
||||
}
|
||||
if self.config.rotation_enabled && periodic_rebalance {
|
||||
self.last_target_selection =
|
||||
Some(stock_list.iter().take(selection_limit).cloned().collect());
|
||||
}
|
||||
if self.config.rotation_enabled && trading_ratio.is_finite() {
|
||||
self.last_trading_ratio = Some(trading_ratio);
|
||||
}
|
||||
@@ -10786,13 +10810,15 @@ impl Strategy for PlatformExprStrategy {
|
||||
)
|
||||
},
|
||||
format!(
|
||||
"selected={} periodic_rebalance={} exits={} projected_positions={} intents={} limit={} decision_date={} signal_rebalance_date_count={} signal_rebalance_match={} selection_market_date={} selection_universe_factor_date={} selection_factor_date={} execution_date={} budget_total={:.2} marked_total={:.2} day_total={:.2}",
|
||||
"selected={} periodic_rebalance={} exits={} projected_positions={} intents={} limit={} replacement_reference={} replacement_reference_count={} decision_date={} signal_rebalance_date_count={} signal_rebalance_match={} selection_market_date={} selection_universe_factor_date={} selection_factor_date={} execution_date={} budget_total={:.2} marked_total={:.2} day_total={:.2}",
|
||||
stock_list.len(),
|
||||
periodic_rebalance,
|
||||
exit_symbols.len(),
|
||||
projected.positions().len(),
|
||||
order_intents.len(),
|
||||
selection_limit,
|
||||
replacement_reference_source,
|
||||
replacement_reference_count,
|
||||
decision_date,
|
||||
self.config.signal_rebalance_dates.len(),
|
||||
self.config.signal_rebalance_dates.contains(&decision_date),
|
||||
@@ -31600,6 +31626,58 @@ mod tests {
|
||||
assert_eq!(held.difference(&selected_set).count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daily_replacement_reference_uses_previous_target_before_lagged_orders_fill() {
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.daily_replacement_limit = 2;
|
||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||
let previous_target = ["000001.SZ", "000002.SZ", "000003.SZ", "000004.SZ"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect::<BTreeSet<_>>();
|
||||
strategy.last_target_selection = Some(previous_target.clone());
|
||||
|
||||
let (source, reference) = strategy.replacement_reference_symbols(BTreeSet::new());
|
||||
let ranked = [
|
||||
"000005.SZ",
|
||||
"000006.SZ",
|
||||
"000007.SZ",
|
||||
"000008.SZ",
|
||||
"000001.SZ",
|
||||
"000002.SZ",
|
||||
"000003.SZ",
|
||||
"000004.SZ",
|
||||
]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<_>>();
|
||||
let selected = PlatformExprStrategy::buffered_selection(&ranked, &reference, 4, 4, 2);
|
||||
|
||||
assert_eq!(source, "previous_target");
|
||||
assert_eq!(reference, previous_target);
|
||||
assert_eq!(
|
||||
selected,
|
||||
vec!["000005.SZ", "000006.SZ", "000001.SZ", "000002.SZ"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daily_replacement_reference_records_an_explicit_empty_previous_target() {
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.daily_replacement_limit = 2;
|
||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||
strategy.last_target_selection = Some(BTreeSet::new());
|
||||
let actual = ["000001.SZ"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
let (source, reference) = strategy.replacement_reference_symbols(actual);
|
||||
|
||||
assert_eq!(source, "previous_target");
|
||||
assert!(reference.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagged_rebalance_emits_target_value_even_when_projection_is_unchanged() {
|
||||
assert!(PlatformExprStrategy::should_emit_rebalance_target_value(
|
||||
|
||||
Reference in New Issue
Block a user