1import numpy as np2import pytest34from chronos_x.data.synthetic import synthetic_bars5from chronos_x.data.features import build_grid, N_DAY6from chronos_x.data.quality import qa_report789@pytest.fixture(scope="module")10def grid():11 df, actions = synthetic_bars(start="2024-01-02", end="2024-02-15", seed=1, split=("2024-01-22", 4.0))12 arrays, meta = build_grid(df, actions, "2024-01-02", "2024-02-15", "SYN", "stock")13 return df, actions, arrays, meta141516def test_qa_clean(grid):17 df, *_ = grid18 q = qa_report(df)19 assert q["ohlc_inconsistent"] == 0 and q["duplicates"] == 0 and q["off_interval"] == 0202122def test_extended_hours_excluded_and_coverage(grid):23 df, actions, arrays, meta = grid24 assert meta.counts["rows_extended_hours"] > 025 assert 0.95 < meta.counts["coverage"] <= 1.026 assert arrays["mask"].shape[1] == 391272829def test_split_adjusted_prices_continuous(grid):30 df, actions, arrays, meta = grid31 dates = meta.session_dates32 i = dates.index("2024-01-22")33 base, mask = arrays["base"], arrays["mask"]34 # last close of day before ex-date vs first open on ex-date: after explicit split adjustment the jump is small35 k1 = np.max(np.flatnonzero(mask[i - 1])); k0 = np.argmax(mask[i] > 0)36 jump = base[i, k0, 0] - base[i - 1, k1, 3]37 assert abs(jump) < 0.0538 assert meta.split_factors[i - 1] == 4.0 and meta.split_factors[i] == 1.0394041def test_buckets_consistent_with_minutes(grid):42 df, actions, arrays, meta = grid43 base, mask, b5, m5, b30, m30 = (arrays[k] for k in ("base", "mask", "b5", "m5", "b30", "m30"))44 s = 545 for j in range(78):46 sl = slice(5 * j, 5 * j + 5)47 pres = mask[s, sl] > 048 if not pres.any():49 assert m5[s, j] == 050 continue51 assert m5[s, j] == 152 assert np.isclose(b5[s, j, 3], base[s, sl, 3][pres][-1])53 assert np.isclose(b5[s, j, 0], base[s, sl, 0][pres][0])54 assert np.isclose(b5[s, j, 1], base[s, sl, 1][pres].max())55 assert np.isclose(b5[s, j, 4], np.log1p(np.expm1(base[s, sl, 4][pres].astype(np.float64)).sum()), atol=1e-4)56 # closing print (slot 390) is never inside a bucket57 assert b30.shape[1] == 13 and b5.shape[1] == 78585960def test_seasonal_profile_is_past_only(grid):61 df, actions, arrays, meta = grid62 seas, base, mask = arrays["seas"], arrays["base"], arrays["mask"]63 assert np.isnan(seas[0]).all()64 s, k = 10, 10065 past = [base[t, k, 4] for t in range(max(0, s - 20), s) if mask[t, k]]66 assert np.isclose(seas[s, k], np.mean(past), atol=1e-5)67