# ============================================================================= # Project : anomaly-atlas # File : benchmarks/synthetic/test_gate_expd.py # Purpose : ยง8.1 gate for expD: grids, LOCF masks, both-fresh de-artifacting # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Data src : hfmarketdata.io (sole data source) # Created : 2026-08-12 # Modified : 2026-08-12 # Platform : macOS / Apple Silicon (arm64) # License : All rights reserved (research code) # ============================================================================= """Gate the expD alignment primitives before real data: * grid building from bar dicts is exact (slots, NaN gaps, RTH filter); * LOCF returns carry correct fresh-masks; missing days never leak; * the both-fresh treatment REMOVES the planted non-synchronicity artifact that the raw LOCF join manufactures (the core expD claim). """ from __future__ import annotations import sys from pathlib import Path import numpy as np sys.path.insert(0, str(Path(__file__).resolve().parent)) from generators import correlated_pair # noqa: E402 from anomaly_atlas.data.cleaning import ( both_fresh, locf_day_returns, nan_xcorr, rth_day_grids, ) SEEDS = [1, 2, 3] def bars_for_day(day: str, prices: dict[str, float]) -> list[dict]: return [{"datetime": f"{day} {hhmm}:00", "close": p} for hhmm, p in prices.items()] def test_grid_building_slots_and_rth_filter(): bars = bars_for_day("2024-01-02", {"09:30": 100.0, "09:32": 101.0, "15:59": 102.0}) bars += bars_for_day("2024-01-02", {"04:00": 99.0, "16:00": 103.0}) # outside RTH days = rth_day_grids(bars) g = days["2024-01-02"] assert np.isfinite(g[[0, 2, 389]]).all() assert np.isnan(g[1]) and np.isnan(g[3]) assert np.isfinite(g).sum() == 3 # extended-hours bars excluded def test_locf_masks_and_missing_days(): bars = bars_for_day("2024-01-02", {"09:30": 100.0, "09:32": 101.0, "09:33": 101.5}) days = rth_day_grids(bars) r, fresh = locf_day_returns(days, ["2024-01-02", "2024-01-03"]) assert len(r) == 389 * 2 assert r[0] == 0.0 and not fresh[0] # 09:31 carried forward # 09:31->09:32 return ends fresh but STARTS on a carried value -> not fresh assert abs(r[1]) > 0 and not fresh[1] # 09:32->09:33: both endpoints fresh -> a genuine 1-minute return assert abs(r[2]) > 0 and fresh[2] assert np.isnan(r[389:]).all() # absent day never leaks assert not fresh[389:].any() def synthetic_pair_day_grids(n_days: int, rho: float, p_obs: float, seed: int): """Correlated 1min walks; y observed sparsely. Returns (days_x, days_y, day_list).""" rng = np.random.default_rng(seed) px, py = correlated_pair(n_days * 390, rho=rho, sigma=0.001, seed=seed) days_x, days_y, day_list = {}, {}, [] for d in range(n_days): day = f"2024-02-{d + 1:02d}" day_list.append(day) gx = px[d * 390 : (d + 1) * 390].copy() gy = py[d * 390 : (d + 1) * 390].copy() mask = rng.random(390) < p_obs mask[0] = True gy[~mask] = np.nan days_x[day] = gx days_y[day] = gy return days_x, days_y, day_list def test_both_fresh_removes_planted_nonsync_artifact(): for seed in SEEDS: dx, dy, dl = synthetic_pair_day_grids(25, rho=0.7, p_obs=0.3, seed=seed) rx, fx = locf_day_returns(dx, dl) ry, fy = locf_day_returns(dy, dl) raw = nan_xcorr(rx, ry, 2) assert raw[1] > 0.10 # artifact present in the raw LOCF join bx, by = both_fresh(rx, fx, ry, fy) sync = nan_xcorr(bx, by, 2) assert abs(sync[1]) < 0.05 # and gone on the synchronized subsample assert sync[0] > 0.6 # true contemporaneous corr (rho=0.7) recovered