File size: 13,505 Bytes
158eaa8
18eaa68
67af571
158eaa8
 
 
 
 
 
72c43b1
158eaa8
8e462c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05f2b9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8e462c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05f2b9c
 
8e462c9
 
 
 
87bb04e
158eaa8
 
aef223f
 
0725b32
170fe18
0725b32
 
7dd1418
 
 
8fd7943
abd4533
 
 
 
 
 
 
 
 
 
158eaa8
 
 
 
 
 
 
 
b452fab
158eaa8
 
 
170fe18
 
1b89d01
6029c0b
 
f93672b
 
 
b452fab
f93672b
6029c0b
38f865e
 
87bb04e
38f865e
 
 
 
 
 
 
 
 
 
 
 
 
87bb04e
 
 
 
 
 
 
38f865e
 
87bb04e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c0b8c6
 
87bb04e
158eaa8
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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):
    """
    Check if a player is eligible for a specific NBA column position.
    
    Args:
        column_name (str): The column name (PG, PG1, PG2, SG, SG1, SG2, etc.)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    if any(pos in column_name for pos in ['PG', 'SG', 'SF', 'PF', 'C']):
        # Extract the base position from the column name
        base_position = next(pos for pos in ['PG', 'SG', 'SF', 'PF', 'C'] if pos in column_name)
        return base_position 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):
    """
    Check if a player is eligible for a specific LOL column position.
    
    Args:
        column_name (str): The column name (TOP, JNG, MID, ADC, SUP, UTIL)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    if any(pos in column_name for pos in ['TOP', 'JNG', 'MID', 'ADC', 'SUP', 'Team']):
        # Extract the base position from the column name
        base_position = next(pos for pos in ['TOP', 'JNG', 'MID', 'ADC', 'SUP', 'Team'] if pos in column_name)
        return base_position 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):
    """
    Check if a player is eligible for a specific MLB column position.
    
    Args:
        column_name (str): The column name (P, SP, RP, C, 1B, 2B, 3B, SS, OF)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    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 any(pos in column_name for pos in ['C', '1B', '2B', '3B', 'SS', 'OF']):
        return any(pos in ['C', '1B', '2B', '3B', 'SS', 'OF'] for pos in player_positions)
    return False

def check_nfl_position_eligibility(column_name, player_positions):
    """
    Check if a player is eligible for a specific NFL column position.
    
    Args:
        column_name (str): The column name (QB, RB, WR, TE, FLEX, DST)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    if any(pos in column_name for pos in ['QB', 'RB', 'WR', 'TE', 'DST']):
        return any(pos in ['QB', 'RB', 'WR', 'TE', 'DST'] for pos 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):
    """
    Check if a player is eligible for a specific Golf column position.
    
    Args:
        column_name (str): The column name (G)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    return True

def check_tennis_position_eligibility(column_name, player_positions):
    """
    Check if a player is eligible for a specific Tennis column position.
    
    Args:
        column_name (str): The column name (TEN)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    return True

def check_mma_position_eligibility(column_name, player_positions):
    """
    Check if a player is eligible for a specific MMA column position.
    
    Args:
        column_name (str): The column name (MMA)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    return True

def check_nascar_position_eligibility(column_name, player_positions):
    """
    Check if a player is eligible for a specific NASCAR column position.
    
    Args:
        column_name (str): The column name (NAS)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    return True

def check_cfb_position_eligibility(column_name, player_positions):
    """
    Check if a player is eligible for a specific CFB column position.
    
    Args:
        column_name (str): The column name (QB, RB, WR, TE, FLEX, DST)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    if any(pos in column_name for pos in ['QB', 'RB', 'WR']):
        return any(pos in ['QB', 'RB', 'WR'] for pos in player_positions)
    elif 'FLEX' in column_name:
        return any(pos in ['RB', 'WR'] for pos in player_positions)
    elif 'SUPERFLEX' 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):
    """
    Check if a player is eligible for a specific NHL column position.
    
    Args:
        column_name (str): The column name (C, LW, RW, D, G, UTIL)
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    if any(pos in column_name for pos in ['C', 'W', 'D', 'G']):
        return any(pos in ['C', 'W', 'D', 'G'] for pos in player_positions)
    elif 'FLEX' in column_name:
        return True  # UTIL can be any position
    elif 'UTIL' in column_name:
            return True  # UTIL can be any position
    return False

def check_position_eligibility(sport, column_name, player_positions):
    """
    Main function to check position eligibility based on sport.
    
    Args:
        sport (str): The sport (NBA, MLB, NFL, NHL)
        column_name (str): The column name
        player_positions (list): List of positions the player is eligible for
        
    Returns:
        bool: True if player is eligible for the column
    """
    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_cfb_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, exposure_stack_bool, remove_teams, projections_df, sport_var, type_var, salary_max):
    # Find comparable players in the projections
    comparable_players = projections_df[projections_df['player_names'] == exposure_player]
    
    comparable_players = comparable_players.reset_index(drop=True)
    if exposure_stack_bool == 'Yes':
        comparable_stack = comparable_players['team'][0]
    else:
        comparable_stack = 0
    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_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))

    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
                                )
    player_exposure = player_mask.sum() / len(working_frame)

    # find the number of lineups that need to be removed to reach the target exposure
    lineups_to_remove = ((player_exposure - exposure_target) * len(working_frame)) * 1.01

    # isolate the rows that contain the player
    player_rows = working_frame[player_mask]
    if comparable_stack != 0:
        player_rows = player_rows[player_rows['Stack'] != comparable_stack]
    
    change_counter = 0

    random_row_indices = list(player_rows.index)
    random.shuffle(random_row_indices)

    # for each row to the the number of lineups to remove, replace with random choice from comparable player list if they can be inserted
    for row in random_row_indices:
        if change_counter < math.ceil(lineups_to_remove):
            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)
            comparable_players = comparable_players[~comparable_players['player_names'].isin(existing_players)]

            # Create a list of comparable players
            comparable_player_list = comparable_players['player_names'].tolist()
            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
    return working_frame