stockproject / backtesting /framework /test_1_1_ic_analysis.py
harshisageek's picture
deploy: clean history for HuggingFace
1cd56b6
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()