Download app.py from mystycrym/SPY_500: direct link, hf CLI and curl.
- Browser
- Download file 7.44 kB
-
https://huggingface.co/spaces/mystycrym/SPY_500/resolve/main/app.py
- Command line
-
hf download hf://spaces/mystycrym/SPY_500/app.py
-
curl -L -o app.py https://huggingface.co/spaces/mystycrym/SPY_500/resolve/main/app.py
7.44 kB
| import datetime as dt | |
| from datetime import timezone | |
| import pandas as pd | |
| import streamlit as st | |
| import yfinance as yf | |
| import plotly.graph_objects as go | |
| st.set_page_config(page_title="SPY Dashboard", layout="wide") | |
| TICKER = "SPY" | |
| # ---------------------- Helpers ---------------------- # | |
| # cache 1 hour to be gentle on API | |
| def fetch_prices(timeframe: str) -> pd.DataFrame: | |
| """Fetch OHLCV data for SPY for the selected timeframe.""" | |
| kwargs = dict(auto_adjust=True, progress=False) | |
| if timeframe == "1D": | |
| return yf.download(TICKER, period="1d", interval="1m", **kwargs) | |
| elif timeframe == "1W": | |
| return yf.download(TICKER, period="7d", interval="15m", **kwargs) | |
| elif timeframe == "1M": | |
| return yf.download(TICKER, period="1mo", interval="1h", **kwargs) | |
| elif timeframe == "YTD": | |
| start = dt.datetime(dt.datetime.now().year, 1, 1, tzinfo=timezone.utc) | |
| return yf.download(TICKER, start=start, interval="1d", **kwargs) | |
| elif timeframe == "1Y": | |
| return yf.download(TICKER, period="1y", interval="1d", **kwargs) | |
| elif timeframe == "5Y": | |
| return yf.download(TICKER, period="5y", interval="1wk", **kwargs) | |
| else: # "ALL" | |
| # Weekly gives a clean long-term view without too many points | |
| return yf.download(TICKER, period="max", interval="1wk", **kwargs) | |
| def fast_info(): | |
| t = yf.Ticker(TICKER) | |
| return getattr(t, "fast_info", {}) | |
| def is_intraday(df: pd.DataFrame) -> bool: | |
| if df.empty: | |
| return False | |
| last_date = df.index[-1].date() | |
| return (df.index.date == last_date).sum() > 1 | |
| def last_day_stats(df: pd.DataFrame) -> dict: | |
| """Return open/high/low/close/volume for the latest trading day represented in df.""" | |
| if df.empty: | |
| return {} | |
| # pick the latest calendar day present in the data | |
| last_dt = pd.to_datetime(df.index[-1]) | |
| mask = (pd.to_datetime(df.index).date == last_dt.date()) | |
| day = df.loc[mask] | |
| if day.empty: | |
| # fallback to last row | |
| row = df.iloc[-1] | |
| return dict( | |
| open=float(row["Open"]), | |
| high=float(row["High"]), | |
| low=float(row["Low"]), | |
| close=float(row["Close"]), | |
| volume=int(row.get("Volume", 0)), | |
| ) | |
| # intraday volume is sum across the day; daily interval has one row already | |
| vol = int(day["Volume"].sum() if is_intraday(df) else day["Volume"].iloc[-1]) | |
| return dict( | |
| open=float(day["Open"].iloc[0]), | |
| high=float(day["High"].max()), | |
| low=float(day["Low"].min()), | |
| close=float(day["Close"].iloc[-1]), | |
| volume=vol, | |
| ) | |
| def year_range_52w() -> tuple[float, float]: | |
| df = yf.download(TICKER, period="1y", interval="1d", auto_adjust=True, progress=False) | |
| if df.empty: | |
| return (None, None) | |
| return (float(df["Low"].min()), float(df["High"].max())) | |
| def human_int(n): | |
| # 1,234,567 -> 1.23M style | |
| try: | |
| n = float(n) | |
| except Exception: | |
| return "-" | |
| for unit in ["", "K", "M", "B", "T"]: | |
| if abs(n) < 1000: | |
| return f"{n:.2f}{unit}".rstrip("0").rstrip(".") + unit | |
| n /= 1000 | |
| return f"{n:.2f}P" | |
| # ---------------------- UI ---------------------- # | |
| st.title("📈 SPY (S&P 500 ETF)") | |
| col_l, col_r = st.columns([3, 1]) | |
| with col_l: | |
| timeframe = st.radio( | |
| "Timeframe", | |
| ["1D", "1W", "1M", "YTD", "1Y", "5Y", "ALL"], | |
| horizontal=True, | |
| index=3, # default YTD | |
| ) | |
| with col_r: | |
| show_ma = st.toggle("Show Moving Averages (20/50)", value=False) | |
| log_scale = st.toggle("Log Scale", value=False) | |
| # Data | |
| prices = fetch_prices(timeframe) | |
| fi = fast_info() | |
| if prices.empty: | |
| st.warning("No data available right now. Market may be closed or the data provider rate-limited. Try a different timeframe.") | |
| st.stop() | |
| stats = last_day_stats(prices) | |
| prev_close = None | |
| if len(prices) > 1: | |
| if is_intraday(prices): | |
| # previous trading day close | |
| prev_date = pd.to_datetime(prices.index[-1]).date() | |
| # take rows from prior calendar day | |
| prior = prices.loc[prices.index.date < prev_date] | |
| if not prior.empty: | |
| prev_close = float(prior["Close"].iloc[-1]) | |
| else: | |
| prev_close = float(prices["Close"].iloc[-2]) | |
| # Chart | |
| fig = go.Figure() | |
| fig.add_trace( | |
| go.Candlestick( | |
| x=prices.index, | |
| open=prices["Open"], | |
| high=prices["High"], | |
| low=prices["Low"], | |
| close=prices["Close"], | |
| name="Price", | |
| increasing_line_color="#26a69a", | |
| decreasing_line_color="#ef5350", | |
| ) | |
| ) | |
| if show_ma and len(prices) >= 50: | |
| ma20 = prices["Close"].rolling(20).mean() | |
| ma50 = prices["Close"].rolling(50).mean() | |
| fig.add_trace(go.Scatter(x=prices.index, y=ma20, name="MA 20", mode="lines")) | |
| fig.add_trace(go.Scatter(x=prices.index, y=ma50, name="MA 50", mode="lines")) | |
| fig.update_layout( | |
| margin=dict(l=10, r=10, t=10, b=10), | |
| height=520, | |
| yaxis_type="log" if log_scale else "linear", | |
| xaxis_rangeslider_visible=False, | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Top metrics row | |
| cur = stats.get("close") | |
| chg = (cur - prev_close) if (cur is not None and prev_close) else None | |
| pct = (chg / prev_close * 100) if (chg is not None and prev_close) else None | |
| m1, m2, m3, m4 = st.columns(4) | |
| m1.metric("Last Price", f"${cur:,.2f}" if cur else "-", f"{(chg):+,.2f}" if chg else None) | |
| m2.metric("Change %", f"{(pct):+.2f}%" if pct else "-") | |
| day_range = f"${stats.get('low', 0):,.2f} – ${stats.get('high', 0):,.2f}" if stats else "-" | |
| m3.metric("Day's Range", day_range) | |
| yr_low, yr_high = year_range_52w() | |
| yr_range = f"${yr_low:,.2f} – ${yr_high:,.2f}" if yr_low and yr_high else "-" | |
| m4.metric("52-Week Range", yr_range) | |
| st.write("---") | |
| # Details table (Robinhood-style) | |
| # Pull additional stats from fast_info, with safe fallbacks | |
| open_p = stats.get("open") | |
| high_p = stats.get("high") | |
| low_p = stats.get("low") | |
| vol = stats.get("volume") | |
| avg10d = getattr(fi, "ten_day_average_volume", None) or fi.get("ten_day_average_volume") | |
| avg3m = getattr(fi, "three_month_average_volume", None) or fi.get("three_month_average_volume") | |
| market_cap = getattr(fi, "market_cap", None) or fi.get("market_cap") | |
| prev_close_fi = getattr(fi, "previous_close", None) or fi.get("previous_close") | |
| details = pd.DataFrame( | |
| { | |
| "Field": [ | |
| "Previous Close", | |
| "Open", | |
| "Day’s High", | |
| "Day’s Low", | |
| "Volume (Today)", | |
| "Avg Volume (10D)", | |
| "Avg Volume (3M)", | |
| "Market Cap", | |
| "52-Week High", | |
| "52-Week Low", | |
| ], | |
| "Value": [ | |
| f"${(prev_close or prev_close_fi):,.2f}" if (prev_close or prev_close_fi) else "-", | |
| f"${open_p:,.2f}" if open_p else "-", | |
| f"${high_p:,.2f}" if high_p else "-", | |
| f"${low_p:,.2f}" if low_p else "-", | |
| human_int(vol) if vol else "-", | |
| human_int(avg10d) if avg10d else "-", | |
| human_int(avg3m) if avg3m else "-", | |
| human_int(market_cap) if market_cap else "-", | |
| f"${yr_high:,.2f}" if yr_high else "-", | |
| f"${yr_low:,.2f}" if yr_low else "-", | |
| ], | |
| } | |
| ) | |
| st.subheader("Details") | |
| st.dataframe( | |
| details, | |
| use_container_width=True, | |
| hide_index=True, | |
| ) | |
| st.caption("Data source: Yahoo Finance via yfinance") | |