修复分钟事件轮动仅执行最后时点的问题

This commit is contained in:
boris
2026-09-09 23:52:35 +08:00
parent bbbd9cf3e0
commit 35acb1c7e7
2 changed files with 30 additions and 7 deletions
+12 -7
View File
@@ -672,6 +672,7 @@ pub struct PlatformExprStrategyConfig {
pub completed_session_factor_fields: BTreeSet<String>,
pub candidate_symbols_by_date: BTreeMap<NaiveDate, BTreeSet<String>>,
pub intraday_execution_time: Option<NaiveTime>,
pub session_event_times: Vec<NaiveTime>,
pub explicit_action_times: Vec<NaiveTime>,
pub delayed_limit_open_exit_enabled: bool,
pub delayed_limit_open_exit_time: Option<NaiveTime>,
@@ -753,6 +754,7 @@ impl PlatformExprStrategyConfig {
completed_session_factor_fields: BTreeSet::new(),
candidate_symbols_by_date: BTreeMap::new(),
intraday_execution_time: None,
session_event_times: Vec::new(),
explicit_action_times: Vec::new(),
delayed_limit_open_exit_enabled: false,
delayed_limit_open_exit_time: None,
@@ -12294,13 +12296,15 @@ impl Strategy for PlatformExprStrategy {
.is_some()
&& self.config.rotation_enabled
{
rules.push(
self.config
.rebalance_schedule
.as_ref()
.expect("checked timed rebalance schedule")
.as_schedule_rule(ScheduleStage::OnDay),
);
let schedule=self.config.rebalance_schedule.as_ref().expect("checked timed rebalance schedule");
if self.config.session_event_times.is_empty() {
rules.push(schedule.as_schedule_rule(ScheduleStage::OnDay));
} else {
for time in &self.config.session_event_times {
let mut timed=schedule.clone();timed.time_rule=Some(ScheduleTimeRule::physical_time(time.hour(),time.minute()));
rules.push(timed.as_schedule_rule(ScheduleStage::OnDay));
}
}
}
if self.config.explicit_actions.is_empty() {
return rules;
@@ -12363,6 +12367,7 @@ impl Strategy for PlatformExprStrategy {
fn decision_quote_times(&self) -> Vec<NaiveTime> {
let mut times = BTreeSet::new();
times.extend(self.config.session_event_times.iter().copied());
if self.uses_intraday_execution_quotes() {
if self.config.explicit_action_times.is_empty() {
times.insert(self.intraday_execution_start_time());
@@ -2538,6 +2538,10 @@ pub fn platform_expr_config_from_spec(
cfg.benchmark_symbol = normalize_symbol(&cfg.benchmark_symbol, None);
}
let trade_times = spec_trade_times(spec);
if crate::pattern_context::specs_in_value(&serde_json::to_value(spec).map_err(|e|e.to_string())?)?.iter().any(|p|p.template=="session_event") {
if trade_times.is_empty() {return Err("session_event_requires_explicit_trade_times".into());}
cfg.session_event_times=trade_times.clone();
}
let explicit_trading_schedule = spec
.runtime_expressions
.as_ref()
@@ -4458,6 +4462,20 @@ mod tests {
assert_eq!(cfg.delayed_limit_open_exit_time, None);
}
#[test]
fn session_rotation_keeps_every_declared_clock_not_only_the_last_one() {
use crate::Strategy;
let literal=serde_json::to_string(&serde_json::json!({"template":"session_event","session_event":"INTRADAY_VOLUME_SPIKE","parameters":{}}).to_string()).unwrap();
let mut spec=serde_json::json!({"rebalance":{"tradeTimes":["09:35","10:40","14:59"]},"runtimeExpressions":{"schedule":{"frequency":"daily","time":"14:59"},"trading":{"rotationEnabled":true,"buyFilterExpr":format!("pattern_signal({literal})")}},"execution":{"matchingType":"minute_last"}});
let config=platform_expr_config_from_value("session","000300.SH",&spec).unwrap();
assert_eq!(config.session_event_times.len(),3);
let strategy=crate::PlatformExprStrategy::new(config);
assert_eq!(strategy.schedule_rules().len(),3);
assert_eq!(strategy.decision_quote_times().len(),3);
spec["rebalance"]["tradeTimes"]=serde_json::json!([]);
assert!(platform_expr_config_from_value("session","000300.SH",&spec).unwrap_err().to_string().contains("explicit_trade_times"));
}
#[test]
fn explicit_trading_schedule_overrides_rebalance_trade_times() {
let spec = serde_json::json!({