aboutsummaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorOpenclaw <openclaw@mail.tylerhoang.xyz>2026-03-29 01:33:07 -0700
committerOpenclaw <openclaw@mail.tylerhoang.xyz>2026-03-29 01:33:07 -0700
commitb1e129bff08076fcd7dfe3ef9c3a98c8f1712a26 (patch)
treeb669a1fcaa126fc7f8f81bbae698e37d4e4e2c94 /components
parent547997cbd069e9b958b12a8da38b3a4a257e29e5 (diff)
Improve UX and disable DCF for financials
Diffstat (limited to 'components')
-rw-r--r--components/market_bar.py84
-rw-r--r--components/valuation.py33
2 files changed, 107 insertions, 10 deletions
diff --git a/components/market_bar.py b/components/market_bar.py
index cb813e5..411b232 100644
--- a/components/market_bar.py
+++ b/components/market_bar.py
@@ -1,25 +1,89 @@
"""Market bar — displays major indices at the top of the app."""
import streamlit as st
from services.data_service import get_market_indices
-from utils.formatters import fmt_number
+
+
+def _delta_class(change_pct: float | None) -> str:
+ if change_pct is None:
+ return "neutral"
+ return "positive" if change_pct >= 0 else "negative"
+
+
+def _delta_text(change_pct: float | None) -> str:
+ if change_pct is None:
+ return "—"
+ arrow = "▲" if change_pct >= 0 else "▼"
+ return f"{arrow} {change_pct * 100:+.2f}%"
def render_market_bar():
indices = get_market_indices()
+ st.markdown(
+ """
+ <style>
+ .prism-market-card {
+ background: rgba(22, 28, 39, 0.92);
+ border: 1px solid rgba(255,255,255,0.06);
+ border-radius: 14px;
+ padding: 0.8rem 0.95rem;
+ min-height: 96px;
+ }
+ .prism-market-label {
+ color: #9aa0b0;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ margin-bottom: 0.45rem;
+ }
+ .prism-market-value {
+ color: #f3f6fb;
+ font-size: 1.5rem;
+ font-weight: 700;
+ line-height: 1.15;
+ margin-bottom: 0.45rem;
+ }
+ .prism-market-delta {
+ display: inline-block;
+ border-radius: 999px;
+ font-size: 0.78rem;
+ font-weight: 600;
+ padding: 0.18rem 0.5rem;
+ }
+ .prism-market-delta.positive {
+ color: #7ce3a1;
+ background: rgba(28, 131, 72, 0.18);
+ }
+ .prism-market-delta.negative {
+ color: #ff8a8a;
+ background: rgba(180, 47, 47, 0.18);
+ }
+ .prism-market-delta.neutral {
+ color: #c6cfdd;
+ background: rgba(198, 207, 221, 0.12);
+ }
+ </style>
+ """,
+ unsafe_allow_html=True,
+ )
+
cols = st.columns(len(indices))
for col, (name, data) in zip(cols, indices.items()):
price = data.get("price")
change_pct = data.get("change_pct")
- if price is None:
- col.metric(label=name, value="—")
- continue
+ value = f"{price:,.2f}" if price is not None else "—"
+ delta_class = _delta_class(change_pct)
+ delta_text = _delta_text(change_pct)
- price_str = f"{price:,.2f}"
- delta_str = f"{change_pct * 100:+.2f}%" if change_pct is not None else None
- col.metric(
- label=name,
- value=price_str,
- delta=delta_str,
+ col.markdown(
+ f"""
+ <div class="prism-market-card">
+ <div class="prism-market-label">{name}</div>
+ <div class="prism-market-value">{value}</div>
+ <span class="prism-market-delta {delta_class}">{delta_text}</span>
+ </div>
+ """,
+ unsafe_allow_html=True,
)
diff --git a/components/valuation.py b/components/valuation.py
index 82e0f0d..d7a84d4 100644
--- a/components/valuation.py
+++ b/components/valuation.py
@@ -15,6 +15,27 @@ from services.valuation_service import run_dcf, run_ev_ebitda, compute_historica
from utils.formatters import fmt_ratio, fmt_pct, fmt_large, fmt_currency
+FINANCIAL_SECTORS = {"Financial Services"}
+FINANCIAL_INDUSTRY_KEYWORDS = (
+ "bank",
+ "insurance",
+ "asset management",
+ "capital markets",
+ "financial data",
+ "credit services",
+ "mortgage",
+ "reit",
+)
+
+
+def _is_financial_company(info: dict) -> bool:
+ sector = str(info.get("sector") or "").strip()
+ industry = str(info.get("industry") or "").strip().lower()
+ if sector in FINANCIAL_SECTORS:
+ return True
+ return any(keyword in industry for keyword in FINANCIAL_INDUSTRY_KEYWORDS)
+
+
def render_valuation(ticker: str):
tab_ratios, tab_dcf, tab_comps, tab_analyst, tab_earnings = st.tabs([
"Key Ratios", "DCF Model", "Comps", "Analyst Targets", "Earnings History"
@@ -97,6 +118,18 @@ def _render_ratios(ticker: str):
def _render_dcf(ticker: str):
info = get_company_info(ticker)
+
+ if _is_financial_company(info):
+ st.warning(
+ "DCF is disabled for financial companies in Prism. Free-cash-flow and capital-structure "
+ "assumptions are not directly comparable for banks, insurers, and similar businesses."
+ )
+ st.caption(
+ "Use ratios, comps, earnings history, and analyst targets instead. A bank-specific valuation "
+ "framework can be added later."
+ )
+ return
+
shares = info.get("sharesOutstanding") or info.get("floatShares")
current_price = info.get("currentPrice") or info.get("regularMarketPrice")
total_debt = info.get("totalDebt") or 0.0