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 = """ """ st.markdown(hide_streamlit_style, unsafe_allow_html=True)