| import pytest |
| import pandas as pd |
| import numpy as np |
| from src.data.hf_loader import HuggingFaceETFLoader |
| from src.features.momentum import FeaturePipeline |
| from src.labels.excess_returns import calculate_forward_excess_returns |
|
|
|
|
| def test_zero_future_leakage(): |
| """ |
| CRITICAL UNIT TEST: |
| Verifies that for every observation at date t, all feature values depend strictly on history <= t, |
| and forward label evaluation starts at date >= t+1. |
| """ |
| |
| df_raw = HuggingFaceETFLoader.generate_synthetic_prices(["SPY", "QQQ", "TLT"], num_days=100) |
| |
| |
| pipeline = FeaturePipeline(lookback_windows=[5, 20]) |
| df_feat = pipeline.transform(df_raw) |
|
|
| |
| df_labeled = calculate_forward_excess_returns(df_feat, horizons=[5]) |
|
|
| |
| |
| valid_rows = df_labeled.dropna(subset=["fwd_ret_5d", "cs_z_mom_20d"]) |
| |
| for idx, row in valid_rows.iterrows(): |
| date_t = row["date"] |
| symbol = row["symbol"] |
| |
| |
| past_prices = df_raw[(df_raw["symbol"] == symbol) & (df_raw["date"] <= date_t)]["close"] |
| assert len(past_prices) >= 20, "Feature must have 20 historical prices" |
|
|
| |
| future_prices = df_raw[(df_raw["symbol"] == symbol) & (df_raw["date"] > date_t)]["close"] |
| assert len(future_prices) >= 6, "Forward label requires future prices > date_t" |
|
|
| print("[TEST PASSED] Zero future leakage verified successfully!") |
|
|
|
|
| if __name__ == "__main__": |
| test_zero_future_leakage() |
|
|