import gradio as gr import yfinance as yf import yfinance.shared as shared import pandas as pd from datetime import datetime, timedelta def stringify_keys(obj): """Recursively convert all dictionary keys to strings (for safe JSON encoding).""" if isinstance(obj, dict): return {str(k): stringify_keys(v) for k, v in obj.items()} elif isinstance(obj, list): return [stringify_keys(v) for v in obj] else: return obj def fetch_data(symbol: str, exchange: str, request: gr.Request = None): print(f"[DEBUG] Received symbol='{symbol}', exchange='{exchange}' at {datetime.utcnow().isoformat()}") if request: print(f"[DEBUG] Client IP: {request.client.host}") # Validate symbol if not symbol or not symbol.strip(): return {"error": "Symbol cannot be empty"} symbol = symbol.strip().upper() ticker = f"{symbol}.NS" if exchange.upper() == "NSE" else symbol print(f"[DEBUG] Fetching for: {ticker}") try: shared._ERRORS.clear() tk = yf.Ticker(ticker) # --- 1️⃣ Info dictionary --- info = stringify_keys(tk.info if tk.info else {}) # --- 2️⃣ Historical (1 year, daily) --- hist = tk.history(period="1y", interval="1d", auto_adjust=True) if isinstance(hist.columns, pd.MultiIndex): hist.columns = hist.columns.get_level_values(0) hist = hist.reset_index() # ✅ Convert to YYYY-MM-DD format only hist["Date"] = pd.to_datetime(hist["Date"]).dt.strftime("%Y-%m-%d") hist_json = hist.round(2).to_dict(orient="records") # --- 3️⃣ Intraday (1 day, 5 min interval) --- intraday = tk.history(period="1d", interval="5m", auto_adjust=True) if not intraday.empty: if isinstance(intraday.columns, pd.MultiIndex): intraday.columns = intraday.columns.get_level_values(0) intraday = intraday.reset_index() # ✅ Add 5h30m to each datetime and keep only hh:mm:ss intraday["Datetime"] = ( pd.to_datetime(intraday["Datetime"]) #pd.to_datetime(intraday["Datetime"]) + timedelta(hours=5, minutes=30) ).dt.strftime("%H:%M:%S") intraday_json = intraday.round(2).to_dict(orient="records") else: intraday_json = [] # --- 4️⃣ Financial Statements --- fin_quarterly = tk.quarterly_financials fin_yearly = tk.financials bal_quarterly = tk.quarterly_balance_sheet bal_yearly = tk.balance_sheet cf_quarterly = tk.quarterly_cashflow cf_yearly = tk.cashflow def df_to_json(df): if df is None or df.empty: return [] df = df.fillna(0) df = df.round(2) df = df.reset_index().rename(columns={"index": "Item"}) return stringify_keys(df.to_dict(orient="records")) # --- Final Combined Response --- result = { "success": True, "ticker": ticker, "timestamp": datetime.utcnow().isoformat(), "info": info, "historical_daily": hist_json, "intraday_5min": intraday_json, "financials": { "quarterly": df_to_json(fin_quarterly), "yearly": df_to_json(fin_yearly) }, "balance_sheet": { "quarterly": df_to_json(bal_quarterly), "yearly": df_to_json(bal_yearly) }, "cashflow": { "quarterly": df_to_json(cf_quarterly), "yearly": df_to_json(cf_yearly) } } result = stringify_keys(result) # final safety layer print(f"[DEBUG] Done. {len(hist_json)} daily, {len(intraday_json)} intraday (5m) records.") return result except Exception as e: print(f"[ERROR] Fetch failed: {e}") return {"error": str(e)} # --- Gradio Interface --- iface = gr.Interface( fn=fetch_data, inputs=[ gr.Textbox(label="Symbol", placeholder="e.g., INFY, AAPL"), gr.Textbox(label="Exchange", placeholder="NSE or NYSE", value="NSE") ], outputs=gr.JSON(label="Result"), title="Stock Data API (Full)", description="Fetch yfinance info, 1-year daily, 1-day 5-min intraday, and financial statements.", api_name="fetch_data" ) if __name__ == "__main__": iface.launch(server_name="0.0.0.0", server_port=7860)