Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
dimakudosh authored and dimakudosh committed Sep 26, 2021
1 parent 5cad6f1 commit e9b46f6
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 70 deletions.
3 changes: 2 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pydfs-lineup-optimizer
======================

**pydfs-lineup-optimizer** is a tool for creating optimal lineups for daily fantasy sport.
Currently it supports following dfs sites:
Currently it supports the following dfs sites:

+--------+------------+---------+--------------+-------+---------+-------------------------+---------------------+------------------+
| League | DraftKings | FanDuel | FantasyDraft | Yahoo | FanBall | DraftKings Captain Mode | FanDuel Single Game | DraftKings Tiers |
Expand Down Expand Up @@ -51,3 +51,4 @@ Contents
usage
rules
performance-and-optimization
custom-settings
15 changes: 9 additions & 6 deletions docs/performance-and-optimization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Performance and Optimization
Solvers
-------

By default, the optimizer uses `pulp <https://coin-or.github.io/pulp/index.html>`_ library under the hood with a default solver that free but slow.
By default, the optimizer uses `pulp <https://coin-or.github.io/pulp/index.html>`_ library under the hood with a default solver that is free but slow.
You can change it to another solver that pulp supports.
Here is an example of how to change the default solver for GLPK solver:

Expand Down Expand Up @@ -37,16 +37,19 @@ Decrease solving complexity
---------------------------

Sometimes optimization process takes a lot of time to generate a single lineup.
It usually happens in mlb and nfl because all teams play on the same day and each team has a lot of players and a total
It usually happens in mlb and nfl because all teams play on the same day and each team has a lot of players and the total
number of players used in optimization is >100. In this case, a good approach is to remove from optimization players with
small fppg value and big salary.
small fppg value and a big salary.

.. code-block:: python
optimizer = get_optimizer(Site.DRAFTKINGS, Sport.BASEBALL)
optimizer.load_players_from_csv('dk_mlb.csv')
for player in optimizer.players:
if player.efficiency < 1: # efficiency = fppg / salary
optimizer.remove_player(player)
optimizer.player_pool.add_filters(
PlayerFilter(from_value=5), # use only players with points >= 5
PlayerFilter(from_value=2, filter_by='efficiency'), # and efficiency(points/salary) >= 2
PlayerFilter(from_value=2000, filter_by='salary'), # and salary >= 3000
)
optimizer.player_pool.exclude_teams(['Seattle Mariners'])
for lineup in optimizer.optimize(100):
print(lineup)
10 changes: 5 additions & 5 deletions docs/rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ and `max_projected_ownership` that are max/min percent of average ownership in g
.. code-block:: python
for player in optimizer.players:
player.projected_ownership = get_projected_ownership(player) # User defined function for getting ownership percent
player.projected_ownership = 0.1
optimizer.set_projected_ownership(max_projected_ownership=0.6)
If you don't specify `projected_ownership` for some players this players will not used in calculating lineup average
Expand Down Expand Up @@ -225,8 +225,8 @@ create lineups with Rodgers/Adams or Brees/Thomas duos with 0.5 exposure:

.. code-block:: python
rodgers_adams_group = PlayersGroup([optimizer.get_player_by_name(name) for name in ('Aaron Rodgers', 'Davante Adams')], max_exposure=0.5)
brees_thomas_group = PlayersGroup([optimizer.get_player_by_name(name) for name in ('Drew Brees', 'Michael Thomas')], max_exposure=0.5)
rodgers_adams_group = PlayersGroup(optimizer.player_pool.get_players('Aaron Rodgers', 'Davante Adams'), max_exposure=0.5)
brees_thomas_group = PlayersGroup(optimizer.player_pool.get_players('Drew Brees', 'Michael Thomas'), max_exposure=0.5)
optimizer.add_stack(Stack([rodgers_adams_group, brees_thomas_group]))
Group players
Expand All @@ -236,14 +236,14 @@ Here is an example:

.. code-block:: python
group = PlayersGroup([optimizer.get_player_by_name(name) for name in ('LeBron James', 'Anthony Davis')])
group = PlayersGroup(optimizer.player_pool.get_players('LeBron James', 'Anthony Davis'))
optimizer.add_players_group(group)
You can use this method for ungrouping players as well. In this example maximum of one player will be in the lineup.

.. code-block:: python
group = PlayersGroup([optimizer.get_player_by_name(name) for name in ('LeBron James', 'Anthony Davis')], max_from_group=1)
group = PlayersGroup(optimizer.player_pool.get_players('LeBron James', 'Anthony Davis'), max_from_group=1)
optimizer.add_players_group(group)
Also you can apply these groups conditionally based on another player selection.
Expand Down
123 changes: 71 additions & 52 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Usage
Base Usage
----------
Creating optimal lineups with **pydfs-lineup-optimizer** is very simple.
Firstly you should create optimizer. You can do this using
shortcut get_optimizer. You must provide daily fantasy site for it and kind of sport.
If site doesn't support specified sport you will get NotImplementedError.
Firstly you should create an optimizer. You can do this using
shortcut get_optimizer. You must provide a daily fantasy site for it and the kind of sport.
If the site doesn't support the specified sport you will get NotImplementedError.

.. code-block:: python
Expand All @@ -18,26 +18,26 @@ If site doesn't support specified sport you will get NotImplementedError.
optimizer = get_optimizer(Site.FANDUEL, Sport.BASKETBALL)
After that you need to load players into your optimizer. You have 2 options:
First is to load players from CSV file like this:
After that, you need to load players into your optimizer. You have 2 options:
The first is to load players from CSV file like this:

.. code-block:: python
optimizer.load_players_from_csv("path_to_csv")
.. note::

CSV file must have the same format as export file in specified dfs site, if you have custom CSV file this method will not work.
Also this method raises `NotImplementedError` for FanBall site because it hasn't export csv feature.
CSV file must have the same format as an export file in a specified dfs site, if you have a custom CSV file this method will not work.
Also, this method raises `NotImplementedError` for FanBall site because it hasn't export csv feature.

Or you can load players using load_players method and pass list with players.

.. code-block:: python
from pydfs_lineup_optimizer import Player
optimizer.load_players(players) # players is list of Player objects
optimizer.player_pool.load_players(players) # players is list of Player objects
After player loading you can create your optimal lineups with following code:
After player loading you can create your optimal lineups with the following code:

.. code-block:: python
Expand All @@ -64,31 +64,52 @@ Below is a full example of how **pydfs-lineup-optimizer** can be used to generat
print(lineup.fantasy_points_projection)
print(lineup.salary_costs)
Advanced Usage
--------------
Player Pool
-----------

For generating optimal lineups you may need to lock some players that you want to see in your lineup.
You can do this using following code:
After importing players to the optimizer you may need to change parameters for some players.
You can retrieve player using following methods:

.. code-block:: python
player = optimizer.get_player_by_name('Rodney Hood') # find player with specified name in your optimizer
second_player = optimizer.get_player_by_id('ID00001') # find player with player id
optimizer.add_player_to_lineup(player) # lock this player in lineup
optimizer.add_player_to_lineup(second_player)
pool = optimizer.player_pool
player = pool.get_player_by_name('Tom Brady') # using player name
player = pool.get_player_by_id('00000001') # using player id
player = pool.get_player_by_name('Tom Brady', 'CPT') # using player name and position
Locked players can be unlocked as well:
For player grouping, you may need to get several players at the same time, for this you can use `get_players` method:

.. code-block:: python
optimizer.remove_player_from_lineup(player)
from pydfs_lineup_optimizer import PlayerFilter
pool = optimizer.player_pool
players = pool.get_players('Tom Brady', 'Rob Gronkowski', 'Chris Godwin') # get players by name
players = pool.get_players('Tom Brady', '00001', pool.get_player_by_name('Rob Gronkowski', 'CPT')) # get players using name, id and player object
players = pool.get_players(PlayerFilter(positions=['QB'])) # get all QB
players = pool.get_players(PlayerFilter(teams=['Tampa Bay'])) # get all players from team Tampa Bay
players = pool.get_players(PlayerFilter(from_value=10, filter_by='fppg')) # get all players with points >= 10
players = pool.get_players(PlayerFilter(teams=['Tampa Bay'], positions=['WR'], from_value=10)) # combined
Also you can exclude some players from optimization process and restore players as well:
For generating optimal lineups you may need to lock some players that you want to see in your lineup.
You can do this using the following code:

.. code-block:: python
optimizer.remove_player(player)
optimizer.restore_player(player)
optimizer.player_pool.lock_player('Tom Brady') # using player name
optimizer.player_pool.lock_player('ID00001') # using player id
tom_brady_captain = optimizer.player_pool.get_player_by_name('Tom Brady', position='CPT')
optimizer.player_pool.lock_player(tom_brady_captain) # using player
# Locked players can be unlocked as well
optimizer.player_pool.unlock_player('Tom Brady')
Also you can exclude some players and teams from optimization process:

.. code-block:: python
optimizer.player_pool.remove_player('Tom Brady')
optimizer.player_pool.restore_player('Tom Brady')
optimizer.player_pool.exclude_teams(['Jets'])
You can specify maximum and minimum exposures for some players or max exposure for all players, you have several ways how to do this.
You can add "Max Exposure" and "Min Exposure" columns with exposure percentage for some players to csv that will be parsed while players loading.
Expand All @@ -97,21 +118,20 @@ pass max_exposure parameter to optimize method

.. code-block:: python
player = optimizer.players[0] # get random player from optimizer players
player = optimizer.player_pool.get_player_by_name('Tom Brady')
player.max_exposure = 0.5 # set 50% max exposure
player.min_exposure = 0.3 # set 30% min exposure
lineups = optimizer.optimize(n=10, max_exposure=0.3) # set 30% exposure for all players
.. note::

Exposure working with locked players, so if you lock some player and set max exposure to 50% percentage
this player will appears only in 50% lineups.
this player will appear only in 50% of lineups.
Player max exposure has higher priority than max_exposure passed in optimize method.
Exposure percentage rounds to ceil.

By default, the optimizer generates lineups based on the total number of lineups. It means if you have a player with a
huge projection it will be selected only in first n lineups.
huge projection it will be selected only in the first n lineups.
You can change this behavior to another algorithm where exposure calculates
after each generated lineup. For example, if you have a player with a huge projection and
set his max_exposure to 0.5 optimizer will select him in the first lineup then skip 2 lineups with this player
Expand All @@ -133,32 +153,32 @@ After optimization you can print to console list with statistic about players us
Example of advanced usage
-------------------------

Below is an full example of how **pydfs-lineup-optimizer** can be used to generate optimal lineups with user constraints.
Below is a full example of how **pydfs-lineup-optimizer** can be used to generate optimal lineups with user constraints.

.. code-block:: python
optimizer = get_optimizer(Site.YAHOO, Sport.BASKETBALL)
optimizer.load_players_from_csv("yahoo-NBA.csv")
nets_centers = filter(lambda p: p.team == 'Nets' and 'C' in p.positions, optimizer.players)
for player in nets_centers:
optimizer.remove_player(player) # Remove all Nets centers from optimizer
harden = optimizer.get_player_by_name('Harden')
westbrook = optimizer.get_player_by_name('Westbrook') # Get Harden and Westbrook
pool = optimizer.player_pool
for player in pool.get_players(PlayerFilter(positions=['C'], teams=['Nets'])):
pool.remove_player(player) # Remove all Nets centers from optimizer
harden = pool.get_player_by_name('Harden')
westbrook = pool.get_player_by_name('Westbrook') # Get Harden and Westbrook
harden.max_exposure = 0.6
westbrook.max_exposure = 0.4 # Set exposures for Harden and Westbrook
optimizer.add_player_to_lineup(harden)
optimizer.add_player_to_lineup(westbrook) # Lock Harden and Westbrook
optimizer.lock_player(harden)
optimizer.lock_player(westbrook) # Lock Harden and Westbrook
for lineup in optimizer.optimize(n=10, max_exposure=0.3):
print(lineup)
Late-Swap
--------------------

Optimizer provides additional functionality that allows to re-optimize existed lineups.
Currently this feature implemented for DK and FanDuel.
For this you should load lineups, you can do it from csv file generated for specific contest.
Currently, this feature is implemented for DK and FanDuel.
For this you should load lineups, you can do it from csv file generated for a specific contest.
Then you should pass loaded lineups to `optimize_lineups` method.
Players with started game will be locked on specific positions and optimizer will change only players with upcoming game.
Players with the started game will be locked on specific positions and the optimizer will change only players with the upcoming game.

.. code-block:: python
Expand All @@ -169,7 +189,7 @@ Players with started game will be locked on specific positions and optimizer wil
for lineup in optimizer.optimize_lineups(lineups):
print(lineup)
Because FanDuel doesn't provide information about locked player and games start time you should manually add information about started games like in example below:
Because FanDuel doesn't provide information about locked player and games start time you should manually add information about started games like in the example below:

.. code-block:: python
Expand All @@ -196,7 +216,7 @@ You can change it using `set_timezone` function:
Export lineups
==============

You can export lineups into a csv file. For this you should call export method of the optimizer after you generate all lineups.
You can export lineups into a csv file. For this, you should call the export method of the optimizer after you generate all lineups.

.. code-block:: python
Expand Down Expand Up @@ -226,22 +246,22 @@ There are several strategies already implemented in this package:

RandomFantasyPointsStrategy adds some deviation for players projection for creating less optimized but more randomized lineups.
You can set this deviation when creating strategy by default min deviation is 0 and max deviation is 12%.
You also can specify player specific deviation using `min_deviation` and `max_deviation` attributes for player,
You also can specify player-specific deviation using `min_deviation` and `max_deviation` attributes for a player
or using additional columns `Min Deviation` and `Max Deviation` in import csv.
Also you can randomize players fppg by specifying projection range using `fppg_floor` and `fppg_ceil` attributes for player or
`Projection Floor` and `Projection Ceil` csv columns. In this case this method has priority over deviation.
Also, you can randomize players fppg by specifying projection range using `fppg_floor` and `fppg_ceil` attributes for player or
`Projection Floor` and `Projection Ceil` csv columns. In this case, this method has priority over deviation.
It works only if both fields are specified.

.. code-block:: python
optimizer.set_fantasy_points_strategy(RandomFantasyPointsStrategy(max_deviation=0.2)) # set random strategy with custom max_deviation
harden = optimizer.get_player_by_name('Harden')
harden = optimizer.player_pool.get_player_by_name('Harden')
harden.min_deviation = 0.3
harden.max_deviation = 0.6 # Set different deviation for player
westbrook = optimizer.get_player_by_name('Westbrook')
westbrook = optimizer.player_pool.get_player_by_name('Westbrook')
westbrook.min_deviation = 0 # Disable randomness for this player
westbrook.max_deviation = 0
doncic = optimizer.get_player_by_name('Doncic')
doncic = optimizer.player_pool.get_player_by_name('Doncic')
doncic.fppg_floor = 60 # Randomize using projection range
doncic.fppg_ceil = 90
lineups = optimizer.optimize(n=10)
Expand All @@ -250,22 +270,21 @@ It works only if both fields are specified.

With RandomFantasyPointsStrategy optimizer generate lineups without ordering by max points projection.

ProgressiveFantasyPointsStrategy is another method to randomize optimizer result.
ProgressiveFantasyPointsStrategy is another method to randomize optimizer results.
It increases fantasy points for each player that wasn't used in the previous lineup by some specified percent of original fantasy points.
It works cumulatively so fantasy points will be greater if player didn't used in lineup multiple times.
After player will be selected to lineup his points will be reset to the original value.
You can change this value for specific player by setting `progressive_scale` property of Player or by adding `Progressive Scale` column to import csv.

It works cumulatively so fantasy points will be greater if the player wasn't used in the lineup multiple times.
After the player will be selected to lineup his points will be reset to the original value.
You can change this value for a specific player by setting `progressive_scale` property of Player or by adding `Progressive Scale` column to import csv.
.. code-block:: python
optimizer.set_fantasy_points_strategy(ProgressiveFantasyPointsStrategy(0.01)) # Set progressive strategy that increase player points by 1%
optimizer.get_player_by_name('Stephen Curry').progressive_scale = 0.02 # For curry points will be increased by 2%
optimizer.player_pool.get_player_by_name('Stephen Curry').progressive_scale = 0.02 # For curry points will be increased by 2%
Exclude lineups
===============

You can run the optimizer several times. In this case, you probably want to avoid duplicated lineups in the result.
You can provide a list of excluded lineups to optimize method.
You can provide a list of excluded lineups to the optimize method.
All of these lineups will be excluded from optimization and newly generated lineups will count them in max repeating players rule.

.. code-block:: python
Expand Down
7 changes: 1 addition & 6 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
from pydfs_lineup_optimizer import Site, Sport, get_optimizer, ProjectionFilter
from pydfs_lineup_optimizer import Site, Sport, get_optimizer


optimizer = get_optimizer(Site.YAHOO, Sport.BASKETBALL)
optimizer.load_players_from_csv("yahoo-NBA.csv")
player_pool = optimizer.player_pool
player_pool.add_filters(
ProjectionFilter(from_projection=20, position='QB'),
ProjectionFilter(from_projection=10, position='TE'),
)
lineup_generator = optimizer.optimize(10)
for lineup in lineup_generator:
print(lineup)
Expand Down

0 comments on commit e9b46f6

Please sign in to comment.