Single-page market dashboard for SPY/QQQ traders. Pulls 18 free public data feeds — spot, options walls, Kalshi prediction-market distributions, Polymarket binary bets, Fed path — into one Streamlit page with zero auth keys, zero servers, and zero ongoing cost.
🔗 Live demo: https://ethanyang85-market-predict.hf.space/ (hosted free on Hugging Face Spaces, no login)
Want your own copy? See Deploy below — Hugging Face Spaces is the recommended path (Docker, no idle hibernation). Streamlit Community Cloud also works as an alternative.
| Row | Panels | Sources |
|---|---|---|
| Header | spot · underlying · futures (overnight) · VIX · ATM IV · P/C OI | yfinance · CBOE (ATM IV, P/C OI) |
| Today's setup | one-line synthesis — gamma regime · walls/pin · P/C positioning · vol vs 1m avg · market-implied direction | derived from the panels below (no extra fetch) |
| Cross-asset | rates · credit · USD · gold · oil — 1d / 5d % (TLT · HYG · UUP · GLD · USO) | yfinance |
| Row 1 | 3-month K-line + volume · VIX 1m mini | yfinance |
| Row 2 | Options walls (call/put OI, max pain, γ flip) · key levels | CBOE delayed quotes |
| Dealer gamma (GEX) | net dealer gamma vs price, shaded by regime (stabilizing/accelerant), γ-flip marked | recomputed from the CBOE chain |
| Row 3 | Daily close brackets — Kalshi + Polymarket overlaid · today's UP/DOWN binary | Kalshi KXINX · Polymarket closes above |
| Row 4 | FOMC stacked path · Kalshi rate-cuts count · Polymarket next-FOMC outcomes | Kalshi KXFEDDECISION, KXRATECUTCOUNT · Polymarket |
| Tabs | Monthly one-touch · Yearly distribution + year-max/min · Recession gauge · Mag 7 ranking · 2026 cuts count | Polymarket · Kalshi KXINXY, KXINXMAXY/MINY, KXRECSSNBER · Polymarket big-tech |
Every Kalshi/Polymarket panel cites event ticker + close date + 24h volume for traceability.
| Path | Cold load | What runs |
|---|---|---|
| Snapshot hit (default) | ~1–3 s | Streamlit reads data/snapshot_SPY.json from disk and renders |
| Snapshot miss / stale | ~13 s | Falls back to live fetch: 18 APIs in parallel via ThreadPoolExecutor |
| Serial fetch (old) | ~108 s | What it was before the parallelization + snapshot pipeline |
The footer of the page tells you which path served the current view.
A GitHub Actions cron (.github/workflows/refresh-snapshot.yml) runs every 15 minutes on a fresh runner — no Yahoo throttle, no shared cloud IP — calls python -m market_predict.refresh_snapshot, and commits the new data/snapshot_*.json files to the dedicated snapshots branch (not main, to avoid redeploy churn). Both HF Spaces and Streamlit Cloud read the snapshot files over HTTP via cdn.statically.io (a global GitHub CDN, 1–4 s vs raw.githubusercontent's 5–30 s). The workflow also curls the live URL to keep containers warm. Public-repo Actions minutes are unlimited, so this is genuinely free.
If the workflow ever stalls and the snapshot is over 30 minutes old, the Streamlit app silently falls back to a live fetch — no broken page.
git clone https://github.com/YichengYang-Ethan/market-predict
cd market-predict
pip install -e .[ui]
streamlit run streamlit_app.pyOpen http://localhost:8501. Switch ticker (SPY/QQQ) in the dropdown; hit Refresh to bust the 5-minute cache.
CLI-only text snapshot (no Streamlit):
pip install -e .
python -m market_predict SPYThe live demo runs here. HF Spaces gives free Docker hosting with no idle hibernation — closer to "always-on" than Streamlit Cloud's 12h spin-down.
- Sign up at https://huggingface.co · New Space → SDK: Docker · template: Blank
git cloneyour new Space's repo (a separate repo from this one), and copy in:- The
market_predict/package +requirements.txt+pyproject.tomlfrom this repo - A
Dockerfilethat runsstreamlit run market_predict/ui/app.pydirectly (do not usestreamlit_app.py— Python'ssys.modulescache makes the import-shim pattern break Streamlit reruns) - A
README.mdwith HF Space frontmatter at the top:--- title: Market Predict emoji: 📊 sdk: docker app_port: 8501 license: mit ---
- The
git push→ HF auto-builds the container, ~60s to RUNNING.
- Fork this repo on GitHub
- share.streamlit.io → New app → connect repo, branch
main, filestreamlit_app.py - Deploy. Auto-redeploys on every push to
main. No secrets to configure.
⚠️ Streamlit Cloud's free tier hibernates after 12h idle. First click after sleep takes 30-60s to wake the container (no way around it from outside). The GitHub Actions cron in this repo includes a warm-ping but it's best-effort. HF Spaces avoids this entirely.
- Spot price, 3-month OHLCV history (auto-adjust off)
- VIX (
^VIX) — 1-month history + spot - E-mini futures (
ES=F/NQ=F) — overnight change vs previous close
Endpoint: cdn.cboe.com/api/global/delayed_quotes/options/{SYM}.json (indices prefixed _, e.g. _SPX).
- Full options chain — call/put open interest, exchange-computed greeks (delta, gamma, theta, vega), IV, volume
- Powers Row 2 walls, max pain, gamma flip, and the header ATM IV / P/C OI
- Why not yfinance for options: Yahoo returns OI≈0 for SPY/QQQ outside RTH, so the panel went dark every evening/weekend. CBOE serves the last session's snapshot 24/7, and OI is an end-of-day figure anyway (OCC publishes pre-open), so a delayed snapshot is the right granularity for walls.
- Caveat: undocumented CDN endpoint (behind cboe.com's delayed-quote pages); 15-min delayed; could change without notice.
Endpoint: api.elections.kalshi.com/trade-api/v2/markets?series_ticker=X
| Series | Used for |
|---|---|
KXINXY / KXNASDAQ100Y |
Yearly probability distribution (~$25 SPX brackets) |
KXINX / KXNASDAQ100 |
Daily close brackets — filtered to soonest expiry |
KXINXMAXY / KXINXMINY (+ QQQ analogs) |
Year max/min one-touch cumulative probabilities |
KXFEDDECISION |
Next 3 FOMC meetings: hold / cut N bp / hike N bp |
KXRATECUTCOUNT |
2026 total rate-cut count (0 cuts, 1 cut, ... 9 cuts) |
KXRECSSNBER |
NBER recession binary (2026 + 2027 events) |
Endpoint: gamma-api.polymarket.com/events?tag_slug=X&active=true&closed=false
| Tag | Used for |
|---|---|
finance |
Monthly one-touch: "Will S&P 500 (SPX) hit $7,450 (HIGH) in June?" |
daily-close |
Daily "closes above $X" cumulative brackets (~$5 SPY strikes) |
daily-close |
Today's UP/DOWN binary (ETF version preferred over cash-index) |
fed / fed-rates / jerome-powell |
Next FOMC decision outcomes + 2026 cuts count |
big-tech |
"Largest company end of [period]" Mag 7 ranking |
Live API drift is real; this dashboard tries to surface clean numbers rather than raw probes. Specifically:
- KXINX returns multiple expiries in one call (today + Friday). Daily brackets chart filters to the soonest
close_timeonly. - Polymarket cumulative is non-monotonic under thin volume — e.g.
P(close ≥ $735) = 97%andP(close ≥ $740) = 98.5%happens when the $740 bid is stale. The chart enforces right-to-left monotonicity before differencing, so brackets sum sensibly. - SPX/SPY ratio is computed live from
^GSPC / SPY(~10.02 with dividend drift), not hardcoded at 10.0, so Kalshi-SPX brackets and Polymarket-SPY brackets sit on the same x-axis. - SPY (SPY) vs S&P 500 (SPX) Up/Down: when Polymarket lists both, the ETF version is preferred — it matches the spot price quoted in the header.
- OI units: Kalshi
open_interest_fpis contract count (each contract pays at most $1). Reported asN,NNN ctrs, not USD. - Mag 7 placeholders (
Company A..T,Other) are filtered — only real tickers show.
See commit e2c1141 for the audit that surfaced these.
- Hardcoded ticker map (
tickers.py). Adding a ticker = one entry after verifying its Kalshi series has active markets. Explicit > clever. - Bracket parsing reads
yes_sub_title, not the ticker suffix.KXINX-...-T9000means "above 9000" butKXINX-...-T4000means "below 4000". Suffixes are not a reliable direction encoding. - Near-the-money walls: call/put wall logic restricts to ±8% from spot. Absolute max OI often sits at deep-OTM crash hedges (e.g. SPY $580 puts when spot is $750), which are not tradable inflection points.
- Gamma flip: net dealer GEX (long-calls / short-puts convention) zero-crossing across a 41-point ±10% spot grid. ATM IV used uniformly across strikes — good enough for the flip level, not a full surface.
- 5-minute
st.cache_dataTTL keeps the API call budget light. Refresh button clears it. - Today's setup synthesis (
transforms/setup.py) is a deterministic read of fields already on the view — gamma regime vs γ-flip, walls/pin, P/C positioning, VIX vs its 1-month mean, market-implied direction — descriptive, not advice. No extra fetch. - Editorial theme — warm canvas, Source Serif 4 headlines, Inter labels, JetBrains-Mono numbers, single deep-red accent — aligned to yichengyang-ethan.github.io so the embedded view reads as part of the site rather than a bolted-on app.
┌───────────────────────────────────────────────────────────────────────┐
│ market-predict │
│ Ticker [SPY ▾] [Refresh] │
├──────────┬──────────┬───────────┬────────┬─────────┬───────────────────┤
│ spot │ S&P 500 │ ES fut │ VIX │ ATM IV │ P/C OI │
├──────────┴──────────┴───────────┴────────┴─────────┴───────────────────┤
│ Row 1: 3-month K-line + Volume │ VIX 1m mini │
│ Row 2: Options walls (OI bars) │ Call wall / Put wall / γ flip │
│ Row 3: Daily brackets (Kalshi + Poly) │ P(close UP) / P(open UP) │
│ + Today's setup · Cross-asset strip · Dealer gamma (GEX) panels │
│ Row 4: FOMC path │ 2026 cuts (Kalshi) │ Next FOMC (Polymarket) │
├────────────────────────────────────────────────────────────────────────┤
│ Tabs (all inline): Monthly · Yearly · Macro/Recession · Mag 7 · 2026 │
└────────────────────────────────────────────────────────────────────────┘
All tabs render inline — no click-to-reveal gate. On a warm snapshot every panel paints on load. The dashboard also runs embedded: ?site=1 strips the in-app header and posts its content height to the host page, so it sits in an auto-height iframe (no inner scroll) on the personal site at yichengyang-ethan.github.io/market-predict.
Color convention:
- 🟣 Kalshi data series — purple
- 🟡 Polymarket data series — gold
- 🔵 Spot price (vline / hline)
- 🟢 Call OI / call wall · 🔴 Put OI / put wall
- ⚫ Max pain dotted gray · 🟠 Gamma flip dotted orange
- Options data is 15-min delayed and end-of-day for OI. The walls panel uses CBOE's free delayed-quote CDN (
cdn.cboe.com/.../delayed_quotes), which switched in after yfinance stopped publishing real near-spot OI for large ETFs (yfinance returned OI≈0 across the ATM band, blanking the panel after hours). CBOE serves the last session's snapshot 24/7 with real OI + greeks — fine for daily context, not for intraday 0DTE. It's an undocumented endpoint and could change without notice. - yfinance spot / history / VIX / futures are ~15 min delayed (fine for daily context; not for intraday 0DTE)
- Kalshi yearly buckets are wide ($25 SPX / $50 NDX) — long tails carry mass outside the central display zone
- SPY/QQQ are ETFs; Kalshi only lists S&P 500 (
^GSPC) and Nasdaq 100 (^NDX) cash-index contracts. ETF→index basis ignored - Polymarket monthly one-touch chart has an intentional gap around spot: Polymarket only lists OTM strikes (HIGH > spot, LOW < spot) since ATM contracts would already be trivially resolved
- Polymarket low-volume strikes give noisy cumulative pricing; the monotonization fix smooths but does not eliminate this
- No historical snapshots yet — coming in v0.2
- v0.2 — daily snapshot job (writes Parquet, seeds backtest data); BTC/ETH ticker support
- v0.3 — Brier-score calibration of Kalshi vs realized outcomes (needs ~60 days of v0.2 snapshots first)
- v0.4 — implied-distribution comparison: Kalshi probability vs option-IV-implied probability at same strike
MIT