aboutsummaryrefslogtreecommitdiff
path: root/components/top_movers.py
diff options
context:
space:
mode:
Diffstat (limited to 'components/top_movers.py')
-rw-r--r--components/top_movers.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/components/top_movers.py b/components/top_movers.py
new file mode 100644
index 0000000..ac76504
--- /dev/null
+++ b/components/top_movers.py
@@ -0,0 +1,100 @@
+"""Top Movers component — day gainers, losers, most active."""
+import streamlit as st
+import yfinance as yf
+
+
+@st.cache_data(ttl=180)
+def _fetch_movers(screen: str, count: int = 8) -> list[dict]:
+ try:
+ result = yf.screen(screen, count=count)
+ return result.get("quotes", [])
+ except Exception:
+ return []
+
+
+def _fmt_pct(val) -> str:
+ try:
+ return f"{float(val):+.2f}%"
+ except Exception:
+ return "—"
+
+
+def _fmt_price(val) -> str:
+ try:
+ return f"${float(val):,.2f}"
+ except Exception:
+ return "—"
+
+
+def _mover_row(q: dict):
+ symbol = q.get("symbol", "")
+ name = q.get("shortName") or q.get("longName") or symbol
+ price = q.get("regularMarketPrice")
+ chg_pct = q.get("regularMarketChangePercent")
+ chg_abs = q.get("regularMarketChange")
+
+ try:
+ chg_f = float(chg_pct)
+ color = "#2ecc71" if chg_f >= 0 else "#e74c3c"
+ sign = "+" if chg_f >= 0 else ""
+ pct_str = f"{sign}{chg_f:.2f}%"
+ except Exception:
+ color = "#9aa0b0"
+ pct_str = "—"
+
+ try:
+ abs_str = f"({'+' if float(chg_abs) >= 0 else ''}{float(chg_abs):.2f})"
+ except Exception:
+ abs_str = ""
+
+ col_sym, col_name, col_price, col_chg = st.columns([1, 3, 1.5, 1.5])
+ with col_sym:
+ st.markdown(f"**{symbol}**")
+ with col_name:
+ st.caption(name[:40])
+ with col_price:
+ st.markdown(_fmt_price(price))
+ with col_chg:
+ st.markdown(
+ f"<span style='color:{color};font-weight:600'>{pct_str}</span>"
+ f"<span style='font-size:0.75rem;color:#9aa0b0'> {abs_str}</span>",
+ unsafe_allow_html=True,
+ )
+
+
+def render_top_movers():
+ st.markdown("#### 🔥 Top Movers")
+
+ tab_gainers, tab_losers, tab_active = st.tabs([
+ "📈 Gainers", "📉 Losers", "⚡ Most Active"
+ ])
+
+ screens = {
+ "gainers": "day_gainers",
+ "losers": "day_losers",
+ "active": "most_actives",
+ }
+
+ with tab_gainers:
+ quotes = _fetch_movers(screens["gainers"])
+ if quotes:
+ for q in quotes:
+ _mover_row(q)
+ else:
+ st.caption("No data available.")
+
+ with tab_losers:
+ quotes = _fetch_movers(screens["losers"])
+ if quotes:
+ for q in quotes:
+ _mover_row(q)
+ else:
+ st.caption("No data available.")
+
+ with tab_active:
+ quotes = _fetch_movers(screens["active"])
+ if quotes:
+ for q in quotes:
+ _mover_row(q)
+ else:
+ st.caption("No data available.")