拆分调仓日期与执行时钟

This commit is contained in:
boris
2026-08-28 00:00:30 +08:00
parent 85c38b0756
commit a9511f9a4a
2 changed files with 65 additions and 16 deletions
+33
View File
@@ -164,6 +164,16 @@ impl<'a> Scheduler<'a> {
.collect()
}
/// Evaluate only the trading-calendar frequency of a rule.
///
/// Strategy callbacks and order execution clocks are separate contracts:
/// a 15:00 schedule is still due on the same daily/weekly/monthly trading
/// date even when the engine's coarse `on_day` callback runs at another
/// default time. Exact clock matching remains in `triggered_rules_at`.
pub fn is_due_on(&self, date: NaiveDate, rule: &ScheduleRule) -> bool {
self.matches(date, rule)
}
fn matches(&self, date: NaiveDate, rule: &ScheduleRule) -> bool {
match &rule.frequency {
ScheduleFrequency::Daily => true,
@@ -265,6 +275,29 @@ mod tests {
])
}
#[test]
fn date_due_is_independent_from_the_order_execution_clock() {
let calendar = sample_calendar();
let scheduler = Scheduler::new(&calendar);
let daily = ScheduleRule::daily("close_signal", ScheduleStage::OnDay)
.with_time_rule(ScheduleTimeRule::physical_time(15, 0));
assert!(scheduler.is_due_on(d(2025, 1, 30), &daily));
assert!(scheduler.is_due_on(d(2025, 1, 31), &daily));
assert!(scheduler.triggered_rules_at(
d(2025, 1, 30),
ScheduleStage::OnDay,
Some(NaiveTime::from_hms_opt(15, 0, 0).unwrap()),
std::slice::from_ref(&daily),
).len() == 1);
assert!(scheduler.triggered_rules_at(
d(2025, 1, 30),
ScheduleStage::OnDay,
Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap()),
std::slice::from_ref(&daily),
).is_empty());
}
#[test]
fn scheduler_matches_daily_weekly_and_monthly_rules() {
let calendar = sample_calendar();