DimaKudosh / pydfs-lineup-optimizer

Daily Fantasy Sports lineup optimzer for all popular daily fantasy sports sites
MIT License
416 stars 156 forks source link

DK NBA Lateswap breaks for some entries #248

Open keithotis opened 3 years ago

keithotis commented 3 years ago

Running the following basic code:

from pydfs_lineup_optimizer import get_optimizer, Site, Sport, CSVLineupExporter

optimizer = get_optimizer(Site.DRAFTKINGS, Sport.BASKETBALL)

optimizer.load_players_from_csv('./DKSalaries.csv')

lineups = optimizer.load_lineups_from_csv('./DKEntries.csv')

for lineup in optimizer.optimize_lineups(lineups):

    print(lineup)

generates various SolverInfeasibleSolutionException and GenerateLineupException errors. This has happened in the middle of live contests but I've been testing this on my entries from yesterday's contests as a sanity check. It should just return all of the lineups as all players are locked but crashes on some lineups. Eg.:

    raise SolverInfeasibleSolutionException(invalid_constraints)

SolverInfeasibleSolutionException: ['_C5', '_C17']

    raise GenerateLineupException(solver_exception.get_user_defined_constraints())

GenerateLineupException: Can't generate lineups

The fact that it works on some lineups but not other's is odd. I had thought I had tracked it down to crashing when a lineup had all locked players but that's not the case with this backtest, it makes it through a few entries in some of yesterday's slates before errors. I also thought it might be when my custom projections set someone to zero when I they were in the original lineup but this test is using just the raw exports from DK thus shouldn't have that issue.

sansbacon commented 3 years ago

According to the instructions, you should use the DKEntries file when you call load_players_from_csv and when you call load_lineups_from_csv. If you are making changes to DKSalaries, then it may have inconsistent information with the DKEntries file which is causing the error. It is difficult to know without posting the files that generated the error, as I can't follow the same steps and do a deep dive on the issue.

Also you should post what operating system you are using as well as the time zone of your computer that is running the code.

You can also take a look at what constraints are causing the error. The error message suggests that it is _C5 and _C17. If you inspect the solution file it should help you understand what those constraints are. To do so, you use a custom PuLP solver, as described in the documentation

from pydfs_lineup_optimizer import get_optimizer, Site, Sport
from pydfs_lineup_optimizer.solvers.pulp_solver import PuLPSolver

class CustomPuLPSolver(PuLPSolver):
    LP_SOLVER = PULP_CBC_CMD(keepFiles=True, logPath='pydfs.log')

optimizer = get_optimizer(Site.DRAFTKINGS, Sport.BASKETBALL, solver=CustomPuLPSolver)
keithotis commented 3 years ago

Well, I typed up the whole elegant response but I think I now know the issue and probably should have assumed this:

I think for lineup in optimizer.optimize_lineups(lineups) tries to generate unique lineups, however a typical slate of entries will contain dozens if not hundreds of contests where I will want to have the same lineup repeated across many different contests.

Right now I think optimizer.load_lineups_from_csv only accepts the janky CSV format that includes the "Instructions" so I think there are a number of solutions in decreasing order of user complexity:

1) Allow the solver to do unique solutions for each Contest ID that comes in the DKEntries.csv

2) Allow optimizer.load_lineups_from_csv to load files of the format exported by optimizer.export and/or dataframes of lineups.

3) Warn users that it only works if all of your entries in the DKEntries file is unique and remind them to add a header that fakes the expected 'Instructions' column when splitting their entries by Contest ID themselves.

Less relevant now but maybe still useful if others think they are having the same issue:

Thanks for the response. My general pipeline involves a whole lot of changes to use custom things but for this test to run down the bug I'm literally just using the raw DKSalaries.csv and the raw DKEntries.csv one gets from the contest and running that exact segment of code. Remember that I'm doing this several days after the contest for reproducibility and the expected behavior is for the script to just spit back out all entries as all players were locked and all entries were valid. I've also tried multiple days of contests to make sure I hadn't messed up.

In case it was the issue I just ran the following test:

from pydfs_lineup_optimizer import get_optimizer, Site, Sport, CSVLineupExporter

optimizer = get_optimizer(Site.DRAFTKINGS, Sport.BASKETBALL)

optimizer.load_players_from_csv('./DKEntries.csv')

lineups = optimizer.load_lineups_from_csv('./DKEntries.csv')

for lineup in optimizer.optimize_lineups(lineups):

    print(lineup)

It worked for three lineups and then crashes with:

Traceback (most recent call last):

  File "D:\Anaconda3\envs\dfs\lib\site-packages\pydfs_lineup_optimizer\lineup_optimizer.py", line 441, in optimize_lineups
    solved_variables = solver.solve()

  File "D:\Anaconda3\envs\dfs\lib\site-packages\pydfs_lineup_optimizer\solvers\pulp_solver.py", line 56, in solve
    raise SolverInfeasibleSolutionException(invalid_constraints)

SolverInfeasibleSolutionException: ['total_players', '_C5', 'budget', '_C21', '_C30', '_C57']

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File (sic), in <module>
    for lineup in optimizer.optimize_lineups(lineups):

  File (sic), in optimize_lineups
    raise GenerateLineupException(solver_exception.get_user_defined_constraints())

GenerateLineupException: Can't generate lineups. Following constraints are not valid: total_players,budget

I'm ET US and this was an issue in both EDT and EST and on Windows 10.

OzzyDFS commented 3 years ago

Just wanted to add on to this issue. Is there a way to skip lineups with all players locked? Perhaps by using an if else statement? When I use the late swap feature, the code stops as soon as it hits a lineup with all players locked and in progress. Even worse, the lineups it had iterated through before don't even get changed. So the end result is the exact same .csv file as before. To illustrate how frustrating this is, let's say I have 150 live lineups that I would like to late swap edit. If the 149th lineup has all players locked, the code breaks, and none of the previous 148 lineups change.

keithotis commented 3 years ago

Just wanted to add on to this issue. Is there a way to skip lineups with all players locked? Perhaps by using an if else statement? When I use the late swap feature, the code stops as soon as it hits a lineup with all players locked and in progress. Even worse, the lineups it had iterated through before don't even get changed. So the end result is the exact same .csv file as before. To illustrate how frustrating this is, let's say I have 150 live lineups that I would like to late swap edit. If the 149th lineup has all players locked, the code breaks, and none of the previous 148 lineups change.

Your code isn't crashing because the entire entry is locked, I'm able to run the code just fine on a slate of entries that's all from yesterday as long as they are all unique and it just spits them back out. It's likely crashing because it came across a lineup that has no more feasible solutions that aren't identical to previous ones. This can happen in a large entry contest in a smaller slate if you are filtering out players between runs based on a projection or value floor, the optimizer will run out of unique valid solutions, particularly in the Center position for NBA. A preferred behavior in late swap would be to allow it to start repeating entries with a warning rather than allowing zero projection players so that it doesn't crash.

lightninglarry commented 2 years ago

@keithotis was there ever a solution to this issue?

OzzyDFS commented 2 years ago

Hello, any updates to this? I see it is still open, so I assume no solution has been found. This can be really frustrating on some of these NBA slates where the optimizer can't edit because of other entries in my csv that have all players locked.