diff --git a/qtrade/clock.py b/qtrade/clock.py index edb49b88..0e50881a 100644 --- a/qtrade/clock.py +++ b/qtrade/clock.py @@ -34,7 +34,9 @@ class Job: JOBS = [ Job("data-hoard.yml", "UTC", tuple(f"{h:02d}:{m:02d}" for h in range(24) for m in (7, 37)), weekdays=False, window_min=20), - Job("hft-shadow.yml", NY, ("09:26",), window_min=300), # its own chain then runs the session + # its own chain runs the session; the hourly re-dispatch restarts it if a link fails (2026-11-09: a run that + # never started stopped it at 13:10) and otherwise only queues behind the running one + Job("hft-shadow.yml", NY, tuple(f"{h:02d}:26" for h in range(9, 16)), window_min=50), Job("flip-sleeve.yml", NY, ("15:45",), window_min=400), Job("auction-sheet.yml", NY, ("16:55",), window_min=400), # after Yahoo's ~16:30-16:45 refresh of Asian bars Job("hoard-archive.yml", NY, ("16:45",), inputs=(("job", "eod"),), window_min=400), diff --git a/tests/test_clock.py b/tests/test_clock.py index 8a53096c..e4b98b5a 100644 --- a/tests/test_clock.py +++ b/tests/test_clock.py @@ -32,3 +32,9 @@ def test_run_fires_each_slot_once(monkeypatch): monkeypatch.setattr(C.time, "sleep", lambda s: clock.__setitem__("t", clock["t"] + 1800)) fired = C.run(1.0, "o/r", "main", dry_run=True) # polls at 0 s and 1800 s, both within the 17:15 slot assert sum("foreign-sheet.yml" in f for f in fired) == 1 + + +def test_hft_shadow_is_re_dispatched_every_hour_of_the_session(): + j = _job("hft-shadow.yml") + keys = [k for h in range(14, 22) for k in C.due(j, pd.Timestamp(f"2026-11-09 {h}:30", tz="UTC"))] + assert len(keys) == 7 and keys[0].endswith("09:26") and keys[-1].endswith("15:26")