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:
84
crates/ml-alpha/tests/multi_resolution_pipeline.rs
Normal file
84
crates/ml-alpha/tests/multi_resolution_pipeline.rs
Normal 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"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user