fix(aura-ingest): express unbounded M1 window bounds via Option<i64>

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<i64> (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<i64> 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
This commit is contained in:
2026-06-04 23:41:41 +02:00
parent 2e381060fa
commit 8bd5d0a3f9
2 changed files with 14 additions and 6 deletions
+13 -5
View File
@@ -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<i64>`: `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<DataServer>,
symbol: &str,
from_ms: i64,
to_ms: i64,
from_ms: Option<i64>,
to_ms: Option<i64>,
) -> Option<M1Columns> {
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<M1Parsed> = Vec::new();
// M1Parsed is Copy; &Arc<[M1Parsed]> derefs to &[M1Parsed] in arg position.
while let Some(chunk) = it.next_chunk() {
+1 -1
View File
@@ -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()
};