DFS_Portfolio_Manager / global_func /exposure_spread.py
James McCool
Implement error handling in 'exposure_spread' to ensure comparable player list is generated safely, defaulting to an empty list if an exception occurs, enhancing robustness in player selection logic.
076e322
raw
history blame
16.6 kB
import random
import numpy as np
import math
#### Goal is to choose a player and adjust the amount of lineups that have them
#### First thing you need to do is find comparable players in the projections, so any player in the projections that is within $500 of the player and within 10% of the projection
#### Take that list of players and create a list that can be accessed for random insertion into the portfolio
#### Find the player and the amount of rows that contain them and then find an exposure rate which is the percentage of total rows
#### Use the exposure target argument and try to replace the player from as many rows as necessary to be at or just under the target
#### makes sure to check if the player is eligible for the position before replacing them
def check_nba_position_eligibility(column_name, player_positions):
if 'PG' in column_name:
return 'PG' in player_positions
elif 'SG' in column_name:
return 'SG' in player_positions
elif 'SF' in column_name:
return 'SF' in player_positions
elif 'PF' in column_name:
return 'PF' in player_positions
elif 'C' in column_name:
return 'C' in player_positions
elif 'G' in column_name:
return any(pos in ['PG', 'SG'] for pos in player_positions)
elif 'F' in column_name:
return any(pos in ['SF', 'PF'] for pos in player_positions)
elif 'UTIL' in column_name:
return True # UTIL can be any position
return False
def check_lol_position_eligibility(column_name, player_positions):
if 'TOP' in column_name:
return 'TOP' in player_positions
elif 'JNG' in column_name:
return 'JNG' in player_positions
elif 'MID' in column_name:
return 'MID' in player_positions
elif 'ADC' in column_name:
return 'ADC' in player_positions
elif 'SUP' in column_name:
return 'SUP' in player_positions
elif 'Team' in column_name:
return 'Team' in player_positions
elif 'CPT' in column_name:
return any(pos in ['TOP', 'JNG', 'MID', 'ADC', 'SUP'] for pos in player_positions)
return False
def check_mlb_position_eligibility(column_name, player_positions):
if any(pos in column_name for pos in ['P', 'SP', 'RP']):
return any(pos in ['P', 'SP', 'RP'] for pos in player_positions)
elif 'C' in column_name:
return 'C' in player_positions
elif '1B' in column_name:
return '1B' in player_positions
elif '2B' in column_name:
return '2B' in player_positions
elif '3B' in column_name:
return '3B' in player_positions
elif 'SS' in column_name:
return 'SS' in player_positions
elif 'OF' in column_name:
return 'OF' in player_positions
return False
def check_nfl_position_eligibility(column_name, player_positions):
if 'QB' in column_name:
return 'QB' in player_positions
elif 'RB' in column_name:
return 'RB' in player_positions
elif 'WR' in column_name:
return 'WR' in player_positions
elif 'TE' in column_name:
return 'TE' in player_positions
elif 'DST' in column_name:
return 'DST' in player_positions
elif 'FLEX' in column_name:
return any(pos in ['RB', 'WR', 'TE'] for pos in player_positions)
elif 'UTIL' in column_name:
return any(pos in ['RB', 'WR', 'TE'] for pos in player_positions)
return False
def check_golf_position_eligibility(column_name, player_positions):
if 'FLEX' in column_name:
return any(pos in ['G'] for pos in player_positions)
return True
def check_tennis_position_eligibility(column_name, player_positions):
if 'FLEX' in column_name:
return any(pos in ['T'] for pos in player_positions)
return True
def check_mma_position_eligibility(column_name, player_positions):
if 'FLEX' in column_name:
return any(pos in ['F'] for pos in player_positions)
return True
def check_nascar_position_eligibility(column_name, player_positions):
if 'FLEX' in column_name:
return any(pos in ['D'] for pos in player_positions)
return True
def check_ncaaf_position_eligibility(column_name, player_positions):
if 'QB' in column_name:
return 'QB' in player_positions
elif 'RB' in column_name:
return 'RB' in player_positions
elif 'WR' in column_name:
return 'WR' in player_positions
elif 'FLEX' in column_name:
return any(pos in ['RB', 'WR'] for pos in player_positions)
elif 'SFLEX' in column_name:
return any(pos in ['RB', 'WR', 'QB'] for pos in player_positions)
return False
def check_nhl_position_eligibility(column_name, player_positions):
if 'C' in column_name:
return 'C' in player_positions
elif 'W' in column_name:
return 'W' in player_positions
elif 'D' in column_name:
return 'D' in player_positions
elif 'G' in column_name:
return 'G' in player_positions
elif 'FLEX' in column_name:
return any(pos in ['C', 'W', 'D'] for pos in player_positions)
elif 'UTIL' in column_name:
return any(pos in ['C', 'W', 'D'] for pos in player_positions)
return False
def check_position_eligibility(sport, column_name, player_positions):
if sport == 'NBA':
return check_nba_position_eligibility(column_name, player_positions)
elif sport == 'MLB':
return check_mlb_position_eligibility(column_name, player_positions)
elif sport == 'NFL':
return check_nfl_position_eligibility(column_name, player_positions)
elif sport == 'NHL':
return check_nhl_position_eligibility(column_name, player_positions)
elif sport == 'MMA':
return check_mma_position_eligibility(column_name, player_positions)
elif sport == 'GOLF':
return check_golf_position_eligibility(column_name, player_positions)
elif sport == 'TENNIS':
return check_tennis_position_eligibility(column_name, player_positions)
elif sport == 'LOL':
return check_lol_position_eligibility(column_name, player_positions)
else:
# Default fallback - assume exact position match
return column_name in player_positions
def exposure_spread(working_frame, exposure_player, exposure_target, ignore_stacks, remove_teams, specific_replacements, projections_df, sport_var, type_var, salary_max, stacking_sports):
comparable_players = projections_df[projections_df['player_names'] == exposure_player]
comparable_players = comparable_players.reset_index(drop=True)
comp_salary_high = comparable_players['salary'][0]
comp_salary_low = comparable_players['salary'][0] - 500
comp_projection_high = comparable_players['median'][0]
comp_projection_low = comparable_players['median'][0] - (comparable_players['median'][0] * .75)
# players can be eligible at multiple positions, so we need to find all the positions the player is eligible at
# the position column can have positions designated as 1B/OF which means they are eligible at 1B and OF
comp_player_position = comparable_players['position'].tolist()
comp_team = comparable_players['team'].tolist()
try:
comp_player_position = [pos.split('/') for pos in comp_player_position]
comp_player_position = [item for sublist in comp_player_position for item in sublist]
comp_player_position = list(set(comp_player_position))
except:
comp_player_position = comparable_players['position'].tolist()
def has_position_overlap(player_positions, target_positions):
player_pos_list = player_positions.split('/')
return any(pos in target_positions for pos in player_pos_list)
# find the exposure rate of the player in the working frame
player_mask = working_frame[working_frame.columns].apply(
lambda row: exposure_player in list(row), axis=1
)
replace_mask = working_frame.apply(
lambda row: exposure_player not in list(row), axis=1
)
player_exposure = player_mask.sum() / len(working_frame)
replace_exposure = replace_mask.sum() / len(working_frame)
# find the number of lineups that need to be removed to reach the target exposure
if exposure_target == 0:
lineups_to_remove = (player_exposure * len(working_frame))
else:
lineups_to_remove = ((player_exposure - exposure_target) * len(working_frame)) * 1.01
lineups_to_add = ((exposure_target - player_exposure) * (len(working_frame) - (player_exposure * len(working_frame)))) * 1.10
# isolate the rows that contain the player
player_rows = working_frame[player_mask]
replace_rows = working_frame[replace_mask]
if ignore_stacks != []:
player_rows = player_rows[~player_rows['Stack'].isin(ignore_stacks)]
replace_rows = replace_rows[~replace_rows['Stack'].isin(ignore_stacks)]
change_counter = 0
random_row_indices_insert = list(player_rows.index)
random_row_indices_replace = list(replace_rows.index)
random.shuffle(random_row_indices_insert)
random.shuffle(random_row_indices_replace)
# for each row to the the number of lineups to remove, replace with random choice from comparable player list if they can be inserted
# we will need to use two separate functions here, one for an exposure player who has a lineups to remove above 0 and one for below 0
# key concept here is if they have a lineups to remove above 0 it means that we are trying to replace them with comparable players
# if the lineups to remove is below zero it means we want to find comparable players and replace them with the exposure player
if lineups_to_remove > 0:
for row in random_row_indices_insert:
if change_counter < math.ceil(lineups_to_remove):
if specific_replacements != []:
comparable_players = projections_df[(projections_df['player_names'].isin(specific_replacements)) &
(projections_df['salary'] <= comp_salary_high + (salary_max - working_frame['salary'][row]))
]
else:
comparable_players = projections_df[
(projections_df['salary'] >= comp_salary_low) &
(projections_df['salary'] <= comp_salary_high + (salary_max - working_frame['salary'][row])) &
(projections_df['median'] >= comp_projection_low) &
(projections_df['position'].apply(lambda x: has_position_overlap(x, comp_player_position)))
]
if exposure_target == 0:
comparable_players = comparable_players[comparable_players['player_names'] != exposure_player]
if remove_teams is not None:
remove_mask = comparable_players.apply(
lambda row: not any(team in list(row) for team in remove_teams), axis=1
)
comparable_players = comparable_players[remove_mask]
# Get the current row data to check for existing players
current_row_data = working_frame.iloc[row]
# Filter out players that are already present in this row
existing_players = set(current_row_data.values)
print(existing_players)
print(comparable_players)
print("^^^^ comparable players")
try:
comparable_players = comparable_players[~comparable_players['player_names'].isin(existing_players)]
comparable_player_list = comparable_players['player_names'].tolist()
except:
comparable_player_list = []
if comparable_player_list:
insert_player = random.choice(comparable_player_list)
# Find which column contains the exposure_player
row_data = working_frame.iloc[row]
for col in working_frame.columns:
if row_data[col] == exposure_player:
# Get the replacement player's positions
replacement_player_positions = projections_df[projections_df['player_names'] == insert_player]['position'].iloc[0].split('/')
# Check if the replacement player is eligible for this column
if type_var == 'Classic':
if check_position_eligibility(sport_var, col, replacement_player_positions):
working_frame.at[row, col] = insert_player
break
else:
working_frame.at[row, col] = insert_player
break
change_counter += 1
else:
for row in random_row_indices_replace:
if change_counter < math.ceil(lineups_to_add):
if specific_replacements != []:
comparable_players = projections_df[(projections_df['player_names'].isin(specific_replacements)) &
(projections_df['salary'] <= comp_salary_high + (salary_max - working_frame['salary'][row]))
]
else:
comparable_players = projections_df[
(projections_df['salary'] >= comp_salary_low) &
(projections_df['salary'] <= comp_salary_high + (salary_max - working_frame['salary'][row])) &
(projections_df['position'].apply(lambda x: has_position_overlap(x, comp_player_position)))
]
if sport_var in stacking_sports:
if sport_var in stacking_sports:
if working_frame.iloc[row]['Size'] == 5 and comp_team != working_frame.iloc[row]['Stack']:
remove_mask = comparable_players.apply(
lambda player_row: not any(team in list(player_row) for team in [working_frame.iloc[row]['Stack']]), axis=1
)
comparable_players = comparable_players[remove_mask]
if remove_teams is not None:
remove_mask = comparable_players.apply(
lambda row: not any(team in list(row) for team in remove_teams), axis=1
)
comparable_players = comparable_players[remove_mask]
comparable_players = comparable_players[comparable_players['player_names'] != exposure_player]
# Create a list of comparable players
comparable_player_list = comparable_players['player_names'].tolist()
if comparable_player_list:
# Find which column contains the exposure_player
row_data = working_frame.iloc[row]
for col in working_frame.columns:
if row_data[col] in comparable_player_list:
if working_frame.iloc[row]['salary'] - projections_df[projections_df['player_names'] == row_data[col]]['salary'].iloc[0] + projections_df[projections_df['player_names'] == exposure_player]['salary'].iloc[0] <= salary_max:
# Get the replacement player's positions
replacement_player_positions = projections_df[projections_df['player_names'] == row_data[col]]['position'].iloc[0].split('/')
exposure_player_positions = projections_df[projections_df['player_names'] == exposure_player]['position'].iloc[0].split('/')
# Check if the replacement player is eligible for this column
if type_var == 'Classic':
if check_position_eligibility(sport_var, col, exposure_player_positions):
working_frame.at[row, col] = exposure_player
change_counter += 1
break
else:
working_frame.at[row, col] = exposure_player
change_counter += 1
break
else:
continue
return working_frame