""" Tab 10 — Company Valuation PER, PBR, PSR, P/OCF cards with 5Y history, industry comparison, historical chart. """ import streamlit as st from config.constants import MARKET_OPTIONS from utils.ticker import get_global_ticker from data.valuation_metrics import ( get_valuation_multiples, get_historical_multiples, get_industry_avg_multiples, get_pe_history_chart_data, ) from data.fundamentals import get_sector_industry try: import plotly.graph_objects as go except ImportError: go = None def _metric_card(label, value, sub_items: dict, col): """Render a single valuation metric card.""" with col: val_str = f"{value:.2f}" if isinstance(value, (int, float)) and value == value else "N/A" st.markdown( f'
' f'
{label}
' f'
{val_str}
', unsafe_allow_html=True, ) for k, v in sub_items.items(): v_str = f"{v:.2f}" if isinstance(v, (int, float)) and v == v else "N/A" color = "#9CA3AF" st.markdown( f'
' f'{k}: {v_str}
', unsafe_allow_html=True, ) st.markdown('
', unsafe_allow_html=True) def _render_pe_chart(ticker: str, quant_ticker: str): """Render 5Y price chart with PE overlay.""" if not go: return df = get_pe_history_chart_data(quant_ticker) if df.empty: return st.markdown("#### Historical Price (5Y)") fig = go.Figure() fig.add_trace(go.Scatter( x=df["Date"], y=df["Close"], mode="lines", name="Price", line=dict(color="#34D399", width=2), )) fig.update_layout( paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font_color="#E5E7EB", height=300, margin=dict(t=20, b=30, l=50, r=20), xaxis=dict(gridcolor="rgba(255,255,255,0.05)"), yaxis=dict(gridcolor="rgba(255,255,255,0.05)", title="Price ($)"), showlegend=False, ) st.plotly_chart(fig, use_container_width=True) def _render_comparison_bar(label, current, avg_5y, industry_avg): """Render a horizontal comparison bar for a metric.""" vals = {"Current": current, "5Y Avg": avg_5y, "Industry": industry_avg} items = [] for k, v in vals.items(): if v and isinstance(v, (int, float)) and v == v: items.append(f'{k}: ' f'{v:.2f}') if items: st.markdown( f'
' f'{label}' f'{" · ".join(items)}
', unsafe_allow_html=True, ) def render_tab10(ticker): """Render the Company Valuation tab.""" market = st.session_state.get("market") or MARKET_OPTIONS[0] quant_ticker = get_global_ticker(ticker, market) if ticker else "" st.subheader("Company Valuation") if not ticker: st.info("Select a company from the sidebar to view valuation metrics.") return si = get_sector_industry(quant_ticker) sector = si.get("sector", "N/A") industry = si.get("industry", "N/A") st.caption(f"**{ticker}** · {sector} · {industry}") with st.spinner("Fetching valuation data..."): multiples = get_valuation_multiples(quant_ticker) hist = get_historical_multiples(quant_ticker) ind_avg = get_industry_avg_multiples(quant_ticker) if not multiples: st.warning("Could not fetch valuation data for this ticker.") return # ── Metric Cards: PER, PBR, PSR, P/OCF ── c1, c2, c3, c4 = st.columns(4) _metric_card("PER (Trailing)", multiples.get("PER"), { "Forward": multiples.get("Forward PER"), "5Y Avg": hist.get("5Y Avg PER"), "Industry": ind_avg.get("Industry Avg PER"), }, c1) _metric_card("PBR", multiples.get("PBR"), { "5Y Avg": hist.get("5Y Avg PBR"), "Industry": ind_avg.get("Industry Avg PBR"), }, c2) _metric_card("PSR", multiples.get("PSR"), { "Industry": ind_avg.get("Industry Avg PSR"), "EV/Revenue": multiples.get("EV/Revenue"), }, c3) _metric_card("P/OCF", multiples.get("P/OCF"), { "EV/EBITDA": multiples.get("EV/EBITDA"), "PEG Ratio": multiples.get("PEG"), }, c4) st.markdown("---") # ── Comparison Table ── left, right = st.columns([3, 2]) with left: st.markdown("#### Valuation Comparison") _render_comparison_bar("PER", multiples.get("PER"), hist.get("5Y Avg PER"), ind_avg.get("Industry Avg PER")) _render_comparison_bar("PBR", multiples.get("PBR"), hist.get("5Y Avg PBR"), ind_avg.get("Industry Avg PBR")) _render_comparison_bar("PSR", multiples.get("PSR"), None, ind_avg.get("Industry Avg PSR")) _render_comparison_bar("EV/EBITDA", multiples.get("EV/EBITDA"), None, None) # Premium/Discount indicator pe_cur = multiples.get("PER") pe_5y = hist.get("5Y Avg PER") if pe_cur and pe_5y and pe_5y > 0: prem = (pe_cur - pe_5y) / pe_5y * 100 color = "#F87171" if prem > 0 else "#34D399" word = "premium" if prem > 0 else "discount" st.markdown( f'
' f'{abs(prem):.1f}% {word}' f' vs 5Y average PER
', unsafe_allow_html=True, ) with right: _render_pe_chart(ticker, quant_ticker) # ── Additional Metrics Table ── st.markdown("---") st.markdown("#### Additional Metrics") m1, m2, m3, m4 = st.columns(4) with m1: beta = multiples.get("Beta") st.metric("Beta", f"{beta:.2f}" if beta else "N/A") with m2: dy = multiples.get("Dividend Yield") st.metric("Div Yield", f"{dy*100:.2f}%" if dy else "N/A") with m3: h52 = multiples.get("52W High") st.metric("52W High", f"${h52:,.2f}" if h52 else "N/A") with m4: l52 = multiples.get("52W Low") st.metric("52W Low", f"${l52:,.2f}" if l52 else "N/A") # ── KPI Analysis ── from views.tab10_kpi import render_kpi_section render_kpi_section(ticker, sector, industry)