# Expiry-day 1m option slices (expiring contract only), real bars, no fill. Output data/ec_cache/{IX}_{E}.parquet: mi,typ,K,o,h,l,c,v,oi,src import pandas as pd,numpy as np,glob,os,pickle O='/workspace/nifty-options-s3/data/options'; C='/workspace/research/popular-strategies/data/ec_cache' def mi(ts): return ts.dt.hour*60+ts.dt.minute-555 def save(ix,e,d,src): d=d[(d.mi>=0)&(d.mi<375)].drop_duplicates(['mi','typ','K']); d['src']=src; d.to_parquet(f'{C}/{ix}_{e.date()}.parquet',index=False); print(ix,e.date(),len(d),d.K.nunique(),src,flush=True) for ix,sub in (('NIFTY','1m'),('SENSEX','sensex/1m')): for f in sorted(glob.glob(f'{O}/{sub}/by_expiry/*.parquet')): e=pd.Timestamp(os.path.basename(f)[:10]); out=f'{C}/{ix}_{e.date()}.parquet' if os.path.exists(out): continue d=pd.read_parquet(f,columns=['timestamp','date','expiry','option_type','strike','open','high','low','close','volume','open_interest'],filters=[('date','==',e)]) d=d[(d.expiry==e)] if len(d)==0: print(ix,e.date(),'NO expiry-day rows'); continue d=pd.DataFrame(dict(mi=mi(d.timestamp),typ=d.option_type,K=d.strike,o=d.open,h=d.high,l=d.low,c=d.close,v=d.volume.astype(float),oi=d.open_interest.astype(float))) save(ix,e,d,'by_expiry') P=pickle.load(open('/workspace/research/flow-forensics-2026-10/recov/data/opt_panels.pkl','rb')) for (dt,e),p in P.items(): if dt!=e or os.path.exists(f'{C}/NIFTY_{e.date()}.parquet'): continue rows=[] for (f,t,K) in p.columns: if f!='close': continue g=pd.DataFrame({k:p[(k,t,K)] for k in ['open','high','low','close','oi']}).dropna(subset=['close']) ix=np.array([int(x[:2])*60+int(x[3:])-555 for x in g.index]) rows.append(pd.DataFrame(dict(mi=ix,typ=t,K=K,o=g.open.values,h=g.high.values,l=g.low.values,c=g.close.values,v=np.nan,oi=g.oi.values))) save('NIFTY',e,pd.concat(rows),'opt_panels') a=pd.read_parquet('/workspace/market-live-stack/bots/upstox/eod/2026-10-27/atm_options_1m_2026-10-27.parquet') a=a[a.expiry.astype(str).str[:10]=='2026-10-27']; ts=pd.to_datetime(a.timestamp) save('NIFTY',pd.Timestamp('2026-10-27'),pd.DataFrame(dict(mi=mi(ts),typ=a.option_type,K=a.strike.astype(float),o=a.open,h=a.high,l=a.low,c=a.close,v=a.volume.astype(float),oi=a.open_interest.astype(float))),'eod_atm')