← All findingssource · memory/project_parlay_pipeline.md
Shipped 2026-06-11 (grading-service merge 8621595, branch parlay-pipeline): nightly 2-leg + 3-leg parlay slips per league, built at the end of each league's 18:00 UTC fanout, settled off bet_clv leg results in the cron settle loop.
- Formulas:
parlay{2,3}_disciplined(Core legs) +parlay{2,3}_pinn(pinn_devig legs, ±200 juice cap). One slip per (league, slate_date, formula_id) in newparlay_slipstable (migration v12). Legs = JSONB by physical-bet identity. - Honesty contract: legs ranked by de-vig fair prob (floor 0.55), one per player, joint_prob = product × 0.95 per same-game pair, non-positive-EV slips NOT emitted. Settlement book-style: any loss = loss (early), push/void reduce, payout_decimal = product of WON legs.
- [internal detail removed]
- Env: PARLAY_MIN_LEG_PROB / PARLAY_SAME_GAME_HAIRCUT / PARLAY_MAX_LEG_JUICE.
- Keystone fix shipped with it: fanout odds were fetched per DATE with no league filter → pinn_devig emitted every slate player (cross-league!) against every game → 599/601 WNBA + 2789/2830 MLB picks settled void. Now
?league=X&date=Y+ per-gamefilterOddsForGameon prop.game_id; pinn_devig bumped v1.1.0. Post-fix CLV buckets start clean from 2026-06-11 fanout. - First slips populate at the 2026-06-11 18:00 UTC fanout. Success =
parlays/performancehit_pct converging on avg_joint_prob + positive ROI over n≥~100 decided slips. - WNBA
parlay*_disciplinedstays empty until project_wnba_snapshot_players_regression is fixed (pinn variants work meanwhile).
Why: goal session "high-success multi-leg parlays NBA+WNBA". Doc: grading-service/docs/parlay-pipeline.md.
2026-07-16 — SURFACED + first record. No frontend ever consumed /parlays/nightly ("operator
card" never built) — fixed: "THE SLIPS" on nightlypicks /ledger (nightly-picks 2d73338→ this commit,
SlipsBoard.tsx + getParlaysCached, 14d window + /parlays/performance strip). All-time record: 17
slips, ALL pinn-sourced (disciplined pool has contributed ZERO legs — starves at 18:00Z, backlog
fanout item = the quality upgrade). 2-leg: 4-5-3void +4.77u (+53% ROI on 9 decided; realized 44%
vs claimed 34% at ~3.4x avg — calibration GOOD side, n tiny). 3-leg: 0-3-2 −3.0u (noise, 20% jp).
June 11-17 slips w/ +130% EV all VOIDED (phantom-EV era pre game-scoping fix); post-fix claims
+8-15% sane. User direction: prefers 2-leg cross-game, $50/slip sizing discussed (+$5-7/slip
expected at claimed probs, not the +53%).
2026-07-17 — "where are the parlays" check, verified still live. User couldn't find them;
confirmed via live curl + fetch of nightlypicks.com/ledger that "THE SLIPS" section renders fine
with real data — last posted 2026-07-12 (2 slips, both LOSS), then a 5-night silent streak
(7/13-7/17) with WNBA games every night (2-4/night) but nothing clearing the EV bar. NOT a
regression: a same-length 5-night drought happened 7/1-7/5, and disciplined-sourced legs are
still ZERO (the "starves at 18:00Z fanout" issue in project_wnba_backlog is still open — every
slip to date is pinn-sourced only). GET /parlays/nightly is the ground truth per-date; the
grading-service /scorecard "disciplined.today.n" field reads 0 on EVERY day checked including
7/12 (the day slips actually posted from disciplined-tagged legs) — looks like a stale/mismatched
counter in the scorecard endpoint, not evidence the legs pool is empty. Separately (unrelated dead
code, not a regression): nightly-picks/src/components/picks/GamePreview.tsx has a per-game
"Parlay of the Day" card (content.parlay_of_day) that is NOT rendered by any live route — the
current game-detail page (/[league]/[date]/[gameSlug]) uses GameReadView, which has no parlay
section at all. If the user ever expected a parlay card on individual game pages, that path was
never wired up in this repo's current design (nightly-picks history is a single squashed "initial
commit", so it shipped unwired from day one, not removed).