NBA 2018 Season- Daily Fantasy
My results and expected long term success in playing daily fantasy during the 2017-2018 NBA Season
With another season of the NBA, I once again tried to compete in daily fantasy competitions using a statistical model. This year, I focused on refining my model and algorithms and on figuring out whether entering these contests is even remotely profitable.
Following the disappointing performance of my daily fantasy NBA model last year, I was determined to at least beat the predictions that Fanduel provides to everyone on their site. To achieve this, I made a few small improvements to my model and process:
-
First, I added additional features to my model. I added categorical variables representing the team the player is on and the team the player is facing.
-
I built and scored my model on a stricter subset of players. I limited the set to players who averaged at least 15 minutes per game and who have played at least 2 games in the past 10 days. This filtered out many players who had inconsistent playing time, who were unlikely to be placed in a lineup anyways.
-
I improved the lineup optimization algorithm by using a multiple integer linear programming (MILP) algorithm. Instead of relying on running numerous simulations to find the best lineup, the MILP algorithm can mathematically determine the optimal lineup. Not only did this result in higher scoring lineups, but the run times also decreased drastically.
With these improvements, I benchmarked my new model’s performance against my best model from last year and the predictions that Fanduel provides. First, I looked at the MSE of fantasy points predictions per player:
Model | MSE | |
Old Model | 116 | |
Fanduel Model | 113 | |
New Model | 102 |
Compared to my older model and the Fanduel model, my new model has a substantially lower MSE. This is especially rewarding since I was previously unable to beat the MSE of the Fanduel model. Next, I looked at the average fantasy points per lineup for each model:
Model | Mean Points per Lineup | |
Old Model | 273 | |
Fanduel Model | 275 | |
New Model | 299 |
Similar to the MSE results above, my new model once again beat my older model and the Fanduel model. Looking at the this data day by day, my new model appears to consistently beat both models every day:
<!DOCTYPE html>
During this NBA season, I also tried to get a better sense of what score is needed to win the competitions. For several days, I entered either the 50/50 or Double Up non-beginner competitions for typically $1 or $2. These two competition styles are very similar, where the top ~50% of entrants about double their money while everyone else loses. Throughout these days, I found that the average lowest winning score was 284, which is a touch above my model’s average. However, when looking at my model’s performance day over day, I found that my model would’ve placed in the top 50% 28/51 times for a win percentage of 55%.
If that 55% win percentage is indeed true over time, I’d expect to achieve a 10% return on money invested in these competitions:
E(Profit on a $x Double Up game) = P(Win) * x - P(Lose) * x
E(Profit on a $x Double Up game) = 0.55 * x - 0.45 * x
E(Profit on a $x Double Up game) = 0.1 * x
Based on this performance and expected return, I should try entering more live competitions next year using this model. The biggest limitation with this analysis is that I only have data on $1-$2 competitions. To make more serious money, I’d need to enter in more expensive competitions. The dilemma is that I don’t have data for these more expensive competitions. It’s very possible that the more expensive the competition, the more competitive it is. Next season, I’ll need to enter those more expensive competitions to get a better sense of the economics. **