import pandas as pd,numpy as np,glob,os,re pd.set_option('display.width',250);pd.set_option('display.max_rows',200) out=open('results/options_report.txt','w') def pr(*a): s=' '.join(str(x) for x in a); print(s); out.write(s+'\n') def load(pattern): fr=[] for f in glob.glob(pattern): d=pd.read_parquet(f); m=re.search(r'_(\d{4}-\d\d-\d\d)_(\d+)(CE|PE)\.parquet',f) d['expiry']=m.group(1); d['strike']=int(m.group(2)); d['type']=m.group(3); fr.append(d[['ts','close','volume','oi','expiry','strike','type']]) return pd.concat(fr) LOT=65 cfg=[('2026-10-23','../lows/data/opt/2026-10-23_2026-10-27_*.parquet',[('13:20','13:37'),('13:37','13:41'),('13:41','13:47'),('13:47','13:54'),('13:54','14:10')]), ('2026-10-13','data/opt_2026-10-13_*.parquet',[('09:15','09:30'),('09:30','10:00'),('14:15','14:30'),('14:30','14:45'),('14:45','15:00'),('15:00','15:14')])] for day,pat,phases in cfg: o=load(pat); o['ts']=pd.to_datetime(o.ts) pr('\n=====',day,'contracts',o.groupby(['expiry','type']).size().to_dict()) rows=[] for e,g in o.groupby('expiry'): for a,b in phases: ta,tb=pd.Timestamp(f'{day} {a}'),pd.Timestamp(f'{day} {b}') for t in ['CE','PE']: gg=g[g.type==t] piv=gg.pivot_table(index='ts',columns='strike',values='oi').ffill() # OI carried forward only between prints of the same contract (no fabrication of price) vol=gg[(gg.ts>ta)&(gg.ts<=tb)].groupby('strike').volume.sum() if ta not in piv.index or tb not in piv.index: continue doi=(piv.loc[tb]-piv.loc[ta])/LOT px=gg.pivot_table(index='ts',columns='strike',values='close') for k in doi.index: rows.append(dict(expiry=e,phase=f'{a}->{b}',type=t,strike=k,dOI_lots=doi[k],vol_lots=vol.get(k,0)/LOT,px_a=px[k].get(ta,np.nan),px_b=px[k].get(tb,np.nan))) R=pd.DataFrame(rows); R.to_csv(f'results/options_strike_phases_{day}.csv',index=False) # bands relative to spot at phase start spot=pd.read_csv('../data/1m/NIFTY_1m.csv',parse_dates=['ts']); spot['ts']=spot.ts.dt.tz_localize(None); spot=spot.set_index('ts').close S=[] for (e,ph,t),g in R.groupby(['expiry','phase','type']): s0=spot[pd.Timestamp(f"{day} {ph[:5]}")]; atm=round(s0/50)*50 itm=g[(g.strikeatm)]; otm=g[(g.strike>atm) if t=='CE' else (g.strike09:30' for kp in ([key,'13:41->13:47'] if day=='2026-10-23' else [key,'14:30->14:45']): x=R[R.phase==kp].copy(); x=x.reindex(x.dOI_lots.abs().sort_values(ascending=False).index).head(10); pr('\nTop |dOI| strikes',kp); pr(x.round(1).to_string(index=False)) out.close()