A market-intelligence engine for PSA-graded cards: daily population snapshots, a scored Buy Board, and universe-wide supply analytics. Everything here is computed from data this system captures itself — the wiki tells you what each number means and how much to trust it.
PSA_DAILY_CALL_BUDGET). Watched certs and every Buy Board target's spec are refreshed daily, least-recently-attempted first, so nothing starves under quota.PARSEBOT_API_KEY.One row per (card spec, grade) target. B is the composite Buy Score — a weighted blend of the three buckets, recomputed live as you drag the S/H/V sliders (weights apply at query time; nothing is baked in). Row actions (watch / bought / passed) are logged with timestamps — they become training labels to fit the weights against real outcomes later. The segmented bar under B shows each bucket's weighted contribution.
s3_prior flag; the real measurement phases in automatically (partial at 30 days, full at 90).Missing components renormalize away rather than dragging a bucket to zero — a card with no comps still gets an honest scarcity-driven B.
FV = median of the most recent ≤8 confidence-gated comps within 90 days (window widens to 180 when thin). Outliers beyond 0.25×–4× of the window median are excluded automatically (shill guard); you can override per-sale (exclude/include on the card page). Tiers reflect the newest usable comp: T5 <14d · T4 <30d · T3 <90d · T2 <180d · T1 older.
Confidence = pop-history factor (saturates at 30 days) × comp-count factor (saturates at 5 in 90d) × freshness factor. It encodes the cold-start ladder — nothing pretends to precision it doesn't have.
| Flag | Meaning |
|---|---|
| falling_knife | Big apparent gap on a collapsing 14-day price — hidden from the default board (the classic value-screener trap). |
| fv_single_sale | Fair value rests on one comp — visible, but confidence ≈ 0. |
| pop_anomaly | Population moved backwards (PSA recategorization) — never treated as demand. |
| s3_prior / h1_neutral / h2_neutral / h3_neutral | Cold-start: that component is on a prior or neutral until enough history exists. |
| no_pop_data | No population snapshots for this spec yet — it enters the next daily run. |
Two modes, one table. All tracked sets ranks every snapshotted card by relative pop growth (the movers view). Selecting a set shows its full PSA-style pop report — every card with Pop 10 / Pop 9 / Total / gem % — with the heat columns overlaid where movement exists, plus a set header (total pop, gem rate, days tracked, daily growth, 90-day projection). All columns sort on click; the search box filters by card, number, or variety; the mix dropdown isolates supply stories. + track a set onboards any PSA set by its HeadingID and can pull the first snapshot immediately. Columns:
Heat needs two snapshot days per card minimum; it sharpens every day the cron runs.
| Signal | Where | Reads as |
|---|---|---|
| Grading momentum | H1, Heat | Submissions surging vs peers → informed money ahead of price. Leads price because grading takes weeks and costs money. |
| Set maturation | /api/sets/:id/maturation | Total-pop and gem-rate curves per set with age. Projection is gated until ≥14 days of history; matching young sets against historical analogue curves is the end-state once long baselines accumulate. |
| Gem-rate drift | Heat, maturation | Early submissions skew gem; decay in gem rate → future 10 supply decelerates → 10:9 premium widens mechanically. |
| Grade-mix forensics | Heat (mix) | Different supply stories: raw dredging adds cheap supply, gem flow adds top-end supply, churn adds none. |
Works for any PSA pop-report set. Get the HeadingID from the psacard.com pop URL — e.g. psacard.com/pop/tcg-cards/2026/pokemon-cri-en-chaos-rising/339974 — then POST /api/sets/track {"setId":"339974","categoryId":"156940"} (156940 covers TCG; the first snapshot verifies it). Each tracked set costs ~1 ingestion call per 300 cards per day against the monthly credit pool, so a 47-card modern set is nearly free and a 500-card set costs 2/day. Every card in a tracked set automatically joins the heat universe and the S1 percentile base.
The daily crawl writes a warehouse row per card per day (populations, gem rate, and 7/30/90-day growth, Δ10s, gem drift, and mix — all precomputed). Everything reads from it, so queries stay fast as history grows for years.
POST /api/query with a JSON filter — era, game, set, pop/gem/growth ranges, mix signal, a search string — plus a window (7/30/90), sort, and limit. Every field is allowlisted, so it's safe to expose. Add ?format=csv to any query for a spreadsheet download.GET /api/sets/:id/diff?from=&to= returns per-card deltas between any two captured dates — the "what changed" view, also CSV-able.First visit asks for your name and issues a personal token (stored only in your browser). Every watch / buy / pass is written to an immutable decision log with who, when, the ask, and a frozen snapshot of that day's scores — passing asks for a reason. This is the training data that later tunes the Buy Score against what actually made money; it's append-only, so changing your mind never erases the earlier call. Refractors SSO will map onto these identities later without losing history.
| Route | What |
|---|---|
| GET /api/board | Scored targets; ?ws/wh/wv weights, ?status, ?showAll, ?honest=1 |
| GET /api/market/heat | Universe heat; ?days=7|30|90, ?minPop |
| GET /api/sets/:id/pop · /maturation | Per-card pop table · maturation curves |
| GET /api/market/movers · /set-movers | Pop deltas for watched certs · tracked-set cards |
| GET /api/certs/:cert · /history | Cert lookup (cache-first) · daily pop series |
| POST /api/targets · PATCH /api/targets/:id 🔒 | Add targets, set asks, bought/passed |
| POST /api/sales/manual 🔒 | Enter comps (single or array) |
| POST /api/sets/track 🔒 · GET /api/sets | Track a set · list tracked |
| GET/POST /api/jobs/snapshot 🔒 · POST /api/jobs/metrics 🔒 | Run the daily pipeline · recompute scores |
🔒 = Authorization: Bearer CRON_SECRET (or the board's secret field — stored only in your browser). Mutations require your authenticated browser session while Vercel deployment protection is on.