test(ml-alpha): synthetic multi-resolution pipeline test

Four pure-CPU tests confirming aggregate_window emits the right
ts_ns - prev_ts_ns span per scale, so the existing snap-feature Δt
Fourier encoder gets correctly-scaled inputs without any CUDA kernel
changes.
This commit is contained in:
jgrusewski
2026-05-22 20:46:55 +02:00
parent b1bfae2367
commit a06abf60df

View File

@@ -0,0 +1,84 @@
//! End-to-end pipeline test using synthetic Mbp10RawInput data.
//!
//! Pure-CPU test (no GPU, no FOXHUNT_TEST_DATA gate). Confirms that
//! `aggregate_window` emits ts_ns / prev_ts_ns spans matching the design,
//! so the snap-feature Δt Fourier encoder receives the correct per-position
//! dt for each scale.
use ml_alpha::cfc::snap_features::Mbp10RawInput;
use ml_alpha::data::aggregation::{aggregate_window, MultiResolutionConfig};
#[test]
fn default_three_scale_position_layout() {
let cfg = MultiResolutionConfig::default_three_scale();
assert_eq!(cfg.scales(), &[(1, 10), (30, 10), (100, 12)]);
assert_eq!(cfg.total_positions(), 32);
assert_eq!(cfg.required_lookback_ticks(), 1510);
}
#[test]
fn aggregate_window_30_tick_dt_spans_window() {
let snaps: Vec<Mbp10RawInput> = (0..30u64)
.map(|i| {
let mut x = Mbp10RawInput::default();
x.ts_ns = 1000 + i * 100;
x.prev_ts_ns = x.ts_ns.saturating_sub(100);
x
})
.collect();
let agg = aggregate_window(&snaps);
let dt = agg.ts_ns - agg.prev_ts_ns;
assert_eq!(dt, 29 * 100, "30-tick window @ 100ns spacing -> dt = 2900ns");
}
#[test]
fn aggregate_window_100_tick_dt_much_larger_than_raw_dt() {
// 100-tick window @ 100ns spacing -> dt = 99 * 100 = 9900ns
let snaps_100: Vec<Mbp10RawInput> = (0..100u64)
.map(|i| {
let mut x = Mbp10RawInput::default();
x.ts_ns = 5000 + i * 100;
x.prev_ts_ns = x.ts_ns.saturating_sub(100);
x
})
.collect();
let agg_100 = aggregate_window(&snaps_100);
let dt_100 = agg_100.ts_ns - agg_100.prev_ts_ns;
// 1-tick raw position -> dt = 100ns (the input's own prev_ts_ns offset).
let raw = Mbp10RawInput {
ts_ns: 5000,
prev_ts_ns: 4900,
..Default::default()
};
let dt_raw = raw.ts_ns - raw.prev_ts_ns;
// 9900 vs 100 -> 99x ratio. Confirms the snap-feature Δt encoder will
// see a clearly distinct time-scale signature per position.
assert!(
dt_100 as f32 / dt_raw as f32 > 50.0,
"coarse-scale dt={dt_100} should be >50x raw dt={dt_raw}"
);
}
#[test]
fn aggregate_window_prev_mid_is_first_snapshot_mid() {
// Walking returns calc: cur.mid (last) - agg.prev_mid (first) should
// equal the realized return across the window.
let snaps: Vec<Mbp10RawInput> = (0..30u64)
.map(|i| {
let mut x = Mbp10RawInput::default();
x.prev_mid = 5000.0 + i as f32;
x.bid_px[0] = x.prev_mid - 0.125;
x.ask_px[0] = x.prev_mid + 0.125;
x.ts_ns = 1000 + i * 100;
x.prev_ts_ns = x.ts_ns.saturating_sub(100);
x
})
.collect();
let agg = aggregate_window(&snaps);
assert!(
(agg.prev_mid - 5000.0).abs() < 1e-5,
"agg.prev_mid should equal first snapshot's prev_mid"
);
}