Download ml/tests/test_normalization.py from Dev2506/flash-crash-watchdog: direct link, hf CLI and curl.
- Browser
- Download file 2.26 kB
-
https://huggingface.co/Dev2506/flash-crash-watchdog/resolve/main/ml/tests/test_normalization.py
- Command line
-
hf download hf://Dev2506/flash-crash-watchdog/ml/tests/test_normalization.py
-
curl -L -o test_normalization.py https://huggingface.co/Dev2506/flash-crash-watchdog/resolve/main/ml/tests/test_normalization.py
2.26 kB
| """Consistency test for the shared rolling-z normalization (BUG-03). | |
| The offline backtest transform `normalize_z` MUST produce the same per-tick | |
| normalized vectors as the online streaming `Stage3TCN._normalize`, so that | |
| offline scores transfer to the live Stage3TCN.feed path. | |
| """ | |
| import numpy as np | |
| from flash_crash_watchdog.lob import OrderBookSnapshot, PriceLevel | |
| from flash_crash_watchdog.models.stage3_tcn import ( | |
| STAGE3_FEATURES, | |
| Stage3TCN, | |
| TCNConfig, | |
| normalize_z, | |
| ) | |
| from flash_crash_watchdog.tick import Tick | |
| def _tick_from_features(fvec: np.ndarray, ts: int) -> Tick: | |
| book = OrderBookSnapshot( | |
| timestamp_ms=ts, bids=[PriceLevel(100.0, 1.0)], asks=[PriceLevel(100.0, 1.0)] | |
| ) | |
| tick = Tick(book=book, symbol="T") | |
| tick.features = {name: float(v) for name, v in zip(STAGE3_FEATURES, fvec)} | |
| return tick | |
| def test_offline_normalize_matches_streaming_stage3(): | |
| rng = np.random.default_rng(0) | |
| n = 800 | |
| F = rng.normal(size=(n, 17)).astype(np.float32) | |
| F[:, 5] = 42.0 # a constant feature: must map to 0 in both paths | |
| norm = normalize_z(F, 500) # shared offline transform | |
| s3 = Stage3TCN(TCNConfig(sequence_length=500)) | |
| streamed = np.zeros_like(norm) | |
| for i in range(n): | |
| s3.feed(_tick_from_features(F[i], i)) | |
| streamed[i] = np.array(s3._window[-1], dtype=np.float32) | |
| np.testing.assert_allclose(streamed, norm, atol=1e-4) | |
| # constant feature maps to 0 in both | |
| np.testing.assert_allclose(streamed[:, 5], 0.0, atol=1e-6) | |
| np.testing.assert_allclose(norm[:, 5], 0.0, atol=1e-6) | |
| def test_normalize_z_matches_hand_computed_rolling_z(): | |
| """Sanity: normalize_z(window=3) equals a hand-rolled rolling z-score.""" | |
| f = np.arange(10, dtype=np.float64).reshape(10, 1) | |
| out = normalize_z(f.astype(np.float32), 3).squeeze() | |
| # row i is z-scored against rows [max(0,i-2), i] with sample std (ddof=1); | |
| # a single-sample window (std=None) maps to 0 in normalize_z. | |
| expected = np.zeros(10) | |
| for i in range(10): | |
| seg = f[max(0, i - 2): i + 1, 0] | |
| if len(seg) < 2: | |
| expected[i] = 0.0 | |
| else: | |
| expected[i] = (seg[-1] - seg.mean()) / seg.std(ddof=1) | |
| np.testing.assert_allclose(out, expected, atol=1e-5) |