How to Win Your Fantasy Basketball League using Python

Sam Bruchhaus
5 min readNov 13, 2021
Photo by Third Serving on Unsplash

In a fantasy basketball league with weekly head-to-head points scoring, daily lineup locks, and a games played limit, the primary decision that must be made by managers is how to allocate players to games. The problem is similar to an optimization problem with binary decision variables where the only constraint is the number of decision variables that can be chosen. The coefficients for the decision variables can be projections for the players’ performance in each game. By sorting the daily projections for the week and taking the top scores for the number of games left, the optimization problem can be solved without a heavyweight optimization engine.

LineupExperts is a free service dedicated to fantasy projection models. It provides tools to optimize lineups, analyze matchups, and find free agents. Best of all, LineupExperts allows those who register to import their league settings, which leads to customized projections. This script uses LineupExperts’ projections as the coefficients to the optimization problem.

Below is a tutorial to build a tool to optimize daily lineups. To use the tool to optimize a fantasy basketball score, copy and paste the script into a Python-based file (Jupyter Notebook is recommended). The first table created shows all players that should be played during the week in order of score. The second table shows which players should be played today.

Download Necessary Packages

The user needs the following packages:

All packages are available via The Python Package Index or Anaconda.

from datetime import date, timedelta
import pandas as pd
import time
from selenium import webdriver
from bs4 import BeautifulSoup
from dateutil import parser
from IPython.core.display import display

A few inputs must be provided by the user:

  • The PATH to your ChromeDriver,
  • The Email and Password associated with a LineupExperts account,
  • The name of the league as shown on LineupExperts’ Your Leagues page (must be signed in to view),
  • The number of games remaining in the matchup. For ESPN leagues, the number of games remaining can be found at the bottom of the matchup tab on the mobile app.
# file path for chromedriver
path_to_chromedriver = 'ENTER PATH TO CHROMEDRIVER'
# define email and password for lineupexperts.com
email = r'ENTER EMAIL'
password = r'ENTER PASSWORD'
# define the name of your league
league_name = 'ENTER LEAGUE NAME'
# define the amount of games left in the week
games_remaining = 'ENTER GAMES REMAINING'

Retrieve Days Left in the Week

Lineup Experts’ lineup optimizer provides projections for each player’s scores for the total week, next week, and each individual day. Because a daily league locks each day, the data obtained is for each individual day left in the week.

# check if it is Sunday
if date.today().strftime('%A') == 'Sunday':
date_list=['Today']

else:

# initialize list with today and tomorrow
date_list = ['Today', 'Tomorrow']

# initialize iterator as 2, meaning the day after tomorrow
i = 2

# loop until getting to the end of the match-up (sunday)
while 'Sunday' not in date_list:

# if saturday, break
if date.today().strftime('%A') == 'Saturday':
break

# add day to list
date_list.append((date.today() +
timedelta(i)).strftime('%A'))

# increase iterator
i+=1

Use Selenium to Navigate to the Lineup Optimizer

Selenium allows for the navigation of the login, leagues, and tools pages.

# set Selenium Options
options = webdriver.ChromeOptions()
options.headless = True
driver=webdriver.Chrome(options=options,
executable_path=path_to_chromedriver)
# login
driver.get('https://www.lineupexperts.com/Log-In')
email_field = driver.find_element_by_id('Email')
password_field = driver.find_element_by_id('Password')
email_field.send_keys(email)
password_field.send_keys(password)
driver.find_element_by_id('LUEFormSubmitButton').click()
# go to your leagues page
driver.find_element_by_link_text('View Your Leagues').click()
# go to league page
time.sleep(5)
driver.find_element_by_xpath('.//span/a/b[normalize-space()="' +
league_name + '"]').click()
# go to lineup optimizer
time.sleep(5)
driver.find_element_by_xpath('//button[normalize-space()="Optimize
Your Lineup"]').click()
link_list= driver.current_url.split('=')

Retrieve and Clean Projections for the Rest of the Week

The following code scrapes the data from the HTML for each day, then cleans the data and saves it into a dataframe.

# define empty dataframe for concatenation
proj_dataframe = pd.DataFrame()
# build dataframe of projections from each days projections
for day in date_list:
# define url
url = link_list[0] + '=' + link_list[1] + '=' + link_list[2] +
'=' + link_list[3] + '=' + day
# navigate to the Lineup Optimizer and Scrape the Data
response = driver.get(url)
# turn into Soup object
soup = BeautifulSoup(driver.page_source, 'html.parser')
# select proper table
if len(soup.find_all('table')) == 0:
continue
else:
table = soup.find_all('table')[1]
# find rows
rows = table.find_all('tr')
# pull out data points
row_list = []
for tr in rows:
td = tr.find_all('td')
row = [tr.text.strip() for tr in td if tr.text.strip()]
if row:
row_list.append(row)
# read into DataFrame and select relevant columns
dataframe = pd.DataFrame(row_list)[[0, 1, 3, 4]]
# rename columns
dataframe.columns = ['Position', 'Name', 'GameInfo',
'Projection']
# remove non-player rows
dataframe = dataframe[dataframe.Position.isin(['G', 'PG', 'SG',
'SG/SF', 'SF',
'F', 'PF',
'PF/C','C',
'Utility',
'Bench'])]
# change Projection Column to a Numeric Datatype
dataframe['Projection'] = pd.to_numeric(dataframe.Projection)
# filter out guys not playing
dataframe = dataframe[dataframe.GameInfo != '-']
# fix name
dataframe['Name'] = dataframe['Name'].str.split('\n').str[0]
# split GameInfo into Proper Columns and Drop
temp_game_info_dataframe = dataframe.GameInfo.str.split(' ',
expand=True)
dataframe['Date'] = pd.to_datetime((temp_game_info_dataframe[0]+
temp_game_info_dataframe[1])
.apply(parser.parse))
dataframe['Opponent'] = temp_game_info_dataframe[3]
dataframe = dataframe.drop(columns='GameInfo')
# append to projections proj_dataframe
proj_dataframe = proj_dataframe.append(dataframe)
.reset_index(drop=True)
# close driver
driver.quit()

Retrieve the Players that Should Be Active the Rest of the Week

The resulting table shows the top players for the number of games left. This table can be used to plan out future days’ lineups. Note that when run late at night, these values could be wrong due to time differences in LineupExperts’ servers.

# sort the dataframe by projections
top_proj_dataframe = proj_dataframe.sort_values(by=['Projection'],
ascending=False)
.reset_index(drop=True)
# retrieve the highest projected scores for the rest of the week
top_proj_dataframe = top_proj_dataframe.iloc[:games_remaining]
# present the players that should be played the rest of the week
top_proj_dataframe

Example Output

Retrieve Players that Should Be Active Today

The resulting table shows the players that should be active today. Note that when run late at night, these values could be wrong due to time differences in LineupExperts’ servers.

# select rows that are today
active_today_dataframe = top_proj_dataframe[top_proj_dataframe["Date"] ==
pd.to_datetime(date.today())]
# present the players that should be active today
if active_today_dataframe.empty:
print("Do not play anyone today!")
else:
display(active_today_dataframe)

Example Output

Closing Notes

The program will allow you to optimize your daily lineups. However, the program does not enforce constraints by position which may exist in some leagues. Source code can be found on Github.

--

--

Sam Bruchhaus

Notre Dame MSBA | Tulane Football + Comp Sci | Analytics | Administration | Scouting | Marketing