"""Prism — Financial Analysis Dashboard"""
import streamlit as st
from dotenv import load_dotenv
load_dotenv()
st.set_page_config(
page_title="Prism",
page_icon="assets/logo.png",
layout="wide",
initial_sidebar_state="expanded",
)
# ── Global CSS ────────────────────────────────────────────────────────────────
st.markdown("""
""", unsafe_allow_html=True)
from components.market_bar import render_market_bar
from components.overview import render_overview
from components.financials import render_financials
from components.valuation import render_valuation
from components.insiders import render_insiders
from components.filings import render_filings
from components.news import render_news
from components.options import render_options
from services.data_service import get_company_info, search_tickers, get_latest_price
if "ticker" not in st.session_state:
st.session_state["ticker"] = None
# ── Sidebar ──────────────────────────────────────────────────────────────────
with st.sidebar:
col_logo, col_title = st.columns([1, 2])
with col_logo:
st.image("assets/logo.png", width=60)
with col_title:
st.markdown("### Prism")
st.caption("Financial Analysis Dashboard")
st.divider()
with st.form("ticker_search_form", clear_on_submit=False):
query = st.text_input(
"Search company or ticker",
placeholder="e.g. Apple, AAPL, MSFT…",
key="search_query",
).strip()
results = search_tickers(query) if query else []
selected_symbol = None
if results:
options = {f"{r['symbol']} — {r['name']} ({r['exchange']})": r["symbol"] for r in results}
choice = st.selectbox(
"Matches",
options=list(options.keys()),
label_visibility="collapsed",
)
selected_symbol = options[choice]
elif query:
selected_symbol = query.upper()
submitted = st.form_submit_button("Open", use_container_width=True, type="primary")
if submitted and selected_symbol:
st.session_state["ticker"] = selected_symbol
if st.session_state["ticker"]:
st.caption(f"Currently viewing: **{st.session_state['ticker']}**")
ticker = st.session_state["ticker"]
# Quick company info in sidebar
st.divider()
if ticker:
info = get_company_info(ticker)
if ticker and info:
st.caption(info.get("longName", ticker))
price = get_latest_price(ticker)
prev_close = info.get("previousClose") or info.get("regularMarketPreviousClose")
if price is not None:
if prev_close and prev_close > 0:
chg = price - prev_close
chg_pct = chg / prev_close * 100
sign = "+" if chg >= 0 else ""
color = "#2ecc71" if chg >= 0 else "#e74c3c"
st.markdown(
f"${price:,.2f}"
f" {sign}{chg:+.2f} ({sign}{chg_pct:.2f}%)",
unsafe_allow_html=True,
)
else:
st.markdown(
f"${price:,.2f}",
unsafe_allow_html=True,
)
st.caption(f"Exchange: {info.get('exchange', '—')}")
st.caption(f"Currency: {info.get('currency', 'USD')}")
st.caption(f"Sector: {info.get('sector', '—')}")
employees = info.get("fullTimeEmployees")
st.caption(f"Employees: {employees:,}" if isinstance(employees, int) else "Employees: —")
website = info.get("website")
if website:
st.markdown(f"[🌐 Website]({website})")
# ── Market Bar ────────────────────────────────────────────────────────────────
with st.container():
render_market_bar()
st.divider()
# ── Main Content ──────────────────────────────────────────────────────────────
if not ticker:
st.info("Search for a company or ticker in the sidebar to get started.")
st.stop()
tab_overview, tab_financials, tab_valuation, tab_options, tab_insiders, tab_filings, tab_news = st.tabs([
"📈 Overview",
"📊 Financials",
"💰 Valuation",
"🎯 Options",
"👤 Insiders",
"📁 Filings",
"📰 News",
])
with tab_overview:
try:
render_overview(ticker)
except Exception as e:
st.error(f"Overview failed to load: {e}")
with tab_financials:
try:
render_financials(ticker)
except Exception as e:
st.error(f"Financials failed to load: {e}")
with tab_valuation:
try:
render_valuation(ticker)
except Exception as e:
st.error(f"Valuation failed to load: {e}")
with tab_options:
try:
render_options(ticker)
except Exception as e:
st.error(f"Options data failed to load: {e}")
with tab_insiders:
try:
render_insiders(ticker)
except Exception as e:
st.error(f"Insider data failed to load: {e}")
with tab_filings:
try:
render_filings(ticker)
except Exception as e:
st.error(f"Filings failed to load: {e}")
with tab_news:
try:
render_news(ticker)
except Exception as e:
st.error(f"News failed to load: {e}")