Space59 / app.py
QuantumLearner's picture
Update app.py
dd8106e verified
import streamlit as st
import pandas as pd
import plotly.express as px
import requests
import numpy as np
import yfinance as yf
import os
# Global API key
API_KEY = os.getenv("FMP_API_KEY")
# ----------------------------
# Page configuration
# ----------------------------
st.set_page_config(page_title="Financial Ratios Dashboard", layout="wide")
# ----------------------------
# Helper function to interpret ratios
# ----------------------------
def interpret_ratios(df, metric_list, section_title):
existing_cols = [m for m in metric_list if m in df.columns]
if not existing_cols or df.empty:
return f"**{section_title}**: Data is not available."
df_valid = df[['date'] + existing_cols].dropna(subset=existing_cols, how='all')
if df_valid.empty:
return f"**{section_title}**: No valid data entries."
df_valid = df_valid.sort_values("date")
latest_row = df_valid.iloc[-1]
latest_date = latest_row['date']
if len(df_valid) > 1:
prior_row = df_valid.iloc[-2]
prior_date = prior_row['date']
else:
prior_row = None
prior_date = None
values_only = df_valid[existing_cols].astype(float)
mean_vals = values_only.mean()
min_vals = values_only.min()
max_vals = values_only.max()
std_vals = values_only.std()
text = f"### {section_title}\n\n"
text += "**Recent Data:**\n"
text += f"- **Latest Record Date:** {latest_date.date()}\n"
for col in existing_cols:
latest_val = latest_row[col]
if pd.isna(latest_val):
text += f"- **{col}:** Data missing.\n"
else:
text += f"- **{col}:** {latest_val:.2f}\n"
if prior_row is not None:
text += f"\n**Comparison with previous record ({prior_date.date()}):**\n"
for col in existing_cols:
latest_val = latest_row[col]
prior_val = prior_row[col]
if pd.isna(latest_val) or pd.isna(prior_val):
text += f"- **{col}:** Comparison not possible.\n"
else:
diff = latest_val - prior_val
if diff > 0:
text += f"- **{col}:** Increased by {diff:.2f}.\n"
elif diff < 0:
text += f"- **{col}:** Decreased by {abs(diff):.2f}.\n"
else:
text += f"- **{col}:** Remained the same.\n"
text += "\n**Historical Summary:**\n"
for col in existing_cols:
text += (f"- **{col}:** Mean = {mean_vals[col]:.2f}, "
f"Min = {min_vals[col]:.2f}, Max = {max_vals[col]:.2f}, "
f"Std Dev = {std_vals[col]:.2f}.\n")
text += "\n**Final Interpretation:**\n"
if section_title == "Liquidity Ratios":
text += (
"- High liquidity ratios (current, quick, cash) suggest the company can meet short-term obligations.\n"
"- Low ratios may raise concerns about paying bills on time.\n"
"- The cash ratio shows how much cash is on hand relative to liabilities.\n"
)
elif section_title == "Efficiency & Turnover Ratios":
text += (
"- Longer days of sales or inventory may signal slower turnover.\n"
"- Faster turnover indicates efficient use of assets.\n"
"- Extreme values call for a closer look at operations.\n"
)
elif section_title == "Profitability Ratios":
text += (
"- Higher margins and returns point to strong profit generation.\n"
"- Lower margins may indicate rising costs or pricing pressure.\n"
"- Tax rates combined with margins offer insight into net profitability.\n"
)
elif section_title == "Debt & Coverage Ratios":
text += (
"- Lower debt ratios and higher interest coverage suggest safer leverage.\n"
"- High debt or low coverage ratios may increase financial risk.\n"
"- These ratios help assess the firm's capacity to cover its debts.\n"
)
elif section_title == "Valuation Ratios":
text += (
"- Lower valuation ratios may hint at an undervalued stock.\n"
"- High ratios could point to overvaluation or high growth expectations.\n"
"- Comparing these with the stock price provides context for market sentiment.\n"
)
elif section_title == "Per Share & Distribution Ratios":
text += (
"- Higher cash flow per share numbers are positive for shareholders.\n"
"- A high payout or dividend payout ratio means more earnings are returned as dividends.\n"
"- These ratios help gauge share performance and distribution policies.\n"
)
else:
text += "- Review these trends to understand their impact.\n"
return text
# ----------------------------
# App header and description
# ----------------------------
st.title("Key Financial Ratios")
st.markdown("""
This dashboard shows key financial ratios for companies.
The ratios are grouped into sections for comparison.
Use the sidebar to set inputs and click **Run Analysis**.
""")
# ----------------------------
# Sidebar inputs
# ----------------------------
st.sidebar.header("Inputs")
with st.sidebar.expander("Settings", expanded=True):
symbol = st.text_input("Company Symbol", value="AAPL", help="Enter the company's stock symbol (e.g., AAPL).")
period = st.selectbox("Period", options=["annual", "quarter"], help="Select annual or quarterly data.")
run_button = st.sidebar.button("Run Analysis")
# ----------------------------
# Main Analysis and Visualization
# ----------------------------
if run_button:
try:
# Fetch ratios data
url = f"https://financialmodelingprep.com/api/v3/ratios/{symbol}?period={period}&apikey={API_KEY}"
response = requests.get(url)
response.raise_for_status()
data = response.json()
if not data:
st.error("No data returned. Check the symbol or period.")
else:
df = pd.DataFrame(data)
if "date" in df.columns:
df['date'] = pd.to_datetime(df['date'], errors='coerce')
df.sort_values("date", inplace=True)
# Fetch historical stock price data
ticker = yf.Ticker(symbol)
price_df = ticker.history(period="max")[["Close"]].reset_index()
price_df.rename(columns={"Date": "date"}, inplace=True)
price_df['date'] = pd.to_datetime(price_df['date'])
price_df['date'] = price_df['date'].dt.tz_localize(None)
price_df.sort_values("date", inplace=True)
# Merge stock price data with ratios data using merge_asof
df = pd.merge_asof(df.sort_values("date"), price_df.sort_values("date"), on="date", direction="backward")
st.success("Data loaded successfully!")
st.write("Each section shows a chart, an interpretation, and the data.")
# Section 1: Liquidity Ratios
st.subheader("1. Liquidity Ratios")
liquidity_vars = ["currentRatio", "quickRatio", "cashRatio"]
try:
fig1 = px.line(df, x="date", y=liquidity_vars, title="Liquidity Ratios")
fig1.update_layout(xaxis_title="Date", yaxis_title="Ratio", legend_title="Metric")
st.plotly_chart(fig1, use_container_width=True)
except Exception:
st.error("Error generating the Liquidity Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, liquidity_vars, "Liquidity Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + liquidity_vars])
# Section 2: Efficiency & Turnover Ratios
st.subheader("2. Efficiency & Turnover Ratios")
efficiency_vars = [
"daysOfSalesOutstanding",
"daysOfInventoryOutstanding",
"operatingCycle",
"daysOfPayablesOutstanding",
"cashConversionCycle",
"receivablesTurnover",
"payablesTurnover",
"inventoryTurnover",
"fixedAssetTurnover",
"assetTurnover"
]
try:
fig2 = px.line(df, x="date", y=efficiency_vars, title="Efficiency & Turnover Ratios")
fig2.update_layout(xaxis_title="Date", yaxis_title="Ratio / Days", legend_title="Metric")
st.plotly_chart(fig2, use_container_width=True)
except Exception:
st.error("Error generating the Efficiency & Turnover Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, efficiency_vars, "Efficiency & Turnover Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + efficiency_vars])
# Section 3: Profitability Ratios
st.subheader("3. Profitability Ratios")
profitability_vars = [
"grossProfitMargin",
"operatingProfitMargin",
"pretaxProfitMargin",
"netProfitMargin",
"effectiveTaxRate",
"returnOnAssets",
"returnOnEquity",
"returnOnCapitalEmployed",
"netIncomePerEBT",
"ebtPerEbit",
"ebitPerRevenue"
]
try:
fig3 = px.line(df, x="date", y=profitability_vars, title="Profitability Ratios")
fig3.update_layout(xaxis_title="Date", yaxis_title="Percentage / Ratio", legend_title="Metric")
st.plotly_chart(fig3, use_container_width=True)
except Exception:
st.error("Error generating the Profitability Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, profitability_vars, "Profitability Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + profitability_vars])
# Section 4: Debt & Coverage Ratios
st.subheader("4. Debt & Coverage Ratios")
debt_vars = [
"debtRatio",
"debtEquityRatio",
"longTermDebtToCapitalization",
"totalDebtToCapitalization",
"interestCoverage",
"cashFlowToDebtRatio",
"companyEquityMultiplier",
"shortTermCoverageRatios",
"cashFlowCoverageRatios",
"capitalExpenditureCoverageRatio",
"dividendPaidAndCapexCoverageRatio"
]
try:
fig4 = px.line(df, x="date", y=debt_vars, title="Debt & Coverage Ratios")
fig4.update_layout(xaxis_title="Date", yaxis_title="Ratio", legend_title="Metric")
st.plotly_chart(fig4, use_container_width=True)
except Exception:
st.error("Error generating the Debt & Coverage Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, debt_vars, "Debt & Coverage Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + debt_vars])
# Section 5: Valuation Ratios (with Stock Price)
st.subheader("5. Valuation Ratios")
valuation_vars = [
"priceBookValueRatio",
"priceToBookRatio",
"priceToSalesRatio",
"priceEarningsRatio",
"priceToFreeCashFlowsRatio",
"priceToOperatingCashFlowsRatio",
"priceCashFlowRatio",
"priceEarningsToGrowthRatio",
"priceSalesRatio",
"dividendYield",
"enterpriseValueMultiple",
"priceFairValue"
]
try:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig5 = make_subplots(specs=[[{"secondary_y": True}]])
for col in valuation_vars:
fig5.add_trace(
go.Scatter(x=df['date'], y=df[col], mode='lines', name=col),
secondary_y=False
)
# Add stock price on secondary y-axis
fig5.add_trace(
go.Scatter(x=df['date'], y=df['Close'], mode='lines', name="Stock Price"),
secondary_y=True
)
fig5.update_layout(title_text="Valuation Ratios & Stock Price")
fig5.update_xaxes(title_text="Date")
fig5.update_yaxes(title_text="Valuation Ratios", secondary_y=False)
fig5.update_yaxes(title_text="Stock Price", secondary_y=True)
st.plotly_chart(fig5, use_container_width=True)
except Exception:
st.error("Error generating the Valuation Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, valuation_vars, "Valuation Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + valuation_vars + ["Close"]])
# Section 6: Per Share & Distribution Ratios
st.subheader("6. Per Share & Distribution Ratios")
share_vars = [
"operatingCashFlowPerShare",
"freeCashFlowPerShare",
"cashPerShare",
"payoutRatio",
"operatingCashFlowSalesRatio",
"freeCashFlowOperatingCashFlowRatio",
"dividendPayoutRatio"
]
try:
fig6 = px.line(df, x="date", y=share_vars, title="Per Share & Distribution Ratios")
fig6.update_layout(xaxis_title="Date", yaxis_title="Ratio", legend_title="Metric")
st.plotly_chart(fig6, use_container_width=True)
except Exception:
st.error("Error generating the Per Share & Distribution Ratios chart.")
with st.expander("Interpretation"):
interp_text = interpret_ratios(df, share_vars, "Per Share & Distribution Ratios")
st.markdown(interp_text)
with st.expander("DataFrame"):
st.dataframe(df[["date"] + share_vars])
except Exception:
st.error("Error fetching data. Check your connection and inputs.")
# Hide Streamlit default style
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)