修复目标生命周期退出后复活
This commit is contained in:
@@ -2419,8 +2419,16 @@ impl PlatformExprStrategy {
|
||||
.entry(symbol.clone())
|
||||
.or_insert(0);
|
||||
self.position_holding_days_last_counted
|
||||
.entry(symbol)
|
||||
.entry(symbol.clone())
|
||||
.or_insert(signal_date);
|
||||
if let Some(target_order) = &mut self.last_target_order
|
||||
&& !target_order.contains(&symbol)
|
||||
{
|
||||
target_order.push(symbol.clone());
|
||||
}
|
||||
if let Some(target_selection) = &mut self.last_target_selection {
|
||||
target_selection.insert(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2428,6 +2436,12 @@ impl PlatformExprStrategy {
|
||||
self.position_entry_dates.remove(symbol);
|
||||
self.position_holding_days.remove(symbol);
|
||||
self.position_holding_days_last_counted.remove(symbol);
|
||||
if let Some(target_order) = &mut self.last_target_order {
|
||||
target_order.retain(|target| target != symbol);
|
||||
}
|
||||
if let Some(target_selection) = &mut self.last_target_selection {
|
||||
target_selection.remove(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
fn max_holding_days_exceeded(&self, symbol: &str) -> Option<i64> {
|
||||
@@ -13252,11 +13266,16 @@ impl Strategy for PlatformExprStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
let blocked_exit_slots = exit_symbols
|
||||
.iter()
|
||||
.filter(|symbol| !Self::projected_position_is_flat(&projected, symbol))
|
||||
.count();
|
||||
let daily_target_count = selection_limit.saturating_sub(blocked_exit_slots);
|
||||
let daily_target_portfolio_scales = resolved_target_scales(
|
||||
&original_target_scales,
|
||||
&candidate_target_scales,
|
||||
&same_day_sold_symbols,
|
||||
selection_limit,
|
||||
&exit_symbols,
|
||||
daily_target_count,
|
||||
self.config.redistribute_target_weights_after_exit,
|
||||
)?;
|
||||
if daily_top_up_active && self.config.target_portfolio_daily_enabled {
|
||||
@@ -13785,6 +13804,58 @@ mod tests {
|
||||
assert!((redistributed.iter().map(|(_, scale)| *scale).sum::<f64>() - 3.0).abs() < 1e-12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_target_lifecycle_removes_exits_and_appends_replacements() {
|
||||
let date = d(2025, 1, 3);
|
||||
let mut strategy = PlatformExprStrategy::new(PlatformExprStrategyConfig::generic());
|
||||
strategy.last_target_order = Some(vec![
|
||||
"KEEP".to_string(),
|
||||
"EXITED".to_string(),
|
||||
"RETRY".to_string(),
|
||||
]);
|
||||
strategy.last_target_selection = Some(BTreeSet::from([
|
||||
"KEEP".to_string(),
|
||||
"EXITED".to_string(),
|
||||
"RETRY".to_string(),
|
||||
]));
|
||||
strategy
|
||||
.position_entry_dates
|
||||
.insert("EXITED".to_string(), date);
|
||||
|
||||
strategy.forget_position_entry_date("EXITED");
|
||||
assert_eq!(
|
||||
strategy.last_target_order.as_deref(),
|
||||
Some(["KEEP".to_string(), "RETRY".to_string()].as_slice())
|
||||
);
|
||||
assert!(
|
||||
!strategy
|
||||
.last_target_selection
|
||||
.as_ref()
|
||||
.expect("target selection")
|
||||
.contains("EXITED")
|
||||
);
|
||||
|
||||
strategy.remember_position_entry_date("REPLACEMENT", date);
|
||||
assert_eq!(
|
||||
strategy.last_target_order.as_deref(),
|
||||
Some(
|
||||
[
|
||||
"KEEP".to_string(),
|
||||
"RETRY".to_string(),
|
||||
"REPLACEMENT".to_string(),
|
||||
]
|
||||
.as_slice()
|
||||
)
|
||||
);
|
||||
assert!(
|
||||
strategy
|
||||
.last_target_selection
|
||||
.as_ref()
|
||||
.expect("target selection")
|
||||
.contains("REPLACEMENT")
|
||||
);
|
||||
}
|
||||
|
||||
fn single_symbol_platform_data(dates: &[NaiveDate], symbol: &str) -> DataSet {
|
||||
DataSet::from_components(
|
||||
vec![Instrument {
|
||||
|
||||
Reference in New Issue
Block a user