# Option-buyer trades on every candidate (ATM CE, nearest expiry and skip-0DTE) + random-minute benchmark. Real 1m bars, no filling. import pandas as pd,numpy as np R='/workspace/research/flow-forensics-2026-10/recov'; B='/workspace/research/flow-forensics-2026-10' C=pd.read_csv(f'{R}/results/candidates_scored.csv',parse_dates=['date']) OPT=pd.read_pickle(f'{R}/data/opt_panels.pkl') P=pd.read_parquet(f'{B}/lows/data/minute_panel_v.parquet',columns=['date','hm','close','low']) t=pd.read_csv(f'{R}/data/today/NIFTY.csv'); t['date']=pd.to_datetime(t.ts.str[:10]); t['hm']=t.ts.str[11:16] P=pd.concat([P[P.date>='2024-10-21'],t[['date','hm','close','low']]]); SP={d:g.set_index('hm') for d,g in P.groupby('date')} exps=sorted({e for _,e in OPT}) def nexp(d,skip0=False): c=[e for e in exps if e>=d and not(skip0 and e==d)]; return c[0] if c else None SLIP=3.0 def lot(t): return 25 if t=RL+200 or x>='15:14': sells.append((rem,b.close-SLIP)); rem=0; break # target: +200 from low, or 15:14 if style=='runner': if b.low<=fill-35: px=min(b.open,fill-35) if i>0 else fill-35; sells.append((rem,px-SLIP)); rem=0; break continue if b.low<=stop: px=min(b.open,stop) if i>0 else stop; sells.append((rem,px-SLIP)); rem=0; break if style in('scale','spotstop'): if not t1 and b.high>=fill+20: sells.append((0.4,fill+20-SLIP)); rem-=0.4; t1=True; stop=max(stop,fill) if not t2 and b.high>=fill+40: sells.append((0.3,fill+40-SLIP)); rem-=0.3; t2=True peak=max(peak,b.high) if t2: stop=max(stop,peak-20,fill+20) if rem>1e-9: sells.append((rem,path.close.iloc[-1]-SLIP)) g=sum(f*p for f,p in sells)-fill; c=charges(d,fill,sells) return dict(fill=fill,gross=g,cost=c,pnl=g-c,pnl_rs_lot=(g-c)*lot(d)) rng=np.random.default_rng(11) days=sorted(C.date.unique()); alld=sorted(SP) rnd=[] for _ in range(3000): d=alld[rng.integers(len(alld))]; sp=SP[d]; hs=[h for h in sp.index if '09:20'<=h<='14:14' and sp.close.get(h)==sp.close.get(h)] if not hs: continue h=hs[rng.integers(len(hs))]; rnd.append(dict(date=d,hm=h,entry=sp.close[h],RL=sp.low.loc[:h].min(),kind='random')) EV=pd.concat([C.assign(kind='candidate'),pd.DataFrame(rnd)],ignore_index=True) out=[] for r in EV.itertuples(): K=float(round(r.entry/50)*50) for lab,e in (('nearest',nexp(r.date)),('skip0DTE',nexp(r.date,True))): if e is None: continue for st in ('scale','sl_only','spotstop','runner'): z=trade(r.date,e,K,r.hm,st,RL=r.RL) if z: out.append(dict(idx=r.Index,date=r.date,hm=r.hm,kind=r.kind,expiry_rule=lab,dte=(e-r.date).days,style=st,**z)) TR=pd.DataFrame(out); TR.to_csv(f'{R}/results/trades_all.csv',index=False) print(TR.groupby(['kind','expiry_rule','style']).pnl.agg(['count','mean','median',lambda x:(x>0).mean()*100]).round(2).to_string())