Spaces:
Running
Running
| 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() | |