In 2021 I joined an NBA fantasy league with some friends. For those unfamiliar with the concept, fantasy basketball involves putting together a team of real NBA players that score you fantasy points based on their statistics in NBA games. Our league is an ESPN total season points league with custom scoring and a yearly redraft (for the fantasy nerds out there).
For better or worse (mainly worse), fantasy basketball has a way of getting in my head and making me think about it all the time. Who are the best draft picks? What would make a good trade? Should I drop this injured player? Should I sub off the day-to-day injured player or keep him in case he heals up? Sadly, using the app to find these answers is remarkably opaque. Similarly, I found the online tools freely available to be cumbersome and not tailored to our league’s format and scoring. So, armed with a brain that can’t switch off and a desire to learn about Python and APIs, I set out to make some tools to help me beat my mates.
statSummary
In our league, there is a games limit on players. If you try to play more games than the limit, the app will prevent you – this really sucks if this means you can’t play your best player at the end of the year because you filled up the slots with worse players earlier. And if you play less games than the limit, then you’re leaving free points on the table. The problem with this is that it is annoying to check whether you are on the right pace to walk the narrow line of games played.
Another issue with the app interface is that only the total points leaderboard is displayed, meaning that if someone spams games early in the season they can be in the lead even though they may not have the best team. Because of this, the average points per game of a team is a better indicator to see who is going to win in the end.
Finally, the only way to easily see the averages of the players on your team in the app is to individually select them, which makes it difficult to easily compare the performance of your team to decide who should be rostered. And if you want to compare projected stats or past stats, the amount of swiping required is even worse.
To help fix these issues, I wrote a python script that uses the handy espn-api library to pull our league’s data, calculate the values of interest, and spit out a table of data for me to reference whenever I’m at my computer. This was my first real experience with APIs, although being able to use a library does help flatten that learning curve.
Future work here might include integrating this into an app so I can access this on my phone, or expanding the report further to add other parameters of interest.
autoCoach
Probably the most time-intensive and intrusive feature of NBA fantasy is checking your roster every day to make sure injured players are substituted off and healthy players are substituted on. This is really important to doing well in the league since if a player is injured they use up one of your limited roster spots and contribute 0 points. Similarly, if one of your best players recovers from injury without you moving back onto your live roster, they could put up a good score that doesn’t add to your fantasy total.
In the 2022-23 season I had two perpetually injured players in LeBron James and Anthony Davis, and this had me checking my phone multiple times at work (US games usually end up right around lunchtime in NZ) to make sure they got put in the right roster spot. Some of the other mates in my team have even resorted to setting alarms to remind them to check player statuses. So one day I got fed up and decided to try and automate it.
I once again used the lovely espn-api library to pull my league’s data to determine if any of the players on the active roster (i.e. not on bench or injured reserve) have the status “OUT”. However, since player statuses are often not updated until less than two hours before a game, only players that were playing within an hour of when the script was run get included on the injured list. Once an injured list is generated, a command is sent to the league API to move the players on the injured list to the bench. This was the first time I had interacted with a website using html commands sent from a script, so it was very exciting to see the players move in the app like magic once the script was working.
With the script sorted, I wanted to figure out a way of getting it to run during the day while I was working with my personal computer turned off. The solution I settled on was to deploy a docker file on Google Cloud Platform (GCP) and set a scheduler to run the script every hour. This was the first time I had earnestly tried to use a cloud computing resource, and it was interesting to try and navigate the documentation required to implement my vision.
I ended up winning the fantasy league the year that I implemented this project, and since we didn’t have any rules against automation that win counts fair and square. However, I’m not allowed to use that script anymore, something about “unfair advantage” and “not in the spirit of the game”. I don’t mind too much, especially since my free GCP trial has run out.
pointsGetter
One thing I find particularly brain-bending in fantasy is evaluating trade proposals. In order to better get a grasp of the value of a player, I developed a simple model of value based on a player’s average, the how many games they played, and the value of a replacement player. There’s also a correction based on the projected estimates that gets applied to correct for any early-season variation, as well as an option to specify how long you expect an injury to continue if a player is currently injured.
This script is still a bit janky to use as it requires manually inputting the projected averages and totals and storing each player’s data as an individual variable. Also, it was initially set up to scrape data from Basketball Reference using PandasBasketball, but during script testing I accidentally made too many automated requests and Basketball Reference has since blocked my script from accessing the site.
This script hasn’t been as useful as the other ones because it needs a little bit more work. Improvements include being able to specify players easier, compare two sides of a trade more clearly, and additional code that automatically finds and suggests trades.