import sys, os import numpy as np, pandas as pd from scipy.stats import spearmanr import warnings; warnings.filterwarnings('ignore') sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) from backtesting.framework.config import STRATEGY_NAME, ACTIVE_SIGNAL_FN, ACTIVE_PARAMS, load_data def run_test_1_1(): print("=" * 80) print(f" TEST 1.1: INFORMATION COEFFICIENT (IC) ANALYSIS - {STRATEGY_NAME}") print("=" * 80) if ACTIVE_SIGNAL_FN is None: print("FAIL: No ACTIVE_SIGNAL_FN defined in config.py") return dc, spy, vf, daily_ret = load_data() raw_signal = ACTIVE_SIGNAL_FN(dc, spy, vf) fwd = dc[vf].pct_change(60).shift(-60) top_n = ACTIVE_PARAMS.get('top_n', 15) ic_vals = [] dates = [] # Calculate IC every 60 days for i in range(200, len(dc)-60, 60): sig = raw_signal.iloc[i].dropna() common = sig.index.intersection(fwd.iloc[i].dropna().index) if len(common) >= top_n: top_stocks = sig[common].nlargest(top_n) corr, _ = spearmanr(top_stocks.values, fwd.iloc[i][top_stocks.index].values) if not np.isnan(corr): ic_vals.append(corr) dates.append(dc.index[i]) if not ic_vals: print("FAIL: Could not calculate IC (Not enough overlap between signal and forward returns).") return ic_series = pd.Series(ic_vals, index=dates) ic_mean = ic_series.mean() ic_std = ic_series.std() n = len(ic_series) t_stat = ic_mean / (ic_std / np.sqrt(n)) if ic_std > 0 else 0 print(f" N observations: {n}") print(f" IC Mean (Top {top_n}): {ic_mean:.4f}") print(f" IC Std Dev: {ic_std:.4f}") print(f" t-statistic: {t_stat:.2f}") print("-" * 80) if t_stat > 2.0: print(f" VERDICT: PASS (Statistically significant edge)") else: print(f" VERDICT: FAIL (t-stat < 2.0)") if __name__ == "__main__": run_test_1_1()