import pandas as pd d=pd.read_csv('results/stream_inventory_raw.csv') d['st']=pd.to_datetime(d.start_ist.str[:19]); d['en']=pd.to_datetime(d.end_ist_est.str[:19]); d['date']=d.st.dt.date import pickle P=pickle.load(open('/workspace/research/flow-forensics-2026-10/recov/data/opt_panels.pkl','rb')) td=sorted(set(pd.Timestamp(k[0]).date() for k in P)); td=[x for x in td if pd.Timestamp('2026-05-06').date()<=x<=pd.Timestamp('2026-10-23').date()] # trading days = days with real Nifty option bars # coverage of market hours 09:15-15:30 per date (union of streams) cov={} for dt_,g in d.dropna(subset=['st']).groupby('date'): mins=set() for _,r in g.iterrows(): a=max(r.st,pd.Timestamp(f'{dt_} 09:15')); b=min(r.en,pd.Timestamp(f'{dt_} 15:30')) if b>a: mins|=set(pd.date_range(a.floor('min'),b,freq='min')) cov[dt_]=len(mins)/375*100 d['market_hours_coverage_pct_day']=d.date.map(cov).round(0) d['trading_day']=d.date.isin(set(td)) d.drop(columns=['st','en']).to_csv('results/stream_inventory.csv',index=False) days=pd.DataFrame({'date':td}); days['streams']=days.date.map(d.groupby('date').size()).fillna(0).astype(int); days['coverage_pct']=days.date.map(cov).fillna(0).round(0) days['flag']=days.apply(lambda r:'NO_STREAM (possible deleted/not streamed)' if r.streams==0 else ('PARTIAL' if r.coverage_pct<80 else ''),axis=1) days.to_csv('results/trading_day_coverage.csv',index=False) print('streams',len(d),'errors',d.error.notna().sum() if 'error' in d else 0,'trading days',len(td),'last data day',td[-1]) print(days.flag.value_counts()); print(days[days.flag!=''].to_string()) print(d[d.error.notna()][['id','title','error']] if 'error' in d else '') print('non-trading-day streams',d[~d.trading_day][['id','date','duration_s']].to_string())