# shared option-trade simulator (copied from step3_trades.py) # 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' 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))