Fantasy Basketball Lineup Optimizer

Fantasy Basketball Lineup Optimizer

Fantasy Basketball Lineup Optimizer

Modeling & Optimization

Six LSTM models, one per statistical category, trained on 290 NBA players to rank the ten best season lineups from a user's roster.

Six LSTM models, one per statistical category, trained on 290 NBA players to rank the ten best season lineups from a user's roster.

Six LSTM models, one per statistical category, trained on 290 NBA players to rank the ten best season lineups from a user's roster.

Model loss curves across all six statistical categories.

The problem

In fantasy basketball you draft 13 players and start 10 each week against another manager, and the scoring weights categories differently: a point is worth 1, a rebound 1.2, an assist 1.5, a steal or a block 3, and a turnover costs you 1. Season averages are the default input for lineup decisions and they are badly behaved. They wash out form, injury recovery, and role changes, which is exactly the signal you want when deciding who to start.

Where the data came from

Three sources, all pulled in Python. We scraped player profiles from Basketball Reference with Selenium and BeautifulSoup for career statistics, used the NBA API to pull every active player and team for the season, and scraped the weekly NBA schedule into a SQL table so the training set stayed current instead of freezing at the point the project started. We then filtered to players with three or more seasons of history, which left 290 with enough data to model.

Six models instead of one

We trained one Long Short-Term Memory model per statistical category rather than a single combined one, because the categories do not behave alike. Points and rebounds are by far the most volatile, and their loss curves sit an order of magnitude above steals and blocks, which barely move at all. A single model would have been tuned to the average of those behaviors and fitted none of them well. Six models let each one learn the volatility of its own category. They cost more to train and they were much easier to debug when one of them was wrong.

The tool

A user enters their roster on the front end. The tool runs each player through all six models, weights the predicted statistics by the fantasy scoring system, and returns the ten highest scoring lineup combinations for the season.

The problem

In fantasy basketball you draft 13 players and start 10 each week against another manager, and the scoring weights categories differently: a point is worth 1, a rebound 1.2, an assist 1.5, a steal or a block 3, and a turnover costs you 1. Season averages are the default input for lineup decisions and they are badly behaved. They wash out form, injury recovery, and role changes, which is exactly the signal you want when deciding who to start.

Where the data came from

Three sources, all pulled in Python. We scraped player profiles from Basketball Reference with Selenium and BeautifulSoup for career statistics, used the NBA API to pull every active player and team for the season, and scraped the weekly NBA schedule into a SQL table so the training set stayed current instead of freezing at the point the project started. We then filtered to players with three or more seasons of history, which left 290 with enough data to model.

Six models instead of one

We trained one Long Short-Term Memory model per statistical category rather than a single combined one, because the categories do not behave alike. Points and rebounds are by far the most volatile, and their loss curves sit an order of magnitude above steals and blocks, which barely move at all. A single model would have been tuned to the average of those behaviors and fitted none of them well. Six models let each one learn the volatility of its own category. They cost more to train and they were much easier to debug when one of them was wrong.

The tool

A user enters their roster on the front end. The tool runs each player through all six models, weights the predicted statistics by the fantasy scoring system, and returns the ten highest scoring lineup combinations for the season.

The problem

In fantasy basketball you draft 13 players and start 10 each week against another manager, and the scoring weights categories differently: a point is worth 1, a rebound 1.2, an assist 1.5, a steal or a block 3, and a turnover costs you 1. Season averages are the default input for lineup decisions and they are badly behaved. They wash out form, injury recovery, and role changes, which is exactly the signal you want when deciding who to start.

Where the data came from

Three sources, all pulled in Python. We scraped player profiles from Basketball Reference with Selenium and BeautifulSoup for career statistics, used the NBA API to pull every active player and team for the season, and scraped the weekly NBA schedule into a SQL table so the training set stayed current instead of freezing at the point the project started. We then filtered to players with three or more seasons of history, which left 290 with enough data to model.

Six models instead of one

We trained one Long Short-Term Memory model per statistical category rather than a single combined one, because the categories do not behave alike. Points and rebounds are by far the most volatile, and their loss curves sit an order of magnitude above steals and blocks, which barely move at all. A single model would have been tuned to the average of those behaviors and fitted none of them well. Six models let each one learn the volatility of its own category. They cost more to train and they were much easier to debug when one of them was wrong.

The tool

A user enters their roster on the front end. The tool runs each player through all six models, weights the predicted statistics by the fantasy scoring system, and returns the ten highest scoring lineup combinations for the season.