From 8bd5d0a3f930cd5c703915b23922d6d22008dbf4 Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 4 Jun 2026 23:41:41 +0200 Subject: [PATCH] fix(aura-ingest): express unbounded M1 window bounds via Option load_m1_window took (from_ms: i64, to_ms: i64) and forwarded Some(from_ms)/Some(to_ms) into stream_m1_windowed, throwing away the underlying API's per-bound Option (None = unbounded). There was thus no way to express an open upper bound, and the natural i64::MAX sentinel overflowed chrono's from_timestamp_millis inside data-server's coarse unix_ms_to_year_month file filter, panicking the whole ingest. Thread Option through both bounds unchanged, so None = unbounded matches the underlying contract and 'to the end of history' needs no sentinel. Rejected the alternative of clamping a too-large bound to a ceiling constant: a ceiling is not actually unbounded (it silently drops any post-ceiling bar) and reintroduces a magic literal; restoring the Option makes the open bound structurally expressible instead. The committed RED test (2e38106) is now GREEN: it reads AAPL.US's latest archived month with None as the upper bound and asserts bar-for-bar equality with the finite sane bound. Verified: cargo test --workspace green (unbounded_window and real_bars both ran the real /mnt/tickdata path, not skipped); clippy --all-targets -D warnings clean. Out of scope, left untouched: the None-vs-Some(empty) rustdoc semantics (#18), and three out-of-workspace frozen fieldtest crates that still call the old bare-i64 signature -- those are dated historical replay records and are intentionally not rewritten. closes #23 --- crates/aura-ingest/src/lib.rs | 18 +++++++++++++----- crates/aura-ingest/tests/real_bars.rs | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/aura-ingest/src/lib.rs b/crates/aura-ingest/src/lib.rs index 7756f22..7134e3c 100644 --- a/crates/aura-ingest/src/lib.rs +++ b/crates/aura-ingest/src/lib.rs @@ -87,15 +87,23 @@ pub fn transpose_m1(bars: &[M1Parsed]) -> M1Columns { /// Drain a data-server M1 window into transposed SoA columns. Reads the /// chronological chunk iterator to exhaustion into one AoS buffer, then /// transposes once at the boundary (C3 — one merge point, not per chunk). -/// `None` if the symbol has no data in `[from_ms, to_ms]` (data-server's own -/// `None`); the inclusive Unix-ms window bounds are data-server's contract. +/// `None` if the symbol has no data in the window (data-server's own `None`); +/// the inclusive Unix-ms window bounds are data-server's contract. +/// +/// Each bound is an `Option`: `None` means unbounded on that side +/// (data-server skips the coarse `unix_ms_to_year_month` file filter rather +/// than reaching for a sentinel). `from_ms = None` reads from the start of +/// history; `to_ms = None` reads to the end. This threads data-server's own +/// per-bound `Option` contract through unchanged, so "to the end of history" +/// is expressible without an `i64::MAX` sentinel (which panics upstream in +/// chrono's `from_timestamp_millis`). pub fn load_m1_window( server: &Arc, symbol: &str, - from_ms: i64, - to_ms: i64, + from_ms: Option, + to_ms: Option, ) -> Option { - let mut it = server.stream_m1_windowed(symbol, Some(from_ms), Some(to_ms))?; + let mut it = server.stream_m1_windowed(symbol, from_ms, to_ms)?; let mut bars: Vec = Vec::new(); // M1Parsed is Copy; &Arc<[M1Parsed]> derefs to &[M1Parsed] in arg position. while let Some(chunk) = it.next_chunk() { diff --git a/crates/aura-ingest/tests/real_bars.rs b/crates/aura-ingest/tests/real_bars.rs index 38ce5ff..cadce75 100644 --- a/crates/aura-ingest/tests/real_bars.rs +++ b/crates/aura-ingest/tests/real_bars.rs @@ -90,7 +90,7 @@ fn sample_strategy_runs_over_real_m1_bars_deterministically() { let (from_ms, to_ms) = (1_154_390_400_000_i64, 1_157_068_799_999_i64); let load = || { - load_m1_window(&server, "AAPL.US", from_ms, to_ms) + load_m1_window(&server, "AAPL.US", Some(from_ms), Some(to_ms)) .expect("AAPL.US has data in the 2006-08 window") .close_stream() };