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://topindianhacker-depthchainindiaq.hf.space/callback" app = FastAPI() # ========================================== # 2. AUTONOMOUS DATA ENGINE # ========================================== SYSTEM_STATUS = { "ready": False, "msg": "Booting System...", "source": "Initializing" } # Database Structure DB = { "index": [], "stocks": [] } UPDATE_LOCK = threading.Lock() def get_nse_constituents(): """ Connects DIRECTLY to NSE to get the live NIFTY 50 list. No hardcoded backup. If this fails, the app waits and retries. """ 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: # 1. Handshake (Load Cookies) print(">>> [NSE] Handshake...") session.get("https://www.nseindia.com", headers=headers, timeout=5) # 2. Fetch Data print(">>> [NSE] Fetching Constituents...") response = session.get(url, headers=headers, timeout=5) if response.status_code == 200: data = response.json().get('data', []) # Filter Stocks (Priority 0) stocks = [x for x in data if x['priority'] == 0] # Calculate Weights dynamically based on Free Float Market Cap 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..." # 1. Get Live Data from NSE nse_data = get_nse_constituents() if not nse_data: SYSTEM_STATUS["msg"] = "NSE Connection Failed. Retrying..." time.sleep(2) # Wait before releasing lock to prevent spam return # Exit and let next cycle try SYSTEM_STATUS["msg"] = "Downloading Fyers Master..." # 2. Get Fyers Master CSV csv_url = "https://public.fyers.in/sym_details/NSE_FO.csv" # Reading only necessary columns for speed df = pd.read_csv(csv_url, usecols=[0, 8, 9, 13], names=['Token', 'Expiry', 'Symbol', 'Inst'], header=0, on_bad_lines='skip') # Filter: Futures only & Not Expired now_ts = int(time.time()) - 86400 df = df[(df['Expiry'] > now_ts) & (df['Symbol'].str.endswith('FUT'))] SYSTEM_STATUS["msg"] = "Mapping Symbols..." final_stocks = [] target_roots = list(nse_data.keys()) # 3. Intelligent Mapping (NSE Root -> Fyers Future) # We group Fyers symbols by their Root # Pre-process Fyers symbols into a Dictionary for O(1) lookup # Key: Root, Value: List of rows fyers_map = {} index_futs = [] for _, row in df.iterrows(): sym = str(row['Symbol']) # NSE:RELIANCE26FEBFUT # Handle Index 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 # Handle Stocks # Extract Root logic: NSE:ROOT26... # We iterate NSE roots to find matches for root in target_roots: prefix = f"NSE:{root}" # Check if symbol starts with prefix AND next char is digit (Date) # This prevents partial matches like TATASTEEL matching TATASTEELBSL if sym.startswith(prefix): remainder = sym[len(prefix):] if len(remainder) > 0 and remainder[0].isdigit(): if root not in fyers_map: fyers_map[root] = [] fyers_map[root].append(row) break # 4. Construct Final DB for root in target_roots: if root in fyers_map: # Sort by Expiry items = sorted(fyers_map[root], key=lambda x: x['Expiry']) # Take top 3 futs = [{"s": x['Symbol'], "e": x['Expiry']} for x in items[:3]] final_stocks.append({ "r": root, "w": nse_data[root], "f": futs }) # Sort Stocks by Weightage final_stocks.sort(key=lambda x: x['w'], reverse=True) # Prepare Index index_items = sorted(index_futs, key=lambda x: x['Expiry'])[:3] final_index = [{"s": x['Symbol'], "e": x['Expiry']} for x in index_items] DB = { "index": final_index, "stocks": final_stocks } SYSTEM_STATUS["ready"] = True SYSTEM_STATUS["msg"] = "Live" SYSTEM_STATUS["source"] = "NSE Live API" except Exception as e: print(f"Error: {e}") SYSTEM_STATUS["msg"] = "Processing Error" 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): """ Returns symbols for requested expiry. If DB is empty (NSE failed), it triggers a retry. """ 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 = [] for s in DB["stocks"]: # Safety check if future exists for that month if len(s["f"]) > idx: stk_list.append({"r": s["r"], "w": s["w"], "s": s["f"][idx]["s"]}) idx_sym = DB["index"][idx]["s"] if len(DB["index"]) > idx else "" return JSONResponse({"s": "ok", "idx": idx_sym, "stocks": stk_list}) # ========================================== # 4. FRONTEND (BLOOMBERG STYLE) # ========================================== FRONTEND = """ NIFTY 50 TERMINAL
MARKET SUMS
NIFTY 50 --.-- --%
NET: --
""" if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)