import cvxpy as cp
def problem(predictions, covariance, costs, positions, budget, risk_aversion):
= cp.Variable(scores.size, "picks", boolean=True)
picks = cp.Variable(7, "formation", boolean=True)
formation = cp.Maximize(
objective @ picks - risk_aversion * cp.quad_form(picks, covariance)
predictions.T
)= [
constraints @ picks <= budget,
prices.T sum(formation) == 1,
cp.@ picks == formations.T @ formation,
positions.T
]= cp.Problem(objective, constraints)
problem return problem
Picking a fantasy football team
This post is a work in progress.
Cartola is a fantasy football league following the Brazilian Série A, where players assume the role of team managers. For the past couple of seasons, I’ve been collecting historical data to attempt to answer the question: what’s the optimal run in a given season?
The problem
Before each round , managers are presented with candidate players. Candidates have costs and positions . For convenience, positions can be encoded as dummies . There are valid formations , where indicates exactly how many players of position are allowed in formation . All formations include players and coach, or for all . The manager begins each round with a budget and they must pick a team following a formation . At the end of the round, players receive scores according to their in-game performance. The manager’s goal is to maximize the team score .
Since the manager doesn’t know the scores when picking their team, they must estimate score predictions . However, predictions aren’t always accurate. Also, scores of players from the same team are correlated. To minimize the risk of picking many players from a single team and having that team perform badly, the manager might want to include the covariance between players in the problem. One way to do this is to set a risk aversion and maximize
Finally, the team is subject to the constraints:
- Cost less or equal to the budget
- Follow a single formation
- Follow a valid formation .
This problem is similar to the problem of Modern Portfolio Theory (Markowitz 1952).
Backtesting
So far, I’ve simplified the manager’s goal to maximize for each round. The manager’s true final goal is to maximize their total score at the end of the season . These two objectives aren’t necessarily the same, because players increase or decrease in valuation according to scores. Since depends on the budget , which depends on the scores , one could argue that it might be a good idea to maximize a balance between scoring and valuation. In the next section, I’ll show that maximizing the score for each round is sufficient for maximizing the total score, given good enough predictions.
For now, I’ll define a function to simulate the manager’s performance across an entire season. At the start of the season, the manager has a budget of . Then, for each round :
- Solve the team picking problem
- Calculate the round score
- If , update the budget
import numpy as np
def backtest(
initial_budget,
scores,
predictions,
covariance,
costs,
appreciations,
positions,
risk_aversion,
):= initial_budget
budget = len(predictions)
rounds = np.empty(rounds)
run for t in range(rounds):
= problem(
prob
predictions[t], covariance[t], costs[t], positions[t], budget, risk_aversion
)
prob.solve()= problem.var_dict["picks"].value
picks = scores[t].T @ picks[t]
run[t] if t < 38:
+= appreciations[t].T @ picks
budget return scores
Scenarios
- Perfect predictions
- Perfect predictions and infinite budget
- Simple predictions and varying levels of risk aversion 1
- Random predictions
1 Explain that this is player-level…
2 Unfortunately, data for the 38th round is missing…
I’l plot… 2
Other ideas
Consider valuation, improve predictions, team leader…
Readings:
- https://peterellisjones.com/posts/fantasy-machine-learning/
- https://www.alexmolas.com/2024/07/15/fantasy-knapsack.html
Citation
@online{assunção2023,
author = {Assunção, Luís},
title = {Picking a Fantasy Football Team},
date = {2023-09-21},
url = {https://assuncaolfi.github.io/site/blog/fantasy-football/},
langid = {en}
}