import pandas as pd import requests import uvicorn import hashlib import json import time import threading import urllib.parse from datetime import datetime from fastapi import FastAPI, Request, Response, Cookie from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse from typing import Optional # ========================================== # 1. CONFIGURATION # ========================================== CLIENT_ID = "K4F0VGIBE1-100" SECRET_KEY = "8V31L6J578" REDIRECT_URI = "https://topsecrettraders-depthchainindiaq.hf.space/callback" app = FastAPI() # ========================================== # 2. AUTONOMOUS DATA ENGINE # ========================================== SYSTEM_STATUS = { "ready": False, "msg": "Booting System...", "source": "Initializing" } # Database Structure # { "index": [], "stocks": [ {r, w, eq, fut, beq, fl} ] } DB = { "index": [], "stocks": [] } UPDATE_LOCK = threading.Lock() def get_nse_constituents(): """ Connects DIRECTLY to NSE to get the live NIFTY 50 list. """ url = "https://www.nseindia.com/api/equity-stockIndices?index=NIFTY 50" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': '*/*', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': 'https://www.nseindia.com/' } session = requests.Session() try: print(">>> [NSE] Handshake...") session.get("https://www.nseindia.com", headers=headers, timeout=5) print(">>> [NSE] Fetching Constituents...") response = session.get(url, headers=headers, timeout=5) if response.status_code == 200: data = response.json().get('data', []) stocks = [x for x in data if x['priority'] == 0] total_ffmc = sum(x.get('ffmc', 0) for x in stocks) result = {} for x in stocks: sym = x['symbol'] ffmc = x.get('ffmc', 0) wgt = (ffmc / total_ffmc) * 100 if total_ffmc > 0 else 0 result[sym] = wgt print(f">>> [NSE] Success. Loaded {len(result)} Symbols.") return result else: print(f">>> [NSE] Failed: Status {response.status_code}") return None except Exception as e: print(f">>> [NSE] Connection Error: {e}") return None def update_db(): global SYSTEM_STATUS, DB if not UPDATE_LOCK.acquire(blocking=False): return try: SYSTEM_STATUS["msg"] = "Connecting to NSE..." nse_data = get_nse_constituents() if not nse_data: SYSTEM_STATUS["msg"] = "NSE Connection Failed. Retrying..." time.sleep(2) return now_ts = int(time.time()) - 86400 # ------------------------------------ # FETCH NSE FUTURES (NSE_FO.csv) # ------------------------------------ SYSTEM_STATUS["msg"] = "Downloading NSE Futures..." csv_nse = "https://public.fyers.in/sym_details/NSE_FO.csv" try: df_nse = pd.read_csv(csv_nse, usecols=[0, 8, 9, 13], names=['Token', 'Expiry', 'Symbol', 'Inst'], header=0, on_bad_lines='skip') df_nse = df_nse[(df_nse['Expiry'] > now_ts) & (df_nse['Symbol'].str.endswith('FUT'))] except Exception as e: print(f">>> [NSE CSV] Error: {e}") df_nse = pd.DataFrame(columns=['Token', 'Expiry', 'Symbol', 'Inst']) # ------------------------------------ # FETCH BSE EQUITY (BSE_CM.csv) # ------------------------------------ SYSTEM_STATUS["msg"] = "Downloading BSE Master..." csv_bse = "https://public.fyers.in/sym_details/BSE_CM.csv" bse_map = {} try: # Fyers BSE CM CSV Structure (approx): # We usually find the Symbol in Col 9 (e.g., BSE:RELIANCE-A) and name/root elsewhere. # We will read specific columns to locate the symbol string. # Index 9 usually contains 'BSE:RELIANCE-A'. # We will perform a smart extraction. df_bse = pd.read_csv(csv_bse, header=None, usecols=[9], names=['Symbol'], on_bad_lines='skip') # Build a map: Root -> Full BSE Symbol # Logic: Parse "BSE:RELIANCE-A" -> Root "RELIANCE" for _, row in df_bse.iterrows(): full_sym = str(row['Symbol']) # e.g., BSE:RELIANCE-A if full_sym.startswith("BSE:"): # Extract Root: Remove 'BSE:' prefix and suffix (after last dash) # Example: BSE:RELIANCE-A -> RELIANCE # Example: BSE:M&M-A -> M&M # Handling potential complex cases safely try: clean = full_sym.replace("BSE:", "") if "-" in clean: # Split by last dash to separate Group (A, B, T, etc.) parts = clean.rsplit('-', 1) root = parts[0] bse_map[root] = full_sym # Also store sanitized version for special chars if '&' in root: bse_map[root.replace('&', '_')] = full_sym except: continue print(f">>> [BSE] Mapped {len(bse_map)} symbols.") except Exception as e: print(f">>> [BSE CSV] Error: {e}") SYSTEM_STATUS["msg"] = "Mapping Global Data..." final_stocks = [] target_roots = list(nse_data.keys()) # 1. Map NSE Futures nse_map = {} index_futs = [] for _, row in df_nse.iterrows(): sym = str(row['Symbol']) if "NIFTY" in sym and "BANK" not in sym and "FIN" not in sym and "MID" not in sym: if sym.startswith("NSE:NIFTY") and "NIFTYIT" not in sym: index_futs.append(row) continue for root in target_roots: prefix = f"NSE:{root}" if sym.startswith(prefix): rem = sym[len(prefix):] if len(rem) > 0 and rem[0].isdigit(): if root not in nse_map: nse_map[root] = [] nse_map[root].append(row) break # Construct Final DB for root in target_roots: # --- NSE DATA --- nse_eq_sym = f"NSE:{root}-EQ" nse_futs = [] if root in nse_map: items = sorted(nse_map[root], key=lambda x: x['Expiry']) for x in items[:3]: dt_lbl = datetime.fromtimestamp(x['Expiry']).strftime('%d %b').upper() nse_futs.append({"s": x['Symbol'], "e": x['Expiry'], "l": dt_lbl}) # --- BSE DATA (Using Map) --- # Try exact match first, then sanitized match, then fallback bse_eq_sym = bse_map.get(root) if not bse_eq_sym: # Try M&M -> M_M logic bse_eq_sym = bse_map.get(root.replace('&', '_')) # If still not found (very rare for Nifty 50), we default to NSE EQ to avoid crash, # or leave empty. Let's leave empty so UI shows invalid. if not bse_eq_sym: print(f"!!! [BSE] Missing: {root}") bse_eq_sym = "" final_stocks.append({ "r": root, "w": nse_data[root], "eq": nse_eq_sym, "f": nse_futs, "beq": bse_eq_sym }) final_stocks.sort(key=lambda x: x['w'], reverse=True) # Prepare Index (NSE Nifty) index_items = sorted(index_futs, key=lambda x: x['Expiry'])[:3] final_index = [] for x in index_items: dt_lbl = datetime.fromtimestamp(x['Expiry']).strftime('%d %b').upper() final_index.append({"s": x['Symbol'], "e": x['Expiry'], "l": dt_lbl}) DB = { "index": final_index, "stocks": final_stocks } SYSTEM_STATUS["ready"] = True SYSTEM_STATUS["msg"] = "Live" SYSTEM_STATUS["source"] = "NSE & BSE Live API" except Exception as e: print(f"Error: {e}") SYSTEM_STATUS["msg"] = f"Error: {str(e)[:20]}" finally: UPDATE_LOCK.release() @app.on_event("startup") def startup(): t = threading.Thread(target=update_db) t.start() # ========================================== # 3. ROUTES # ========================================== @app.get("/") def home(access_token: Optional[str] = Cookie(None)): if not access_token: state = "init" enc_redirect = urllib.parse.quote(REDIRECT_URI, safe="") auth_url = (f"https://api-t1.fyers.in/api/v3/generate-authcode?client_id={CLIENT_ID}&redirect_uri={enc_redirect}&response_type=code&state={state}") return HTMLResponse(f"""

BLOOMBERG NIFTY 50

AUTONOMOUS DATA TERMINAL

LOGIN
""") return HTMLResponse(FRONTEND.replace("{{USER_TOKEN}}", access_token)) @app.get("/callback") def callback(auth_code: Optional[str]=None, code: Optional[str]=None): c = auth_code or code h = hashlib.sha256(f"{CLIENT_ID}:{SECRET_KEY}".encode()).hexdigest() r = requests.post("https://api-t1.fyers.in/api/v3/validate-authcode", json={"grant_type":"authorization_code","appIdHash":h,"code":c}) if r.status_code==200: resp = RedirectResponse("/") resp.set_cookie("access_token", f"{CLIENT_ID}:{r.json()['access_token']}", httponly=True) return resp return JSONResponse(r.json()) @app.get("/logout") def logout(): r = RedirectResponse("/") r.delete_cookie("access_token") return r @app.get("/api/config") def get_conf(expiry: int = 0): if not SYSTEM_STATUS["ready"] or not DB["stocks"]: if not UPDATE_LOCK.locked(): threading.Thread(target=update_db).start() return JSONResponse({"s": "wait", "msg": SYSTEM_STATUS["msg"]}) idx = max(0, min(expiry, 2)) stk_list = [] # Index Info idx_sym = DB["index"][idx]["s"] if len(DB["index"]) > idx else "" idx_lbl = DB["index"][idx]["l"] if len(DB["index"]) > idx else "--" for s in DB["stocks"]: # Extract NSE Future nse_f = s["f"][idx]["s"] if len(s["f"]) > idx else "" nse_fl = s["f"][idx]["l"] if len(s["f"]) > idx else "" stk_list.append({ "r": s["r"], "w": s["w"], "eq": s["eq"], # NSE Cash "fut": nse_f, # NSE Future "fl": nse_fl, # NSE Future Date "beq": s["beq"] # BSE Cash (Correct Mapped Symbol) }) return JSONResponse({ "s": "ok", "idx": idx_sym, "idx_l": idx_lbl, "stocks": stk_list }) # ========================================== # 4. FRONTEND (BLOOMBERG STYLE) # ========================================== FRONTEND = """ NIFTY 50 TERMINAL
NSE
BSE
EQ
FUT
MARKET DEPTH --
NIFTY FUT --.-- --%
NET: --
""" if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)