import gradio as gr import yfinance as yf import plotly.graph_objs as go import pandas as pd import datetime import pandas.api.types as ptypes STYLE_BLOCK = """ """ def format_large_number(num): if not isinstance(num, (int, float)): return num # Return as is if not a number sign = '-' if num < 0 else '' num = abs(float(num)) if num >= 1_000_000_000_000: # Lakh Crore (10^12) return f"{sign}{num / 1_000_000_000_000:.2f} LCr" elif num >= 10_000_000: # Crore (10^7) return f"{sign}{num / 10_000_000:.2f} Cr" elif num >= 100_000: # Lakh (10^5) return f"{sign}{num / 100_000:.2f} Lac" else: return f"{sign}{num:,.0f}" def format_timestamp_to_date(timestamp): if not isinstance(timestamp, (int, float)) or timestamp <= 0: return "N/A" try: return datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d') except ValueError: return "Invalid Date" def fetch_data(symbol, req_type): yfsymbol=symbol+".NS" try: ticker = yf.Ticker(yfsymbol) content_html = "" # Info block as cards + big boxes if req_type.lower() == "info": info = ticker.info if not info: content_html = "

No info available

" else: info_categories = { "Company Overview": [ "longName", "symbol", "exchange", "quoteType", "sector", "industry", "fullTimeEmployees", "website", "address1", "city", "state", "zip", "country", "phone" ], "Valuation Metrics": [ "marketCap", "enterpriseValue", "trailingPE", "forwardPE", "pegRatio", "priceToSalesTrailing12Months", "enterpriseToRevenue", "enterpriseToEbitda" ], "Key Financials": [ "fiftyTwoWeekHigh", "fiftyTwoWeekLow", "fiftyDayAverage", "twoHundredDayAverage", "trailingAnnualDividendRate", "trailingAnnualDividendYield", "dividendRate", "dividendYield", "exDividendDate", "lastSplitFactor", "lastSplitDate", "lastDividendValue", "payoutRatio", "beta", "sharesOutstanding", "impliedSharesOutstanding" ], "Operational Details": [ "auditRisk", "boardRisk", "compensationRisk", "shareHolderRightsRisk", "overallRisk", "governanceEpochDate", "compensationAsOfEpochDate" ], "Trading Information": [ "open", "previousClose", "dayLow", "dayHigh", "volume", "averageVolume", "averageVolume10days", "fiftyTwoWeekChange", "SandP52WeekChange", "currency", "regularMarketDayLow", "regularMarketDayHigh", "regularMarketOpen", "regularMarketPreviousClose", "regularMarketPrice", "regularMarketVolume", "regularMarketChange", "regularMarketChangePercent" ], "Analyst & Target": [ "targetMeanPrice", "numberOfAnalystOpinions", "recommendationKey", "recommendationMean" ] } long_summary = info.pop("longBusinessSummary", None) officers = info.pop("companyOfficers", None) categorized_html = "" for category_name, keys in info_categories.items(): category_key_value_html = "" # Collect key-value pairs for this category for key in keys: if key in info and info[key] is not None and info[key] != []: value = info[key] # Apply formatting based on key if key in ["exDividendDate", "lastSplitDate", "governanceEpochDate", "compensationAsOfEpochDate"]: value = format_timestamp_to_date(value) elif key in ["marketCap", "enterpriseValue", "fullTimeEmployees", "volume", "averageVolume", "averageVolume10days", "sharesOutstanding", "impliedSharesOutstanding", "regularMarketVolume"]: value = format_large_number(value) elif isinstance(value, (int, float)): if 'percent' in key.lower() or 'ratio' in key.lower() or 'yield' in key.lower() or 'beta' in key.lower() or 'payoutRatio' in key.lower(): value = f"{value:.2%}" # Format percentages elif 'price' in key.lower() or 'dividend' in key.lower() or 'average' in key.lower(): value = f"{value:.2f}" # Format currency/prices else: value = f"{value:,.0f}" category_key_value_html += f"

{key.replace('_', ' ').title()}

{value}

" if category_key_value_html: # Only add category header and card if there is content in it categorized_html += f"

{category_name}

{category_key_value_html}
" extra_sections = "" if long_summary: extra_sections += f"

Business Summary

{long_summary}

" if officers: officer_rows = "".join( f"{o.get('name','')}"f"{o.get('title','')}"f"{o.get('age','')}"f"" for o in officers ) officer_table = f"{officer_rows}
NameTitleAge
" extra_sections += f"

Company Officers

{officer_table}
" content_html = f"{categorized_html}{extra_sections}" # Daily chart elif req_type.lower() == "daily": df = yf.download(yfsymbol, period="1y", interval="1d").round(2) if df.empty: content_html = f"

No daily data for {symbol}

" else: if isinstance(df.columns, pd.MultiIndex): df.columns = df.columns.get_level_values(0) low_price = df["Low"].min() high_price = df["High"].max() price_range = high_price - low_price vol_band_min = low_price - (price_range / 5) vol_band_max = low_price vol_max = df["Volume"].max() vol_scale = (vol_band_max - vol_band_min) / vol_max if vol_max > 0 else 1 fig = go.Figure() fig.add_trace(go.Candlestick( x=df.index, open=df["Open"], high=df["High"], low=df["Low"], close=df["Close"], name="Price" )) fig.add_trace(go.Bar( x=df.index, y=df["Volume"] * vol_scale + vol_band_min, name="Volume", marker_color="lightblue", customdata=df["Volume"], hovertemplate="Volume: %{customdata}" )) fig.update_layout( xaxis_title="Date", yaxis_title="Price", yaxis=dict(range=[vol_band_min, high_price]), xaxis_rangeslider_visible=False, height=600 ) chart_html = fig.to_html(full_html=False) table_html = df.tail(30).to_html(classes="styled-table", border=0) content_html = f"{chart_html}

Recent Daily Data (last 30 rows)

{table_html}" # Intraday chart elif req_type.lower() == "intraday": df = yf.download(yfsymbol, period="1d", interval="5m").round(2) if df.empty: content_html = f"

No intraday data for {symbol}

" else: if isinstance(df.columns, pd.MultiIndex): df.columns = df.columns.get_level_values(0) low_price = df["Low"].min() high_price = df["High"].max() price_range = high_price - low_price vol_band_min = low_price - (price_range / 5) vol_band_max = low_price vol_max = df["Volume"].max() vol_scale = (vol_band_max - vol_band_min) / vol_max if vol_max > 0 else 1 fig = go.Figure() fig.add_trace(go.Candlestick( x=df.index, open=df["Open"], high=df["High"], low=df["Low"], close=df["Close"], name="Price" )) fig.add_trace(go.Bar( x=df.index, y=df["Volume"] * vol_scale + vol_band_min, name="Volume", marker_color="orange", customdata=df["Volume"], hovertemplate="Volume: %{customdata}" )) fig.update_layout( xaxis_title="Time", yaxis_title="Price", yaxis=dict(range=[vol_band_min, high_price]), xaxis_rangeslider_visible=False, height=600 ) chart_html = fig.to_html(full_html=False) table_html = df.tail(50).to_html(classes="styled-table", border=0) content_html = f"{chart_html}

Recent Intraday Data (last 50 rows)

{table_html}" # Financial sections elif req_type.lower() == "qresult": df = ticker.quarterly_financials if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Quarterly Results

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No quarterly results available

" elif req_type.lower() == "result": df = ticker.financials if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Annual Results

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No annual results available

" elif req_type.lower() == "balance": df = ticker.balance_sheet if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Balance Sheet

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No balance sheet available

" elif req_type.lower() == "cashflow": df = ticker.cashflow if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Cash Flow

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No cashflow available

" elif req_type.lower() == "dividend": df = ticker.dividends.to_frame('Dividend') if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Dividend History

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No dividend history available

" elif req_type.lower() == "split": df = ticker.splits.to_frame('Split') if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Split History

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No split history available

" elif req_type.lower() == "other": # This typically handles earnings df = ticker.earnings if not df.empty: for col in df.columns: if ptypes.is_numeric_dtype(df[col]): df[col] = df[col].apply(format_large_number) content_html = f"

Earnings

{df.to_html(classes='styled-table', border=0)}" if not df.empty else "

No earnings data available

" else: content_html = f"

No handler for {req_type}

" except Exception as e: content_html = f"

Error

{str(e)}

" # Wrap the content_html in a complete HTML document structure full_html_output = f""" Stock Data for {symbol} {STYLE_BLOCK} {content_html} """ return full_html_output iface = gr.Interface( fn=fetch_data, inputs=[ gr.Textbox(label="Stock Symbol", value="PNB"), gr.Dropdown( label="Request Type", choices=[ "info", "intraday", "daily", "qresult", "result", "balance", "cashflow", "dividend", "split", "other" ], value="info" ) ], outputs=gr.HTML(label="Full HTML Output"), title="Stock Data API (Full)", description="Fetch data from NSE and yfinance", api_name="fetch_data" ) if __name__ == "__main__": iface.launch(server_name="0.0.0.0", server_port=7860)