import streamlit as st import pandas as pd import plotly.express as px import requests import os # Optional environment key API_KEY = os.getenv("FMP_API_KEY") # Page config st.set_page_config(page_title="Financial Growth Metrics", layout="wide") # ---------------------------- # Caching function with session_state # ---------------------------- def fetch_data(url, cache_key): """Load data from session_state if cached, else fetch from URL.""" if cache_key in st.session_state: return st.session_state[cache_key] try: resp = requests.get(url) resp.raise_for_status() data = resp.json() st.session_state[cache_key] = data return data except requests.exceptions.RequestException: st.session_state[cache_key] = [] return [] # ---------------------------- # Helper for interpretations # ---------------------------- def interpret_growth_metrics(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 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 # Basic stats 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}:** Missing in latest record.\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}:** No comparison (missing data).\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}:** No change.\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"StdDev={std_vals[col]:.2f}\n") text += "\n**Final Interpretation:**\n" text += "- Track these changes over time.\n" return text # ---------------------------- # Page Functions # ---------------------------- def show_financial_growth(symbol, period): url = f"https://financialmodelingprep.com/api/v3/financial-growth/{symbol}?period={period}&apikey={API_KEY}" data = fetch_data(url, f"financial_growth_{symbol}_{period}") if not data: st.error("No data returned. Check symbol or period.") return df = pd.DataFrame(data) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], errors="coerce") df.sort_values("date", inplace=True) st.success("Data loaded successfully.") st.write("Below are key growth metrics. Expand each section for interpretation.") # 1. Profitability st.subheader("1. Profitability") profitability_vars = ["revenueGrowth", "grossProfitGrowth", "ebitgrowth", "operatingIncomeGrowth", "netIncomeGrowth"] try: fig1 = px.line(df, x="date", y=profitability_vars, title="Profitability Metrics") fig1.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig1, use_container_width=True) except Exception: st.error("Could not plot Profitability Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, profitability_vars, "Profitability Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in profitability_vars if c in df.columns] st.dataframe(df[cols]) # 2. EPS st.subheader("2. Earnings Per Share") eps_vars = ["epsgrowth", "epsdilutedGrowth"] try: fig2 = px.line(df, x="date", y=eps_vars, title="EPS Metrics") fig2.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig2, use_container_width=True) except Exception: st.error("Could not plot EPS Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, eps_vars, "EPS Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in eps_vars if c in df.columns] st.dataframe(df[cols]) # 3. Share Count st.subheader("3. Share Count") share_vars = ["weightedAverageSharesGrowth", "weightedAverageSharesDilutedGrowth"] try: fig3 = px.line(df, x="date", y=share_vars, title="Share Count Adjustments") fig3.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig3, use_container_width=True) except Exception: st.error("Could not plot Share Count.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, share_vars, "Share Count Adjustments")) with st.expander("DataFrame"): cols = ["date"] + [c for c in share_vars if c in df.columns] st.dataframe(df[cols]) # 4. Dividend st.subheader("4. Dividend per Share") div_vars = ["dividendsperShareGrowth"] try: fig4 = px.line(df, x="date", y=div_vars, title="Dividend Growth") fig4.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig4, use_container_width=True) except Exception: st.error("Could not plot Dividend Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, div_vars, "Dividend Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in div_vars if c in df.columns] st.dataframe(df[cols]) # 5. Cash Flow st.subheader("5. Cash Flow") cashflow_vars = ["operatingCashFlowGrowth", "freeCashFlowGrowth"] try: fig5 = px.line(df, x="date", y=cashflow_vars, title="Cash Flow Metrics") fig5.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig5, use_container_width=True) except Exception: st.error("Could not plot Cash Flow Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, cashflow_vars, "Cash Flow Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in cashflow_vars if c in df.columns] st.dataframe(df[cols]) # 6. Multi-Year Growth (Per Share) st.subheader("6. Multi-Year Growth (Per Share)") multi_year_groups = { "Revenue Growth": [ "tenYRevenueGrowthPerShare", "fiveYRevenueGrowthPerShare", "threeYRevenueGrowthPerShare" ], "Operating Cash Flow Growth": [ "tenYOperatingCFGrowthPerShare", "fiveYOperatingCFGrowthPerShare", "threeYOperatingCFGrowthPerShare" ], "Net Income Growth": [ "tenYNetIncomeGrowthPerShare", "fiveYNetIncomeGrowthPerShare", "threeYNetIncomeGrowthPerShare" ], "Shareholders’ Equity Growth": [ "tenYShareholdersEquityGrowthPerShare", "fiveYShareholdersEquityGrowthPerShare", "threeYShareholdersEquityGrowthPerShare" ], "Dividend per Share Growth": [ "tenYDividendperShareGrowthPerShare", "fiveYDividendperShareGrowthPerShare", "threeYDividendperShareGrowthPerShare" ] } for subgroup, vars_list in multi_year_groups.items(): st.markdown(f"**{subgroup}**") try: fig = px.line(df, x="date", y=vars_list, title=subgroup) fig.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig, use_container_width=True) except Exception: st.error(f"Could not plot chart for {subgroup}.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, vars_list, subgroup)) with st.expander("DataFrame"): cols = ["date"] + [c for c in vars_list if c in df.columns] st.dataframe(df[cols]) # 7. Balance Sheet st.subheader("7. Balance Sheet") balance_vars = ["receivablesGrowth", "inventoryGrowth", "assetGrowth", "bookValueperShareGrowth", "debtGrowth"] try: fig7 = px.line(df, x="date", y=balance_vars, title="Balance Sheet Metrics") fig7.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig7, use_container_width=True) except Exception: st.error("Could not plot Balance Sheet Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, balance_vars, "Balance Sheet Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in balance_vars if c in df.columns] st.dataframe(df[cols]) # 8. Expense st.subheader("8. Expense") expense_vars = ["rdexpenseGrowth", "sgaexpensesGrowth"] try: fig8 = px.line(df, x="date", y=expense_vars, title="Expense Metrics") fig8.update_layout(xaxis_title="Date", yaxis_title="Growth Rate", legend_title="Metric") st.plotly_chart(fig8, use_container_width=True) except Exception: st.error("Could not plot Expense Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, expense_vars, "Expense Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in expense_vars if c in df.columns] st.dataframe(df[cols]) def show_balance_sheet_growth(symbol, period): url = f"https://financialmodelingprep.com/api/v3/balance-sheet-statement-growth/{symbol}?period={period}&apikey={API_KEY}" data = fetch_data(url, f"balance_sheet_growth_{symbol}_{period}") if not data: st.error("No data returned. Check symbol or period.") return df = pd.DataFrame(data) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], errors="coerce") df.sort_values("date", inplace=True) st.success("Data loaded successfully.") st.write("These sections show balance sheet growth metrics.") # 1. Asset Growth st.subheader("1. Asset Growth") asset_vars = [ "growthCashAndCashEquivalents", "growthShortTermInvestments", "growthCashAndShortTermInvestments", "growthNetReceivables", "growthInventory", "growthTotalCurrentAssets", "growthPropertyPlantEquipmentNet", "growthTotalNonCurrentAssets", "growthTotalAssets" ] try: fig_a = px.line(df, x="date", y=asset_vars, title="Asset Growth") fig_a.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_a, use_container_width=True) except Exception: st.error("Could not plot Asset Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, asset_vars, "Asset Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in asset_vars if c in df.columns] st.dataframe(df[cols]) # 2. Liability Growth st.subheader("2. Liability Growth") liability_vars = [ "growthAccountPayables", "growthShortTermDebt", "growthTaxPayables", "growthTotalCurrentLiabilities", "growthLongTermDebt", "growthTotalNonCurrentLiabilities", "growthTotalLiabilities" ] try: fig_l = px.line(df, x="date", y=liability_vars, title="Liability Growth") fig_l.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_l, use_container_width=True) except Exception: st.error("Could not plot Liability Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, liability_vars, "Liability Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in liability_vars if c in df.columns] st.dataframe(df[cols]) # 3. Equity Growth st.subheader("3. Equity Growth") equity_vars = [ "growthCommonStock", "growthRetainedEarnings", "growthAccumulatedOtherComprehensiveIncomeLoss", "growthTotalStockholdersEquity" ] try: fig_e = px.line(df, x="date", y=equity_vars, title="Equity Growth") fig_e.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_e, use_container_width=True) except Exception: st.error("Could not plot Equity Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, equity_vars, "Equity Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in equity_vars if c in df.columns] st.dataframe(df[cols]) # 4. Debt Metrics st.subheader("4. Debt Metrics") debt_vars = ["growthTotalDebt", "growthNetDebt"] try: fig_d = px.line(df, x="date", y=debt_vars, title="Debt Metrics") fig_d.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_d, use_container_width=True) except Exception: st.error("Could not plot Debt Metrics.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, debt_vars, "Debt Metrics")) with st.expander("DataFrame"): cols = ["date"] + [c for c in debt_vars if c in df.columns] st.dataframe(df[cols]) def show_income_growth(symbol, period): url = f"https://financialmodelingprep.com/api/v3/income-statement-growth/{symbol}?period={period}&apikey={API_KEY}" data = fetch_data(url, f"income_growth_{symbol}_{period}") if not data: st.error("No data returned. Check symbol or period.") return df = pd.DataFrame(data) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], errors="coerce") df.sort_values("date", inplace=True) st.success("Data loaded successfully.") st.write("These sections show income statement growth metrics.") # 1. Profitability st.subheader("1. Profitability") profitability_vars = [ "growthRevenue", "growthGrossProfit", "growthOperatingIncome", "growthNetIncome", "growthEBITDA" ] try: fig_p = px.line(df, x="date", y=profitability_vars, title="Profitability Growth") fig_p.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_p, use_container_width=True) except Exception: st.error("Could not plot Profitability Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, profitability_vars, "Profitability Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in profitability_vars if c in df.columns] st.dataframe(df[cols]) # 2. Expense st.subheader("2. Expense") expense_vars = [ "growthCostOfRevenue", "growthOperatingExpenses", "growthResearchAndDevelopmentExpenses", "growthSellingAndMarketingExpenses" ] try: fig_e = px.line(df, x="date", y=expense_vars, title="Expense Growth") fig_e.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_e, use_container_width=True) except Exception: st.error("Could not plot Expense Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, expense_vars, "Expense Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in expense_vars if c in df.columns] st.dataframe(df[cols]) # 3. Net and Tax Metrics st.subheader("3. Net and Tax Metrics") net_tax_vars = ["growthIncomeBeforeTax", "growthIncomeTaxExpense", "growthNetIncome", "growthNetIncomeRatio"] try: fig_n = px.line(df, x="date", y=net_tax_vars, title="Net Income and Tax Growth") fig_n.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_n, use_container_width=True) except Exception: st.error("Could not plot Net Income/Tax Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, net_tax_vars, "Net Income/Tax Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in net_tax_vars if c in df.columns] st.dataframe(df[cols]) # 4. EPS Growth st.subheader("4. EPS Growth") eps_vars = ["growthEPS", "growthEPSDiluted"] try: fig_eps = px.line(df, x="date", y=eps_vars, title="EPS Growth") fig_eps.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_eps, use_container_width=True) except Exception: st.error("Could not plot EPS Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, eps_vars, "EPS Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in eps_vars if c in df.columns] st.dataframe(df[cols]) def show_cashflow_growth(symbol, period): url = f"https://financialmodelingprep.com/api/v3/cash-flow-statement-growth/{symbol}?period={period}&apikey={API_KEY}" data = fetch_data(url, f"cashflow_growth_{symbol}_{period}") if not data: st.error("No data returned. Check symbol or period.") return df = pd.DataFrame(data) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], errors="coerce") df.sort_values("date", inplace=True) st.success("Data loaded successfully.") st.write("These sections show cash flow statement growth metrics.") # 1. Operating Activities st.subheader("1. Operating Activities") operating_vars = [ "growthNetIncome", "growthDepreciationAndAmortization", "growthChangeInWorkingCapital", "growthNetCashProvidedByOperatingActivites", "growthOperatingCashFlow" ] try: fig_op = px.line(df, x="date", y=operating_vars, title="Operating Cash Flow Growth") fig_op.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_op, use_container_width=True) except Exception: st.error("Could not plot Operating CF Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, operating_vars, "Operating CF Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in operating_vars if c in df.columns] st.dataframe(df[cols]) # 2. Investing Activities st.subheader("2. Investing Activities") investing_vars = [ "growthInvestmentsInPropertyPlantAndEquipment", "growthAcquisitionsNet", "growthPurchasesOfInvestments", "growthSalesMaturitiesOfInvestments", "growthNetCashUsedForInvestingActivites" ] try: fig_inv = px.line(df, x="date", y=investing_vars, title="Investing Cash Flow Growth") fig_inv.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_inv, use_container_width=True) except Exception: st.error("Could not plot Investing CF Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, investing_vars, "Investing CF Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in investing_vars if c in df.columns] st.dataframe(df[cols]) # 3. Financing Activities st.subheader("3. Financing Activities") financing_vars = [ "growthDebtRepayment", "growthCommonStockRepurchased", "growthDividendsPaid", "growthNetCashUsedProvidedByFinancingActivities" ] try: fig_fin = px.line(df, x="date", y=financing_vars, title="Financing Cash Flow Growth") fig_fin.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_fin, use_container_width=True) except Exception: st.error("Could not plot Financing CF Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, financing_vars, "Financing CF Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in financing_vars if c in df.columns] st.dataframe(df[cols]) # 4. Free Cash Flow st.subheader("4. Free Cash Flow") fcf_vars = ["growthFreeCashFlow"] try: fig_fcf = px.line(df, x="date", y=fcf_vars, title="Free Cash Flow Growth") fig_fcf.update_layout(xaxis_title="Date", yaxis_title="Growth", legend_title="Metric") st.plotly_chart(fig_fcf, use_container_width=True) except Exception: st.error("Could not plot Free Cash Flow Growth.") with st.expander("Interpretation"): st.markdown(interpret_growth_metrics(df, fcf_vars, "Free Cash Flow Growth")) with st.expander("DataFrame"): cols = ["date"] + [c for c in fcf_vars if c in df.columns] st.dataframe(df[cols]) # ---------------------------- # Main # ---------------------------- st.title("Financial Growth Metrics") st.markdown(""" This dashboard shows key financial growth metrics across balance sheet, income statement, and cash flow statement. Use the sidebar to set inputs, then press **Run Analysis**. After that, you can switch pages without re-running. """) # Sidebar Navigation st.sidebar.header("Navigation") page = st.sidebar.radio( "Select Page", ["Financial Growth", "Balance Sheet Growth", "Income Growth", "Cash Flow Growth"], index=0 ) st.sidebar.header("Inputs") symbol = st.sidebar.text_input("Company Symbol", value="AAPL") period = st.sidebar.selectbox("Period", options=["annual", "quarter"]) # Here is the run button run_button = st.sidebar.button("Run Analysis") # If user hasn't clicked "Run Analysis," do not load data if not run_button and f"{page.lower().replace(' ', '_')}_{symbol}_{period}" not in st.session_state: st.warning("Press 'Run Analysis' to fetch data and see the results.") else: # If user clicked "Run", or data is cached from a previous run, show the selected page if page == "Financial Growth": show_financial_growth(symbol, period) elif page == "Balance Sheet Growth": show_balance_sheet_growth(symbol, period) elif page == "Income Growth": show_income_growth(symbol, period) elif page == "Cash Flow Growth": show_cashflow_growth(symbol, period) # Hide Streamlit default style hide_streamlit_style = """ """ st.markdown(hide_streamlit_style, unsafe_allow_html=True)