区分股票池退出后重新入场
This commit is contained in:
@@ -653,6 +653,7 @@ pub struct PlatformExprStrategyConfig {
|
||||
pub delayed_limit_open_exit_time: Option<NaiveTime>,
|
||||
pub release_slot_on_exit_signal: bool,
|
||||
pub redistribute_target_weights_after_exit: bool,
|
||||
pub reenter_exited_targets: bool,
|
||||
pub explicit_action_stage: PlatformExplicitActionStage,
|
||||
pub explicit_action_schedule: Option<PlatformRebalanceSchedule>,
|
||||
pub subscription_guard_required: bool,
|
||||
@@ -729,6 +730,7 @@ impl PlatformExprStrategyConfig {
|
||||
delayed_limit_open_exit_time: None,
|
||||
release_slot_on_exit_signal: false,
|
||||
redistribute_target_weights_after_exit: false,
|
||||
reenter_exited_targets: false,
|
||||
explicit_action_stage: PlatformExplicitActionStage::OnDay,
|
||||
explicit_action_schedule: None,
|
||||
subscription_guard_required: false,
|
||||
@@ -2436,6 +2438,7 @@ impl PlatformExprStrategy {
|
||||
self.position_entry_dates.remove(symbol);
|
||||
self.position_holding_days.remove(symbol);
|
||||
self.position_holding_days_last_counted.remove(symbol);
|
||||
if !self.config.reenter_exited_targets {
|
||||
if let Some(target_order) = &mut self.last_target_order {
|
||||
target_order.retain(|target| target != symbol);
|
||||
}
|
||||
@@ -2443,6 +2446,7 @@ impl PlatformExprStrategy {
|
||||
target_selection.remove(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn max_holding_days_exceeded(&self, symbol: &str) -> Option<i64> {
|
||||
let max_days = self.config.max_holding_days.filter(|value| *value > 0)?;
|
||||
@@ -13863,6 +13867,35 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stock_pool_target_lifecycle_keeps_exits_for_next_day_reentry() {
|
||||
let date = d(2025, 1, 3);
|
||||
let mut config = PlatformExprStrategyConfig::generic();
|
||||
config.reenter_exited_targets = true;
|
||||
let mut strategy = PlatformExprStrategy::new(config);
|
||||
strategy.last_target_order = Some(vec!["KEEP".to_string(), "EXITED".to_string()]);
|
||||
strategy.last_target_selection =
|
||||
Some(BTreeSet::from(["KEEP".to_string(), "EXITED".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(), "EXITED".to_string()].as_slice())
|
||||
);
|
||||
assert!(
|
||||
strategy
|
||||
.last_target_selection
|
||||
.as_ref()
|
||||
.expect("target selection")
|
||||
.contains("EXITED")
|
||||
);
|
||||
assert!(!strategy.position_entry_dates.contains_key("EXITED"));
|
||||
}
|
||||
|
||||
fn single_symbol_platform_data(dates: &[NaiveDate], symbol: &str) -> DataSet {
|
||||
DataSet::from_components(
|
||||
vec![Instrument {
|
||||
|
||||
@@ -1022,6 +1022,8 @@ pub struct StrategyExpressionTradingConfig {
|
||||
#[serde(default)]
|
||||
pub redistribute_target_weights_after_exit: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub reenter_exited_targets: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub subscription_guard_required: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub subscriptions: Vec<String>,
|
||||
@@ -2299,6 +2301,9 @@ pub fn platform_expr_config_from_spec(
|
||||
if let Some(enabled) = trading.redistribute_target_weights_after_exit {
|
||||
cfg.redistribute_target_weights_after_exit = enabled;
|
||||
}
|
||||
if let Some(enabled) = trading.reenter_exited_targets {
|
||||
cfg.reenter_exited_targets = enabled;
|
||||
}
|
||||
if let Some(enabled) = trading.delayed_limit_open_exit {
|
||||
cfg.delayed_limit_open_exit_enabled = enabled;
|
||||
if enabled {
|
||||
@@ -3490,7 +3495,8 @@ mod tests {
|
||||
"rebalanceExistingPositions": true,
|
||||
"holdUntilExit": true,
|
||||
"releaseSlotOnExitSignal": true,
|
||||
"redistributeTargetWeightsAfterExit": true
|
||||
"redistributeTargetWeightsAfterExit": true,
|
||||
"reenterExitedTargets": true
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -3505,6 +3511,7 @@ mod tests {
|
||||
assert!(cfg.hold_until_exit_enabled);
|
||||
assert!(cfg.release_slot_on_exit_signal);
|
||||
assert!(cfg.redistribute_target_weights_after_exit);
|
||||
assert!(cfg.reenter_exited_targets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user