import requests import uvicorn import json import threading import os import time from fastapi import FastAPI from fastapi.responses import HTMLResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware # ========================================== # 1. CONFIGURATION & BACKEND # ========================================== app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) SYMBOL_FILE = "symbols.json" SYMBOL_CACHE = [] 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", "Referer": "https://gocharting.com/", "Accept": "application/json" } # --- BACKGROUND DATABASE WORKER --- def update_symbol_master(): global SYMBOL_CACHE print(">>> [SYSTEM] Updating Symbol DB...") temp_list = [] defaults = [ {"symbol": "NIFTY", "name": "Nifty 50", "exchange": "NSE"}, {"symbol": "BANKNIFTY", "name": "Nifty Bank", "exchange": "NSE"}, {"symbol": "NIFTYFINANCE", "name": "Nifty Fin Service", "exchange": "NSE"} ] temp_list.extend(defaults) for i in range(65, 91): # A-Z letter = chr(i) try: url = f"https://gocharting.com/api/instruments_with_options/search?q={letter}" r = requests.get(url, headers=HEADERS, timeout=5) if r.status_code == 200: data = r.json() results = data.get("payload", {}).get("results", []) for item in results: details = item.get("item", {}) if details.get("has_options_chain"): temp_list.append({ "symbol": details.get("symbol"), "name": details.get("name"), "exchange": details.get("exchange") }) time.sleep(0.1) except: pass seen = set() unique_list = [] for d in temp_list: t = tuple(d.items()) if t not in seen: seen.add(t) unique_list.append(d) SYMBOL_CACHE = unique_list with open(SYMBOL_FILE, "w") as f: json.dump(SYMBOL_CACHE, f) print(f">>> [SYSTEM] DB Updated: {len(SYMBOL_CACHE)} symbols.") def load_cache_on_startup(): global SYMBOL_CACHE if os.path.exists(SYMBOL_FILE): try: with open(SYMBOL_FILE, "r") as f: SYMBOL_CACHE = json.load(f) except: pass if len(SYMBOL_CACHE) < 5: threading.Thread(target=update_symbol_master).start() @app.on_event("startup") def startup_event(): load_cache_on_startup() # ========================================== # 2. API ROUTES # ========================================== @app.get("/") def home(): return HTMLResponse(FRONTEND_HTML) @app.get("/api/search") def search_symbols(q: str): q = q.upper() results = [item for item in SYMBOL_CACHE if q in item["symbol"] or q in item["name"].upper()] return JSONResponse(results[:15]) @app.get("/api/fetch") def proxy_data(symbol: str, exchange: str): # This fetches the FULL chain (all expiries) url = "https://gocharting.com/api/options/chain" params = {"exchange": exchange, "segment": "SPOT", "symbol": symbol, "quote_underlying": "true", "quote_chain": "true"} try: r = requests.get(url, headers=HEADERS, params=params, timeout=10) return JSONResponse(r.json()) except Exception as e: return JSONResponse({"status": "error", "msg": str(e)}, status_code=500) # ========================================== # 3. FRONTEND (MULTI-EXPIRY AGGREGATOR) # ========================================== FRONTEND_HTML = """