All projects

Case 01 / Prediction, optimization, product

SmartPlay FPL

Player forecasts and multi-week squad planning for Fantasy Premier League, built around a separately testable prediction model and optimizer.

Role
Founder; product and engineering
Status
Live + sourceLive product; open-source model and solver
Period
Jan 2026 - present
Key decision
Keep prediction and optimization separate so either one can be evaluated, replaced, or reused without rewriting the other.
Evidence
Live product. Open model, weights, and solver. Evaluated across 24 clean gameweeks with three seeds.
SmartPlay player browser comparing price, expected minutes, expected points, and upcoming opponents.
Player projections, prices, and fixtures in the live comparison view. Capture: September 2026.

A better player forecast is only half the problem.

Fantasy managers make one decision at a time, but the rules couple those decisions across budget, formation, transfers, captaincy, chips, and future gameweeks.

The input is noisy too. A player forecast depends on uncertain minutes, form, opponents, and sparse high-scoring events. A useful product has to make that uncertainty visible without leaving the manager with a spreadsheet and a shrug.

Supporting artifact

Model quality is part of the product.

SmartPlay model audit screen showing evaluation metrics and their interpretation.
An earlier model-audit screen. Values reflect that captured version; the current evaluation protocol is described below.

From match data to a legal squad.

A scheduled TypeScript pipeline coordinates source ingestion and Python model sidecars. Each run writes its own rows, passes quality gates, and is published atomically to PostgreSQL. The web app reads that published state; solver jobs cross an explicit projection contract into a separate worker.

System map / SmartPlay
SmartPlay FPL system architectureFPL and Understat data feed a scheduled TypeScript pipeline that calls Python model sidecars. The pipeline publishes one validated run to PostgreSQL. The web application, worker-backed HiGHS solver, and chat clients read through explicit boundaries, while an independent data-quality gate can roll back a failed publication.FPL APIUnderstatScheduled orchestrationTypeScript pipeline + Dastaningest, validate, predict, publishJSON over stdioPythonmodel sidecarsSingle source of truthPostgreSQLone atomically published runData-quality gaterollback on failureNext.js productexplore and compareWorker + HiGHSplans and solver queueChat clientsHTTP only, no DB access
  1. 01FPL + UnderstatFixtures, players, xG and xA
  2. 02Pipeline + DastanIngest, validate, predict
  3. 03PostgreSQL SSOTAtomic published run
  4. 04Web + workerExplore, queue, explain
  5. 05HiGHS solverTransfers, lineup, chips
The pipeline owns published prediction state. Product surfaces and the separate optimizer consume it through explicit contracts.

The trade-offs.

01

Separate the predictor from the decision-maker

Instead of
Build one end-to-end service that forecasts players and immediately returns a transfer plan.
Choice
Publish Dastan and the optimizer as separate artifacts with a documented projection contract.
Why it holds
Forecast quality and decision quality are different questions. The boundary lets either side be tested, replaced, or used with another model.
Cost
The contract, versioning, and production parity between repositories need active maintenance.
02

Measure the noise floor before trusting a feature

Instead of
Keep a feature when a single training run shows a small lift.
Choice
Measure seed-to-seed objective movement, use three seeds, and set the keep margin from the observed noise.
Why it holds
The measured 0.0106 seed noise was larger than most plausible feature effects. Smaller one-off gains were not evidence.
Cost
Experiments take longer, and plausible features are rejected when the evidence cannot clear the threshold.
03

Publish a complete run, not rows in place

Instead of
Update the live prediction table as each step finishes.
Choice
Write run-scoped data, validate it, then flip the published run inside a transaction with rollback to the last known-good run.
Why it holds
The interface and solver should never observe a mixture of old and new predictions.
Cost
Run history consumes storage and the publish state needs concurrency controls.

A live product and reproducible evaluation.

Built

  • A scheduled multi-step pipeline joining FPL and Understat data, Python prediction sidecars, SQL checks, and post-publish data-quality gates.
  • A PostgreSQL single source of truth with a published-run flag, supporting tables, and a worker-backed solver queue.
  • A Next.js product surface plus Discord and Telegram clients that use the same API rather than touching the database.
  • Dastan: six seasons, 163,072 player-fixtures, 286 features, trained weights, training data, and a reproducible evaluation harness.
  • SmartPlay Solver: the production multi-gameweek mixed-integer program packaged around the open-source HiGHS optimizer.

Evaluation

  • Dastan scores predictions inside each gameweek and averages those scores, because pooling a whole season answers a different and easier question.
  • The headline walk-forward evaluation retrains without each test window and averages three seeds across 24 clean gameweeks.
  • A 500-trial hyperparameter search failed to improve the model out of sample. The negative result is published with the successful experiments.

Supported result

  • The released model, weights, training frame, predictions, baseline reports, and solver are inspectable rather than represented by a single leaderboard number.
  • The live product connects those projections to squad exploration, transfer planning, captaincy, bench order, and chip decisions.

What this does not solve.

Current limitations

  • The model ranks players better than it estimates exact points for known starters; current documentation reports conservative starter projections.
  • It sees Premier League minutes, not every domestic cup or European fixture, so congestion remains incomplete.
  • A top-ranked prediction is still uncertain. The system supports judgment; it does not replace it.

Next improvements

  • Add all-competition minutes so short-rest and congestion features reflect the real schedule.
  • Keep targeting the starters cohort, where added features have not yet shown a reliable gain.
  • Make uncertainty and plan sensitivity more prominent at the point where a manager commits to a move.

Attribution

  • Dastan uses OpenFPL feature engineering under its MIT license and replaces the estimator; the case study links the upstream research and repository through Dastan.
  • The solver NOTICE credits the Apache-2.0 FPL optimization work that informed its formulation. HiGHS performs the optimization.