"""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"{pct_str}"
f" {abs_str}",
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.")