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"| Name | Title | Age |
{officer_rows}
"
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)