def _build_lineup( self, players: List[Player], context: OptimizationContext, unswappable_players: Optional[List[LineupPlayer]] = None, ) -> Lineup: lineup = [] positions = self._settings.positions[:] if not positions: for player in sorted(players, key=lambda p: p.positions[0]): lineup.append( LineupPlayer( player, player.positions[0], used_fppg=context.players_used_fppg.get(player))) return Lineup(lineup, self._settings.lineup_printer) if unswappable_players: players = [ player for player in players if player not in unswappable_players ] positions = get_remaining_positions(positions, unswappable_players) lineup.extend(unswappable_players) players_with_positions = link_players_with_positions( players, positions) for player, position in players_with_positions.items(): lineup.append( LineupPlayer(player, position.name, used_fppg=context.players_used_fppg.get(player))) positions_order = [pos.name for pos in self._settings.positions] lineup.sort(key=lambda p: positions_order.index(p.lineup_position)) return Lineup(lineup, self._settings.lineup_printer)
def import_lineups(self, players): with open(self.filename, 'r') as csv_file: lines = csv.reader(csv_file) try: header = next(lines) start_column = 4 # First 4 columns has info about tournament end_column = header.index('Instructions') - 1 except (IndexError, ValueError): raise LineupOptimizerIncorrectCSV position_names = header[start_column:end_column] players_dict = {player.id: player for player in players} lineups = [] for line in lines: if not line[0]: break lineup_players = [] for index, position in zip(range(start_column, end_column), position_names): try: player_data = line[index] player_data = player_data.replace( '(LOCKED)', '') # Remove possible '(LOCKED)' substring player_id = player_data.split('(')[1][:-1] except IndexError: raise LineupOptimizerIncorrectCSV try: player = players_dict[player_id] except KeyError: raise LineupOptimizerIncorrectCSV( 'Player not found in players pool') lineup_players.append(LineupPlayer(player, position)) lineups.append(Lineup(lineup_players)) return lineups
def setUp(self): self.future_game_info = GameInfo( home_team='H', away_team='A', game_started=False, starts_at=datetime.now(timezone('EST')) + timedelta(days=1)) self.finished_game_info = GameInfo( home_team='H', away_team='A', game_started=False, starts_at=datetime.now(timezone('EST')) - timedelta(days=1)) self.lineup_optimizer = get_optimizer(Site.DRAFTKINGS, Sport.BASKETBALL) positions = ['PG', 'SG', 'SF', 'PF', 'C', 'PG/SG', 'SF/PF', 'C'] self.active_players = create_players(positions, game_info=self.future_game_info, salary=5000, fppg=20) self.inactive_players = create_players( positions, game_info=self.finished_game_info, salary=4500, fppg=10) self.lineup_optimizer.load_players(self.active_players + self.inactive_players) self.lineup = Lineup([ LineupPlayer(self.active_players[0], 'PG'), LineupPlayer(self.inactive_players[1], 'SG'), LineupPlayer(self.active_players[2], 'SF'), LineupPlayer(self.inactive_players[3], 'PF'), LineupPlayer(self.active_players[4], 'C'), LineupPlayer(self.inactive_players[5], 'G'), LineupPlayer(self.active_players[6], 'F'), LineupPlayer(self.inactive_players[7], 'UTIL'), ])
def import_lineups(self, players): with open(self.filename, 'r') as csv_file: lines = csv.reader(csv_file) try: header = next(lines) start_column = 4 # First 4 columns has info about tournament end_column = header.index('Instructions') - 1 except (IndexError, ValueError): raise LineupOptimizerIncorrectCSV position_names = header[start_column:end_column] players_dict = {player.id: player for player in players} lineups = [] for line in lines: if not line[0]: break lineup_players = [] for index, position in zip(range(start_column, end_column), position_names): try: match = re.search(self.LINEUP_PLAYER_ID_REGEX, line[index]) except IndexError: raise LineupOptimizerIncorrectCSV if not match: raise LineupOptimizerIncorrectCSV player_id = match.group('id') try: player = players_dict[player_id] except KeyError: raise LineupOptimizerIncorrectCSV('Player not found in players pool') lineup_players.append(LineupPlayer(player, position)) lineups.append(Lineup(lineup_players)) return lineups
def test_standard_strategy(self): player1 = Player('1', '1', '1', ['P'], 'test', 5000, 20) player2 = Player('2', '2', '2', ['P'], 'test', 8000, 30) strategy = StandardFantasyPointsStrategy() self.assertEqual(strategy.get_player_fantasy_points(player1), 20) self.assertEqual(strategy.get_player_fantasy_points(player2), 30) strategy.set_previous_lineup(Lineup([LineupPlayer(player1, 'P')])) self.assertEqual(strategy.get_player_fantasy_points(player1), 20) self.assertEqual(strategy.get_player_fantasy_points(player2), 30)
def test_progressive_strategy(self): player1 = Player('1', '1', '1', ['P'], 'test', 5000, 20) player2 = Player('2', '2', '2', ['P'], 'test', 8000, 30, progressive_scale=0.2) strategy = ProgressiveFantasyPointsStrategy(scale=0.1) self.assertEqual(strategy.get_player_fantasy_points(player1), 20) self.assertEqual(strategy.get_player_fantasy_points(player2), 30) strategy.set_previous_lineup(Lineup([LineupPlayer(player1, 'P')])) self.assertEqual(strategy.get_player_fantasy_points(player1), 20) self.assertEqual(strategy.get_player_fantasy_points(player2), 36) strategy.set_previous_lineup(Lineup([LineupPlayer(player1, 'P')])) self.assertEqual(strategy.get_player_fantasy_points(player1), 20) self.assertEqual(strategy.get_player_fantasy_points(player2), 42) strategy.set_previous_lineup(Lineup([LineupPlayer(player2, 'P')])) self.assertEqual(strategy.get_player_fantasy_points(player1), 22) self.assertEqual(strategy.get_player_fantasy_points(player2), 30)
def _build_lineup(self, players, unswappable_players=None): # type: (List[Player], Optional[List[LineupPlayer]]) -> Lineup lineup = [] positions = self._settings.positions[:] if unswappable_players: players = [ player for player in players if player not in unswappable_players ] positions = get_remaining_positions(positions, unswappable_players) lineup.extend(unswappable_players) players_with_positions = link_players_with_positions( players, positions) for player, position in players_with_positions.items(): lineup.append(LineupPlayer(player, position.name)) positions_order = [pos.name for pos in self._settings.positions] lineup.sort(key=lambda p: positions_order.index(p.lineup_position)) return Lineup(lineup, self._settings.lineup_printer)
def test_random_strategy(self): player1 = Player('1', '1', '1', ['P'], 'test', 5000, 20, min_deviation=0.1, max_deviation=0.2) player2 = Player('2', '2', '2', ['P'], 'test', 8000, 40) strategy = RandomFantasyPointsStrategy(0.05, 0.1) player1_fppg = strategy.get_player_fantasy_points(player1) if player1_fppg < 16 or 18 < player1_fppg < 22 or player1_fppg > 24: self.fail('Incorrect generated points') player2_fppg = strategy.get_player_fantasy_points(player2) if player2_fppg < 36 or 38 < player2_fppg < 42 or player2_fppg > 44: self.fail('Incorrect generated points') strategy.set_previous_lineup(Lineup([LineupPlayer(player1, 'P')])) player1_fppg = strategy.get_player_fantasy_points(player1) if player1_fppg < 16 or 18 < player1_fppg < 22 or player1_fppg > 24: self.fail('Incorrect generated points') player2_fppg = strategy.get_player_fantasy_points(player2) if player2_fppg < 36 or 38 < player2_fppg < 42 or player2_fppg > 44: self.fail('Incorrect generated points')
def optimize(self, n=1): ''' Select optimal lineup from players list. This method uses Mixed Integer Linear Programming method for evaluating best starting lineup. It's return generator. If you don't specify n it will return generator with all possible lineups started from highest fppg to lowest fppg. :type n: int :rtype: List[Lineup] ''' positions = {} current_max_points = 100000 # not sure why this is at the top - something to do with iterators?? lineup_points = sum(player.fppg for player in self._lineup) if len(self._lineup) == self._settings.total_players: lineup = Lineup(self._lineup) yield lineup raise StopIteration() counter = 0 while n > counter: players = self._players prob = LpProblem("Daily Fantasy Sports", LpMaximize) x = LpVariable.dicts('players', players, lowBound=0, upBound=1, cat=LpInteger) # objective function - maximize fppg prob += sum([player.fppg * x[player] for player in players]) # constraints ## < current_max_points: needed for multiple lineups - set max points slightly below last lineup ## < budget: have to obey salary cap ## = total_players: need correct number of players ## == 1: no duplicate player IDs ## num + addition: obey position minimums (1,1,1,1,1,3,3) prob += sum([player.fppg * x[player] for player in players]) <= current_max_points prob += sum([player.salary * x[player] for player in players]) <= self._budget prob += sum([x[player] for player in players]) == self._total_players for pid in set([p.id for p in players]): prob += sum( [x[player] for player in players if player.id == pid]) == 1 for position, num in self._positions.items(): prob += sum([x[player] for player in players if any([player_position in position for player_position in player.positions])]) \ >= num GUROBI(OutputFlag=0).solve(prob) if prob.status == 1: lineup_players = self._lineup[:] for player in players: if x[player].value() == 1.0: lineup_players.append(player) lineup = Lineup(lineup_players) current_max_points = lineup.fantasy_points_projection - lineup_points - 0.1 yield lineup counter += 1 else: raise LineupOptimizerException("Can't generate lineups") raise StopIteration()