# Score adjudicated fresh calls on real 1m option bars + random benchmark (PREREG YOUTUBE-CALLS) import sys,re,numpy as np,pandas as pd; sys.path.insert(0,'scripts'); from score import score from scipy import stats d=pd.read_csv('data/review_adjudicated.csv'); d=d[d.label=='F'].copy(); o=pd.read_csv('data/F_ocr.csv'); d=d.merge(o,on='cid',how='left') P=pd.read_parquet('/workspace/research/flow-forensics-2026-10/lows/data/minute_panel_v.parquet',columns=['date','hm','close']); P=P[P.date>='2026-08-01'] NS=P.set_index(['date','hm']).close S=pd.read_parquet('/workspace/nifty-s3/timeframes/sensex_1m/sensex.parquet',columns=['timestamp','close']); S=S[S.timestamp>='2026-08-01'] SS=pd.Series(S.close.values,index=pd.MultiIndex.from_arrays([S.timestamp.dt.normalize(),S.timestamp.dt.strftime('%H:%M')])) def pick(r,side): lab=(r.top if side=='CE' else r.bot) if isinstance(r.top if side=='CE' else r.bot,str) else '' c=[x[:5] for x in str(lab).split(';') if x.endswith(side)] if not c: # label may sit in other panel crop c=[x[:5] for x in (str(r.top)+';'+str(r.bot)).split(';') if x.endswith(side)] c=[x for x in c if 20000<=int(x)<=30000 or 60000<=int(x)<=90000] if c: return int(c[0]),'ocr' if isinstance(r.spoken_strike,(int,float)) and r.spoken_strike==r.spoken_strike: return int(r.spoken_strike),'spoken' return None,None rows=[] for r in d.itertuples(): side=r.side_fix if isinstance(r.side_fix,str) else (r.side if isinstance(r.side,str) else None) base=dict(cid=r.cid,vid=r.vid,date=r.date,entry_ist=r.entry_ist,side=side,note=r.note,claim=r.claim if isinstance(r.claim,str) else 'no_claim',url=f'https://www.youtube.com/watch?v={r.vid}&t={int(r.entry_off_s)}s') if side is None: rows.append({**base,'status':'unscorable','why':'side_unknown'}); continue K,ks=pick(r,side) if K is None: rows.append({**base,'status':'unscorable','why':'strike_unknown'}); continue ix='NIFTY' if K<30000 else 'SENSEX' sl=r.sl_fix if r.sl_fix==r.sl_fix else (r.sl_pts if r.sl_pts==r.sl_pts else None); slf=sl is None if slf: sl=12.0 if ix=='NIFTY' else 20.0 tg=r.tg_fix if r.tg_fix==r.tg_fix else (r.tg_pts if r.tg_pts==r.tg_pts else None); tgf=tg is None if tgf: tg=2*sl hm=r.entry_ist[11:16]; z=score(ix,r.date,K,side,hm,float(sl),float(tg)) rows.append({**base,'idx':ix,'strike':K,'strike_src':ks,'sl':sl,'sl_fallback':slf,'tg':tg,'tg_fallback':tgf,**z,'why':z.get('why')}) R=pd.DataFrame(rows) # random benchmark: same minute/side/SL/TG, ATM strike, 30 random other window days with data days=sorted(set(P.date.dt.strftime('%Y-%m-%d'))&set(pd.read_csv('results/trading_day_coverage.csv').date)); days=[x for x in days if '2026-08-25'<=x<='2026-10-23'] rng=np.random.default_rng(11); rb=[] for r in R[R.status=='scored'].itertuples(): pool=[x for x in days if x!=r.date and (r.idx=='NIFTY' or x<='2026-10-05')] for x in rng.choice(pool,min(30,len(pool)),replace=False): hm=r.entry_ist[11:16]; sp=(NS if r.idx=='NIFTY' else SS).get((pd.Timestamp(str(x)),hm)) if sp is None or sp!=sp: continue step=50 if r.idx=='NIFTY' else 100; K=int(round(sp/step)*step); z=score(r.idx,str(x),K,r.side,hm,float(r.sl),float(r.tg)) if z['status']=='scored': rb.append(dict(cid=r.cid,day=x,net=z['net_pts'])) RB=pd.DataFrame(rb); R.to_csv('results/yt_calls_scored.csv',index=False); RB.to_csv('results/yt_random_bench.csv',index=False) sc=R[R.status=='scored']; print(R.status.value_counts().to_dict(), R.why.value_counts().to_dict()) v=sc.net_pts; m=RB.groupby('cid').net.mean().reindex(sc.cid) reps=[np.mean([rng.choice(RB[RB.cid==c].net.values) for c in sc.cid if (RB.cid==c).any()]) for _ in range(2000)] summ=dict(n=len(v),mean=v.mean(),median=v.median(),win=(v>0).mean()*100,total=v.sum(),p=stats.ttest_1samp(v,0,alternative='greater').pvalue,rand_mean=float(np.mean(reps)),rand_pctile=float((np.array(reps)