import pandas as pd,numpy as np from scipy.stats import fisher_exact rng=np.random.default_rng(11) E=pd.read_csv('results/p2_events.csv'); E['date']=pd.to_datetime(E.date) T=pd.read_csv('results/p2_option_trades.csv'); T['date']=pd.to_datetime(T.date) FUT0=pd.Timestamp('2024-10-21'); SPLIT=pd.Timestamp('2025-07-01') def per(d): return np.where(d=j[1]).astype(float).where(E[j[0]].notna()) E['hold60']=E.hold60.map({True:1.0,False:0.0,'True':1.0,'False':0.0}); E['hold_day']=E.hold_day.map({True:1.0,False:0.0,'True':1.0,'False':0.0}) M=['r40_15','r40_30','r80_30','r80_60','r120_60','hold60','hold_day','to_1514'] def summ(x): return pd.Series({'n':len(x),**{m:x[m].mean()*(100 if m!='to_1514' else 1) for m in M}}) F=E[E.date>=FUT0] # outcome table per config and period cfg=F[F.kind=='signal'].groupby(['X','V','win','oi','period']).apply(summ) ctrl=F[F.kind!='signal'].groupby(['kind','X','period']).apply(summ) # by year for signals and controls yr=F.groupby(['kind','X','V','win','oi','year']).apply(summ) # price-only, all years 2022-2026 po=E[E.kind.isin(['price_bar','at_low_any'])].copy(); po['period2']=np.where(po.date=3 and len(rr)>n: sims=np.array([rr.pnl.sample(n,replace=True,random_state=int(s)).mean() for s in rng.integers(0,1e9,2000)]) p=(sims>=m).mean(); rm=rr.pnl.mean() else: p=np.nan; rm=np.nan ct=TT[(TT.kind=='control_quiet')&(TT.X==X)&(TT['style']==style)]; ct=ct if prd=='all' else ct[per(ct.date)==prd] row.update({f'{style}_n':n,f'{style}_avg':m,f'{style}_win%':(tr.pnl>0).mean()*100 if n else np.nan,f'{style}_total':tr.pnl.sum(),f'{style}_rand_avg':rm,f'{style}_p_vs_random':p,f'{style}_ctrl_avg':ct.pnl.mean(),f'{style}_ctrl_n':len(ct)}) res.append(row) Rz=pd.DataFrame(res) def holm(p): p=np.asarray(p,float); o=np.argsort(np.where(np.isnan(p),2,p)); m=np.isfinite(p).sum(); adj=np.full(len(p),np.nan); run=0 for r,i in enumerate(o): if not np.isfinite(p[i]): continue run=max(run,min(1,(m-r)*p[i])); adj[i]=run return adj for prd in ['train','test','all']: idx=Rz.period==prd for c in ['p_fisher','scale_p_vs_random','sl_only_p_vs_random']: Rz.loc[idx,c+'_holm']=holm(Rz.loc[idx,c].values) Rz.to_csv('results/p2_config_stats.csv',index=False) cfg.to_csv('results/p2_outcomes_by_config.csv'); ctrl.to_csv('results/p2_outcomes_controls.csv'); yr.to_csv('results/p2_outcomes_by_year.csv'); pot.to_csv('results/p2_priceonly_train_test.csv'); poy.to_csv('results/p2_priceonly_by_year.csv') # random benchmark summary rs=TT[TT.kind.isin(['random','control_quiet','price_bar'])].groupby(['kind','style']).pnl.agg(['count','mean','median',lambda x:(x>0).mean()*100]).round(2) rs.to_csv('results/p2_benchmarks.csv') pd.set_option('display.width',250) print(Rz[Rz.period!='all'][['X','V','win','oi','period','n','r40_30','ctrl_r40_30','p_fisher','p_fisher_holm','hold60','ctrl_hold60','scale_n','scale_avg','scale_win%','scale_rand_avg','scale_p_vs_random','scale_p_vs_random_holm','scale_ctrl_avg']].round(3).to_string()) print(rs); print(ctrl.round(1).to_string()); print(pot.round(1).to_string())