# Upstox HISTORICAL candles only (v2 historical-candle). No live feeds. Token never printed. import requests,pandas as pd,gzip,json,time,os,urllib.parse B='/workspace/research/flow-forensics-2026-10/data'; os.makedirs(f'{B}/1m',exist_ok=True) tok=open('/home/box/.secrets/upstox_access_token').read().strip() H={'User-Agent':'Mozilla/5.0','Accept':'application/json','Authorization':'Bearer '+tok} ins=pd.DataFrame(json.load(gzip.open(f'{B}/NSE.json.gz'))) cons=pd.read_csv('/workspace/market-live-stack/bots/upstox/nifty50_constituents.csv') eq=ins[(ins.segment=='NSE_EQ')&(ins.instrument_type=='EQ')].set_index('isin') keys={r.Symbol:eq.loc[r['ISIN Code'],'instrument_key'] for _,r in cons.iterrows()} keys.update({'NIFTY':'NSE_INDEX|Nifty 50','BANKNIFTY':'NSE_INDEX|Nifty Bank','NIFTYFUT':'NSE_FO|48704','BANKNIFTYFUT':'NSE_FO|48699','NIFTYFUT_NOV':'NSE_FO|61471','INDIAVIX':'NSE_INDEX|India VIX'}) json.dump(keys,open(f'{B}/keys.json','w'),indent=1) def get(k,iv,to,fr): u=f'https://api.upstox.com/v2/historical-candle/{urllib.parse.quote(k,safe="")}/{iv}/{to}/{fr}' for i in range(4): r=requests.get(u,headers=H,timeout=30) if r.status_code==200: return r.json()['data']['candles'] print('retry',k,iv,r.status_code,r.text[:120]); time.sleep(2+i*3) return [] cols=['ts','open','high','low','close','volume','oi'] for s,k in keys.items(): d=pd.DataFrame(get(k,'day','2026-10-23','2026-08-01'),columns=cols); d.to_csv(f'{B}/1m/{s}_day.csv',index=False) rows=[] for to,fr in [('2026-10-23','2026-10-01'),('2026-09-30','2026-09-08')]: rows+=get(k,'1minute',to,fr); time.sleep(0.25) m=pd.DataFrame(rows,columns=cols).drop_duplicates('ts').sort_values('ts'); m.to_csv(f'{B}/1m/{s}_1m.csv',index=False) print(s,k,'day',len(d),'1m',len(m),m.ts.min() if len(m) else None,m.ts.max() if len(m) else None,flush=True)