"Solving" Daily Fantasy Football
45 more days until the NFL 2021 season kicks off.
More importantly, that's 45 more days until fantasy football starts.
I personally find sports more enjoyable to watch when there's "something" riding on the game. It could be the enjoyment of watching my favourite teams, or the cultural pride associated with watching international sports. But sometimes it's just fun to play fantasy sports and put a bit of money on the line.
I don't specifically recall when I gave daily fantasy football a try, but I remember it coincided with a week where the Packers were not playing on the Sunday, and so I filled out a daily fantasy sports (DFS) roster in order to give me a reason to watch football all day on the Sunday (that's a good enough reason .. right?).
For those unfamiliar with DFS, you have to assemble a roster with the goal of scoring the highest amount of points on a given day, and these points are granted based on your player's IRL performance during their games. The catch is that each player is given a salary value, and you cannot exceed some maximum salary constraint set by the platform. For example, Yahoo Fantasy Football gives you $200 to select 9 players of the following positions: 1 QB, 2 WR, 2RB, 1 TE, 1 Flex (WR, RB, or TE), and 1 defense. Salary-wise, you're looking at around $30-$40 for your top-tier players, all the way down to ~$10 for your run-of-the-mill guys. Sounds easy enough, you just need to pick the good players, right?
When you take a deeper look at how DFS works, it can really be broken down into two parts: (1) predicting individual player points, and (2) finding the optimal line-up to maximize the points with respect to the salary constraint.
1. Predicting points
The meat and potatoes of this operation; it doesn't matter how good your solver is if your projections aren't accurate. There is a lot to consider when figuring out which players to choose for your weekly lineup. Are they on a winning team? How did they perform last week? What is the weather going to be like on game day? Are they named Patrick Mahomes? These are the questions that need to be answered and then assigned a numeric value that represents how much more effective that player will be compared to the others. For example, would you rather have Aaron Rodgers as your QB if he's against Aaron Donald and the Rams defense, or should you save your salary and take a budget friendly QB like Justin Herbert with a more expensive reciever and/or running back instead? For my own use case, I figured I would defer to the people who do these predictions as their full-time jobs, and use the projected scores provided by FantasyPros and Yahoo. I had intended on comparing the two proivders and using them as inspiration for developing my own machine learning model, but as I'll touch upon later, the FantasyPros data worked extremely well out of the box. Once we have an idea of how the players might perform on game day, it's time to figure out how to assemble the best possible roster.
2. Maximizing points
This sounds like a job for linear programming. As described by Encyclopaedia Britannica, linear programming is a "mathematical modeling technique in which a linear function is maximized or minimized when subjected to various constraints" [0]. In our case, we have various constraints about our roster size, position requirements, and salary, and we're looking to maximize the amount of points for our roster. While researching libraries and techniques I could use within Python for linear programming, I stumbled across a blog post by Kyle Stahl entitled "Draft Kings Player Optimization" [1]. In his post he outlines, explains, and shows the code that he used for solving daily fantasy football, in his case, he used Draft Kings data as an input and PuLP for linear programming. There's no sense in re-inventing the wheel, and his code was well explained and written; it is now the core code that I ended up using for my own DFS tool.
Prior to the 2020 NFL season, I pieced together a couple of Python scripts (including the linear programming solver mentioned above) to automate the process of selecting a daily fantasy football roster. Originally I was planning on running a series of trials to see who had the more accurate projections between Yahoo and Fantasy Pros (spoiler alert: Yahoo's projections were hot garbage). The scripts I used can be found on my GitHub at https://github.com/aptmac/dfs-helper [2], and currently requires two commands in-order to work.
python fetch_data.py
Simple enough, this script just scrapes the projected scores from the Fantasy Pros website and Yahoo DFS API, and matches them up with the Yahoo player salary constraints. The output here is a csv and json file comprised of the players and their associated salary cost.
python dfs-solver.py csv/fantasypros.csv
Next, we need to feed the csv file into our solver, in the case shown above it's the Fantasy Pros csv data. The solver works it's magic and we're presented output that looks like:
name position team salary points
4 Patrick Mahomes QB KC 36.0 XX.X
32 Keenan Allen WR LAC 26.0 XX.X
34 James Conner RB PIT 26.0 XX.X
46 Darren Waller TE LV 23.0 XX.X
53 Terry McLaurin WR WAS 22.0 XX.X
56 Todd Gurley II RB ATL 21.0 XX.X
110 Robby Anderson WR CAR 19.0 XX.X
140 David Montgomery RB CHI 16.0 XX.X
219 Jacksonville Jaguars DEF JAX 11.0 X.X
Total used amount of salary cap: 200.0
Projected points: XXX.X
The last step now is to plug this lineup into the Yahoo Fantasy Football app (or website), and get ready to watch some football.
Now that we have set our generated roster, how effective is this tool overall? The answer here depends on how you play DFS. From my experience with Yahoo daily fantasy, there are two main contest types: 1v1 matchmaking and multi-player contests. The first option is my go-to and where I was arguably most successful. As the name suggests, Yahoo matches you up against one other player, and your two rosters compete against each other to see who wins. dfs-helper managed to get a season-long win percentage of around ~70% (which is fantastic, considering this IS gambling after all), which gave my account a diamond ranking for the season, indicating that I was at the 95th percentile. There is another method of contest, where you compete against many other players, who often have the ability to submit multiple entries and rosters. This is where the big money is, and where the heavy hitters come to play. Often times I would see individuals submit 5+ entries, each with slight modifications to their roster in hopes of striking that golden lineup. I played a handful of these, and while I did manage to place high enough for payout a few times, I didn't enjoy playing as much as when I was matched against one other person. Having said that, there was a free entry contest run by BetMGM which had 142,492 entrants, and my lineup placed 972th, which actually one me a whole $1, which was really cool.
So, what's next for dfs-helper? I have a couple of shorter term features I'd like to implement, and some ideas that I'd like to experiment with in future seasons. For shorter term goals, I'm hoping to get the solver to present multiple roster combinations instead of just the "optimal" solution. As for implementing this, I'm thinking that the easiest way would be to iterate as many times as rosters I want to generate, and after each "optimal" roster is generated I would add it as a constraint and re-solve. I have yet to give this a try, but it's something I'm thinking about. Next, I would like to create a simple front-end for interacting with dfs-helper. Last year I started working on an app called "FFDojo", which will be a compilation of tools that I use throughout the fantasy football season. This includes features such as my drafting tool, trade analyzer, Reddit news scraper, and tier lists. I haven't really touched the FFDojo code in a while, but with fantasy football season quickly approaching I think that will change soon. As for long term features, I'd really like to start working on my own data model and machine learning pipeline for creating my own projections. I'm not sure how this will work yet, and if I'll be using something like nflscrapR to analyze raw player data, or if I'll take the Boris Chen approach and instead analyze the accuracy of sports writers and their projections (or possibly some combination of the two). Regardless, there are plenty of ways to enhance dfs-helper, and I'm looking forward to experimenting in the future (time permitting of course).
Lastly, I'd just like to mention that playing fantasy sports for money is gambling, and that winning is never guaranteed. I work on these fantasy tools because I think they're fun, not because I think I can make money off of them.
[0] https://www.britannica.com/science/linear-programming-mathematics