builder, creator, pokemon master & raptors

what am i up to rn?
stuff ive built before my recent reads

projects

← back to projects

yapper lottery

random impromptu topic yapping generator

?
← back

reads

← all projects

fantasy xchange, monitoring the fantasy stock of nba players

TL;DR β€” I built Fantasy Xchange (FX), a web app that turns every active NBA player into a fantasy "ticker" you can track. Each player gets a single price (the FX Score) driven by their real box scores, and the whole league renders as a dark, Bloomberg-style finance terminal β€” risers, divers, breakouts, a live ticker, plus a small quant lab that runs factor research on fantasy production. It's a full product: FastAPI backend, Svelte 5 frontend, Supabase, and a daily sync worker, all independently deployed. Try it live.

Fantasy Xchange terminal dashboard

Why I built it

Fantasy managers already think like traders. You "buy low" on a breakout, you "sell high" before a player's minutes dry up, players "rise" and "fall." I wanted to lean all the way into that and see if I could make fantasy basketball feel like watching a market β€” and use it as a serious product-engineering project: real data pipeline, real services, real deploys, not a toy.

The pitch in one line: what if you could watch an NBA player's fantasy value the way you watch a stock?

Fantasy Xchange landing β€” hero

Fantasy Xchange landing β€” market metaphor

How it works

Everything is built on one number.

  • Player = stock. Every active player is a ticker.
  • Price = FX Score. A single value that summarizes a player's all-around fantasy production from a game.
  • Momentum = form. Recent games vs. their own season baseline tells you who's heating up or cooling off.

The FX Score is computed from the real box score β€” points, rebounds, assists, with steals and blocks weighted heavily (rare, high-signal), and penalties for turnovers and missed shots. It collapses a messy stat line into one comparable number, and the entire product is built on top of it.

How the FX Score is computed

From there, momentum drives the dashboard:

rise % = (recent 3-game avg βˆ’ season avg) / season avg
  • Big positive β†’ breakout / hot streak
  • Big negative β†’ slump
  • Same logic on minutes β†’ surfaces role changes before the box score catches up

How you use it

You open the terminal β€” a four-panel dashboard that reads like a trading screen:

  • Daily Leaderboard β€” best and worst FX on the latest slate
  • Market Movers β€” top risers and divers vs. season baseline
  • Opportunity Watch β€” players gaining or losing minutes (role expansion before production follows)
  • Weekly Schedule β€” game density and back-to-backs, for streaming decisions

Click any player to pull up their detail card β€” season FX, averages, league rank, recent game log. There's a ⌘K command-palette search, a live ticker strip, and a breakout ticker for single-game spikes.

Player detail card

Top 100 by FX Score

⌘K command-palette search

Example: finding a waiver-wire sleeper

  • Open Opportunity Watch β†’ a bench player's minutes are trending up sharply
  • Their FX is still middling, so nobody's noticed yet
  • Cross-check Market Movers β†’ momentum just turned positive
  • That's your "buy low" β€” role is expanding, production usually follows

The app never tells you to draft anyone β€” it surfaces the signal, you make the call.

The quant lab

Two screens take the metaphor further and apply real systematic-investing concepts to fantasy data β€” paper analytics only, no overclaiming.

Signal Lab β€” factor IC research

  • Signal Lab runs factor research on three signals (momentum, mean-reversion/value, minutes-lead) and scores each with an Information Coefficient β€” the same rank-correlation metric quant desks use to ask "does this signal actually predict the next few games?"
  • Strategy Desk runs a paper long–short momentum book (long the top movers, short the bottom, care about the spread) with a walk-forward backtest, plus a mean-reversion watchlist gated on minutes stability β€” i.e. slumping players whose role is still intact, so a bounce is plausible.

Strategy Desk β€” long-short book + mean reversion

Takeaway

The fun was making fantasy hoops feel like a live market while keeping the engineering honest underneath β€” one clean metric, a UI that sells the metaphor, and a backend solid enough to feed it fresh every night.


Technical implementation

For the engineering-minded. Fantasy Xchange is three independently deployable services around managed Postgres β€” built so the thing that writes data can never take down the thing that serves it.

Architecture

  • Frontend β€” Svelte 5 (runes), Vite, Tailwind, deployed on Railway. Pure presentation, talks only to the API.
  • Backend β€” FastAPI + Pydantic, serving rankings, player, schedule, and strategy endpoints, plus the sync + FX computation.
  • Datasync worker β€” a scheduled cron service that wakes nightly and triggers the backend's sync endpoints behind an API key.
  • Database β€” Supabase (Postgres + PostgREST). Backend-only access via service-role key; clean HTTP seams between every service.

Engineering decisions that make it feel real-time and robust

  • Pre-compute for read speed. FX Score and all trend aggregates (fx_season, last-3, last-5, minutes trends) are computed at ingest, so the dashboard just sorts indexed columns β€” no live number-crunching on the read path.
  • Idempotent ingestion. Everything upserts, so re-running a day is safe and backfills are just a re-trigger. One LeagueGameLog call per slate instead of hammering the API per player.
  • Date-anchored windows. "Today" anchors to the most recent date with games, not wall-clock time β€” so the terminal is never blank on an off-night or in the off-season.
  • In-memory TTL cache on the hot ranking and strategy endpoints (5-min), keeping the terminal snappy under repeated reads.
  • Stateless services. No server sessions, horizontally scalable, sync is fire-and-forget via background tasks so the worker returns immediately.

The quant engine

  • A pure-Python strategy module computes Spearman-rank Information Coefficient, signal values, the long–short book, and the walk-forward backtest (rebalance every Nth game date, forward-window the next 3 games per leg, measure the spread).
  • Wrapped in cached FastAPI routes with Supabase pagination over the game-log history.

Frontend craft

  • Svelte 5 runes ($state, $derived, $effect) driving a custom finance-terminal design system β€” near-black theme, orange accent, green/red deltas, serif hero type, monospace data, CSS-animated tickers.
  • Client-side state routing with a cookie-gated landing page (7-day window), runtime-injected API host for portable deploys, and a fetchSmartDate helper that gracefully falls back a day if the latest slate is empty.

The through-line: one metric, pre-computed and served fast, behind small decoupled services β€” the product reads as a slick live market because the boring parts (ingestion, caching, date logic) are doing the heavy lifting underneath.


Note: written with the help of an llm from my project + own points, dont mind the emdashes or llm-ish words :>

Mikey