James McCool commited on
Commit
efd84aa
·
1 Parent(s): ba2158d

Add MMA support to ROO simulation with site-specific functions and dynamic projections

Browse files
Files changed (2) hide show
  1. app.py +17 -5
  2. function_hold/MMA_functions.py +448 -0
app.py CHANGED
@@ -18,6 +18,7 @@ from pandas import DataFrame
18
 
19
  #bring in functions
20
  from function_hold.NBA_functions import DK_NBA_ROO_Build, FD_NBA_ROO_Build
 
21
 
22
  def load_file(upload):
23
  if upload is not None:
@@ -38,11 +39,11 @@ def load_file(upload):
38
  return None
39
  return None
40
 
41
- tab1, tab2 = st.tabs(["Data Load", "Manage Portfolio"])
42
  with tab1:
43
  if st.button('Clear data', key='reset1'):
44
  st.session_state.clear()
45
- sport_var = st.selectbox("Select Sport", ["NBA", "NFL", "MLB"])
46
  st.subheader("Projections File")
47
  if sport_var == "NBA":
48
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
@@ -50,7 +51,8 @@ with tab1:
50
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
51
  elif sport_var == "MLB":
52
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
53
-
 
54
  # Create two columns for the uploader and template button
55
  upload_col, template_col = st.columns([3, 1])
56
 
@@ -64,6 +66,8 @@ with tab1:
64
  template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
65
  elif sport_var == "MLB":
66
  template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
 
 
67
  # Add download button for template
68
  st.download_button(
69
  label="Template",
@@ -86,8 +90,11 @@ with tab2:
86
  site_var_sb = st.selectbox("Select Site", ["Draftkings", "Fanduel"])
87
  distribution_type_sb = st.selectbox("Select Distribution Type", ['normal', 'poisson', 'bimodal'])
88
  st.info("The distribution type will determine the shape of the distribution of the ROO values. The normal distribution is for more linear projections, the poisson distribution is for stats like HRs and other counting stats, and the bimodal distribution is useful for event oriented outcomes frequent in MMA.")
89
- floor_var_sb = st.number_input("Floor (low end multiplier)", min_value=0.00, max_value=.50, value=.25, step=.01)
90
- ceiling_var_sb = st.number_input("Ceiling (high end multiplier)", min_value=1.50, max_value=3.00, value=2.00, step=.01)
 
 
 
91
  std_var_sb = st.number_input("Standard Deviation (variance within distribution)", min_value=1.00, max_value=5.00, value=4.00, step=.01)
92
 
93
  if projections_file:
@@ -107,6 +114,11 @@ with tab2:
107
  disp_file = DK_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
108
  elif site_var_sb == "Fanduel":
109
  disp_file = FD_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
 
 
 
 
 
110
 
111
  try:
112
  if 'disp_file' in locals():
 
18
 
19
  #bring in functions
20
  from function_hold.NBA_functions import DK_NBA_ROO_Build, FD_NBA_ROO_Build
21
+ from function_hold.MMA_functions import DK_MMA_ROO_Build, FD_MMA_ROO_Build
22
 
23
  def load_file(upload):
24
  if upload is not None:
 
39
  return None
40
  return None
41
 
42
+ tab1, tab2 = st.tabs(["Data Load", "Create ROO"])
43
  with tab1:
44
  if st.button('Clear data', key='reset1'):
45
  st.session_state.clear()
46
+ sport_var = st.selectbox("Select Sport", ["NBA", "NFL", "MLB", "MMA"])
47
  st.subheader("Projections File")
48
  if sport_var == "NBA":
49
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
 
51
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
52
  elif sport_var == "MLB":
53
  st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
54
+ elif sport_var == "MMA":
55
+ st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Salary', 'Median', 'KO Potential', 'Own'")
56
  # Create two columns for the uploader and template button
57
  upload_col, template_col = st.columns([3, 1])
58
 
 
66
  template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
67
  elif sport_var == "MLB":
68
  template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
69
+ elif sport_var == "MMA":
70
+ template_df = pd.DataFrame(columns=['Player', 'Salary', 'Median', 'KO_var', 'Own'])
71
  # Add download button for template
72
  st.download_button(
73
  label="Template",
 
90
  site_var_sb = st.selectbox("Select Site", ["Draftkings", "Fanduel"])
91
  distribution_type_sb = st.selectbox("Select Distribution Type", ['normal', 'poisson', 'bimodal'])
92
  st.info("The distribution type will determine the shape of the distribution of the ROO values. The normal distribution is for more linear projections, the poisson distribution is for stats like HRs and other counting stats, and the bimodal distribution is useful for event oriented outcomes frequent in MMA.")
93
+ if sport_var == "MMA":
94
+ st.info("MMA utilizes imputs from the projections file to determine distribution ranges, floor and ceiling are determined by Knockout potential (KO_var).")
95
+ elif sport_var != "MMA":
96
+ floor_var_sb = st.number_input("Floor (low end multiplier)", min_value=0.00, max_value=.50, value=.25, step=.01)
97
+ ceiling_var_sb = st.number_input("Ceiling (high end multiplier)", min_value=1.50, max_value=3.00, value=2.00, step=.01)
98
  std_var_sb = st.number_input("Standard Deviation (variance within distribution)", min_value=1.00, max_value=5.00, value=4.00, step=.01)
99
 
100
  if projections_file:
 
114
  disp_file = DK_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
115
  elif site_var_sb == "Fanduel":
116
  disp_file = FD_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
117
+ elif sport_var == "MMA":
118
+ if site_var_sb == "Draftkings":
119
+ disp_file = DK_MMA_ROO_Build(projections, std_var_sb, distribution_type_sb)
120
+ elif site_var_sb == "Fanduel":
121
+ disp_file = FD_MMA_ROO_Build(projections, std_var_sb, distribution_type_sb)
122
 
123
  try:
124
  if 'disp_file' in locals():
function_hold/MMA_functions.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from numpy import nan as np_nan
2
+ from numpy import where as np_where
3
+ from numpy import random as np_random
4
+ from numpy import zeros as np_zeros
5
+ from numpy import array as np_array
6
+ from pandas import concat as pd_concat
7
+ from pandas import merge as pd_merge
8
+ from pandas import DataFrame
9
+
10
+ def DK_MMA_ROO_Build(projections_file, std_var, distribution_type):
11
+ total_sims = 1000
12
+
13
+ projects_raw = projections_file.copy()
14
+ projects_raw = projects_raw.replace("", np_nan)
15
+ dk_df = projects_raw.sort_values(by='Median', ascending=False)
16
+
17
+ basic_own_df = dk_df.copy()
18
+
19
+ def calculate_ownership(df):
20
+ # Filter the dataframe based on the position
21
+ frame = df.copy()
22
+
23
+ # Calculate Small Field Own%
24
+ frame['Base Own%'] = np_where(
25
+ (frame['Own'] - frame['Own'].mean() >= 0),
26
+ frame['Own'] * (5 * (frame['Own'] - (frame['Own'].mean() / 1.5)) / 100) + frame['Own'].mean(),
27
+ frame['Own']
28
+ )
29
+ frame['Base Own%'] = np_where(
30
+ frame['Base Own%'] > 85,
31
+ 85,
32
+ frame['Base Own%']
33
+ )
34
+
35
+ # Calculate Small Field Own%
36
+ frame['Small Field Own%'] = np_where(
37
+ (frame['Own'] - frame['Own'].mean() >= 0),
38
+ frame['Own'] * (6 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
39
+ frame['Own']
40
+ )
41
+ frame['Small Field Own%'] = np_where(
42
+ frame['Small Field Own%'] > 85,
43
+ 85,
44
+ frame['Small Field Own%']
45
+ )
46
+
47
+ # Calculate Large Field Own%
48
+ frame['Large Field Own%'] = np_where(
49
+ (frame['Own'] - frame['Own'].mean() >= 0),
50
+ frame['Own'] * (2.5 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
51
+ frame['Own']
52
+ )
53
+ frame['Large Field Own%'] = np_where(
54
+ frame['Large Field Own%'] > 85,
55
+ 85,
56
+ frame['Large Field Own%']
57
+ )
58
+
59
+ # Calculate Cash Own%
60
+ frame['Cash Own%'] = np_where(
61
+ (frame['Own'] - frame['Own'].mean() >= 0),
62
+ frame['Own'] * (8 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
63
+ frame['Own']
64
+ )
65
+ frame['Cash Own%'] = np_where(
66
+ frame['Cash Own%'] > 85,
67
+ 85,
68
+ frame['Cash Own%']
69
+ )
70
+
71
+ return frame
72
+
73
+ # Apply the function to each dataframe
74
+ basic_own_df = calculate_ownership(basic_own_df)
75
+
76
+ own_norm_var_reg = 600 / basic_own_df['Own'].sum()
77
+ own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
78
+ own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
79
+ own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
80
+ basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
81
+ basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
82
+ basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
83
+ basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
84
+
85
+ basic_own_df['Own'] = np_where(basic_own_df['Own'] > 90, 90, basic_own_df['Own'])
86
+
87
+ # Apply the function to each dataframe
88
+ basic_own_df = calculate_ownership(basic_own_df)
89
+
90
+ own_norm_var_reg = 600 / basic_own_df['Own'].sum()
91
+ own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
92
+ own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
93
+ own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
94
+ basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
95
+ basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
96
+ basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
97
+ basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
98
+
99
+ own_dict = dict(zip(basic_own_df.Player, basic_own_df.Own))
100
+ small_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Small Field Own%']))
101
+ large_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Large Field Own%']))
102
+ cash_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Cash Own%']))
103
+ min_dict = dict(zip(basic_own_df.Player, basic_own_df.KO_var))
104
+
105
+ flex_file = basic_own_df[['Player', 'Salary', 'Median', 'KO_var']]
106
+ flex_file = flex_file.rename(columns={"Agg": "Median"})
107
+ flex_file['Floor'] = flex_file['Median'] * (1-flex_file['KO_var'])
108
+ flex_file['Ceiling'] = flex_file['Median'] * (1+flex_file['KO_var'])
109
+ flex_file['STD'] = (flex_file['Median'] / std_var)
110
+ flex_file = flex_file[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
111
+ flex_file = flex_file.reset_index(drop=True)
112
+ hold_file = flex_file.copy()
113
+ overall_file = flex_file.copy()
114
+ salary_file = flex_file.copy()
115
+
116
+ try:
117
+ overall_floor_gpu = np_array(overall_file['Floor'])
118
+ overall_ceiling_gpu = np_array(overall_file['Ceiling'])
119
+ overall_median_gpu = np_array(overall_file['Median'])
120
+ overall_std_gpu = np_array(overall_file['STD'])
121
+ overall_salary_gpu = np_array(overall_file['Salary'])
122
+
123
+ data_shape = (len(overall_file['Player']), total_sims) # Example: 1000 rows
124
+ salary_array = np_zeros(data_shape)
125
+ sim_array = np_zeros(data_shape)
126
+
127
+ for x in range(0, total_sims):
128
+ result_gpu = overall_salary_gpu
129
+ salary_array[:, x] = result_gpu
130
+ cupy_array = salary_array
131
+
132
+ salary_file = salary_file.reset_index(drop=True)
133
+ salary_cupy = DataFrame(cupy_array, columns=list(range(0, total_sims)))
134
+ salary_check_file = pd_concat([salary_file, salary_cupy], axis=1)
135
+ except:
136
+ for x in range(0,total_sims):
137
+ salary_file[x] = salary_file['Salary']
138
+ salary_check_file = salary_file.copy()
139
+
140
+ salary_file=salary_check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
141
+
142
+ salary_file = salary_file.div(1000)
143
+
144
+ try:
145
+ for x in range(0, total_sims):
146
+ if distribution_type == 'normal':
147
+ # Normal distribution (existing logic)
148
+ result_gpu = np_random.normal(overall_median_gpu, overall_std_gpu)
149
+ elif distribution_type == 'poisson':
150
+ # Poisson distribution - using median as lambda
151
+ result_gpu = np_random.poisson(overall_median_gpu)
152
+ elif distribution_type == 'bimodal':
153
+ # Bimodal distribution - mixture of two normal distributions
154
+ # First peak centered at 80% of median, second at 120% of median
155
+ if np_random.random() < 0.5:
156
+ result_gpu = np_random.normal(overall_floor_gpu, overall_std_gpu)
157
+ else:
158
+ result_gpu = np_random.normal(overall_ceiling_gpu, overall_std_gpu)
159
+ else:
160
+ raise ValueError("Invalid distribution type. Must be 'normal', 'poisson', or 'bimodal'")
161
+
162
+ sim_array[:, x] = result_gpu
163
+ add_array = sim_array
164
+
165
+ overall_file = overall_file.reset_index(drop=True)
166
+ df2 = DataFrame(add_array, columns=list(range(0, total_sims)))
167
+ check_file = pd_concat([overall_file, df2], axis=1)
168
+ except:
169
+ for x in range(0,total_sims):
170
+ if distribution_type == 'normal':
171
+ overall_file[x] = np_random.normal(overall_file['Median'], overall_file['STD'])
172
+ elif distribution_type == 'poisson':
173
+ overall_file[x] = np_random.poisson(overall_file['Median'])
174
+ elif distribution_type == 'bimodal':
175
+ # Bimodal distribution fallback
176
+ if np_random.random() < 0.5:
177
+ overall_file[x] = np_random.normal(overall_file['Floor'], overall_file['STD'])
178
+ else:
179
+ overall_file[x] = np_random.normal(overall_file['Ceiling'], overall_file['STD'])
180
+ check_file = overall_file.copy()
181
+
182
+ overall_file=check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
183
+
184
+ players_only = hold_file[['Player']]
185
+ raw_lineups_file = players_only
186
+
187
+ for x in range(0,total_sims):
188
+ maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
189
+ raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
190
+ players_only[x] = raw_lineups_file[x].rank(ascending=False)
191
+
192
+ players_only=players_only.drop(['Player'], axis=1)
193
+
194
+ salary_4x_check = (overall_file - (salary_file*4))
195
+ salary_5x_check = (overall_file - (salary_file*5))
196
+ salary_6x_check = (overall_file - (salary_file*6))
197
+ gpp_check = (overall_file - ((salary_file*5)+10))
198
+
199
+ players_only['Average_Rank'] = players_only.mean(axis=1)
200
+ players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
201
+ players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
202
+ players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
203
+ players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
204
+ players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
205
+ players_only['5x%'] = salary_5x_check[salary_5x_check >= 1].count(axis=1)/float(total_sims)
206
+ players_only['6x%'] = salary_6x_check[salary_6x_check >= 1].count(axis=1)/float(total_sims)
207
+ players_only['GPP%'] = gpp_check[gpp_check >= 1].count(axis=1)/float(total_sims)
208
+
209
+ players_only['Player'] = hold_file[['Player']]
210
+
211
+ final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
212
+
213
+ final_Proj = pd_merge(hold_file, final_outcomes, on="Player")
214
+ final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
215
+
216
+ final_Proj['name_team'] = final_Proj['Player'] + final_Proj['Position']
217
+ final_Proj['Own'] = final_Proj['Player'].map(own_dict)
218
+ final_Proj['Small_Own'] = final_Proj['Player'].map(small_own_dict)
219
+ final_Proj['Large_Own'] = final_Proj['Player'].map(large_own_dict)
220
+ final_Proj['Cash_Own'] = final_Proj['Player'].map(cash_own_dict)
221
+ final_Proj['CPT_Own'] = final_Proj['Own'] / 6
222
+ final_Proj['LevX'] = ((final_Proj[['Top_finish', '6x%', 'Top_5_finish']].mean(axis=1))*100) - final_Proj['Own']
223
+ final_Proj['ValX'] = ((final_Proj[['5x%', '6x%']].mean(axis=1))*100) + final_Proj['LevX']
224
+
225
+ final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%', 'Own', 'Small_Own', 'Large_Own', 'Cash_Own', 'CPT_Own', 'LevX', 'ValX']]
226
+ final_Proj = final_Proj.sort_values(by='Median', ascending=False)
227
+
228
+ return final_Proj.copy()
229
+
230
+ def FD_MMA_ROO_Build(projections_file, std_var, distribution_type):
231
+ total_sims = 1000
232
+
233
+ projects_raw = projections_file.copy()
234
+ fd_df = projects_raw.sort_values(by='Median', ascending=False)
235
+
236
+ basic_own_df = fd_df.copy()
237
+
238
+ def calculate_ownership(df):
239
+ # Filter the dataframe based on the position
240
+ frame = df.copy()
241
+
242
+ # Calculate Small Field Own%
243
+ frame['Base Own%'] = np_where(
244
+ (frame['Own'] - frame['Own'].mean() >= 0),
245
+ frame['Own'] * (5 * (frame['Own'] - (frame['Own'].mean() / 1.5)) / 100) + frame['Own'].mean(),
246
+ frame['Own']
247
+ )
248
+ frame['Base Own%'] = np_where(
249
+ frame['Base Own%'] > 85,
250
+ 85,
251
+ frame['Base Own%']
252
+ )
253
+
254
+ # Calculate Small Field Own%
255
+ frame['Small Field Own%'] = np_where(
256
+ (frame['Own'] - frame['Own'].mean() >= 0),
257
+ frame['Own'] * (6 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
258
+ frame['Own']
259
+ )
260
+ frame['Small Field Own%'] = np_where(
261
+ frame['Small Field Own%'] > 85,
262
+ 85,
263
+ frame['Small Field Own%']
264
+ )
265
+
266
+ # Calculate Large Field Own%
267
+ frame['Large Field Own%'] = np_where(
268
+ (frame['Own'] - frame['Own'].mean() >= 0),
269
+ frame['Own'] * (2.5 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
270
+ frame['Own']
271
+ )
272
+ frame['Large Field Own%'] = np_where(
273
+ frame['Large Field Own%'] > 85,
274
+ 85,
275
+ frame['Large Field Own%']
276
+ )
277
+
278
+ # Calculate Cash Own%
279
+ frame['Cash Own%'] = np_where(
280
+ (frame['Own'] - frame['Own'].mean() >= 0),
281
+ frame['Own'] * (8 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
282
+ frame['Own']
283
+ )
284
+ frame['Cash Own%'] = np_where(
285
+ frame['Cash Own%'] > 85,
286
+ 85,
287
+ frame['Cash Own%']
288
+ )
289
+
290
+ return frame
291
+
292
+ # Apply the function to each dataframe
293
+ basic_own_df = calculate_ownership(basic_own_df)
294
+
295
+ own_norm_var_reg = 600 / basic_own_df['Own'].sum()
296
+ own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
297
+ own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
298
+ own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
299
+ basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
300
+ basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
301
+ basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
302
+ basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
303
+
304
+ basic_own_df['Own'] = np_where(basic_own_df['Own'] > 90, 90, basic_own_df['Own'])
305
+
306
+ # Apply the function to each dataframe
307
+ basic_own_df = calculate_ownership(basic_own_df)
308
+
309
+ own_norm_var_reg = 600 / basic_own_df['Own'].sum()
310
+ own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
311
+ own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
312
+ own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
313
+ basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
314
+ basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
315
+ basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
316
+ basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
317
+
318
+ own_dict = dict(zip(basic_own_df.Player, basic_own_df.Own))
319
+ small_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Small Field Own%']))
320
+ large_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Large Field Own%']))
321
+ cash_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Cash Own%']))
322
+ min_dict = dict(zip(basic_own_df.Player, basic_own_df.KO_var))
323
+
324
+ flex_file = basic_own_df[['Player', 'Salary', 'Median', 'KO_var']]
325
+ flex_file = flex_file.rename(columns={"Agg": "Median"})
326
+ flex_file['Floor'] = flex_file['Median'] * (1-flex_file['KO_var'])
327
+ flex_file['Ceiling'] = flex_file['Median'] * (1+flex_file['KO_var'])
328
+ flex_file['STD'] = (flex_file['Median'] / std_var)
329
+ flex_file = flex_file[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
330
+ flex_file = flex_file.reset_index(drop=True)
331
+ hold_file = flex_file.copy()
332
+ overall_file = flex_file.copy()
333
+ salary_file = flex_file.copy()
334
+
335
+ try:
336
+ overall_floor_gpu = np_array(overall_file['Floor'])
337
+ overall_ceiling_gpu = np_array(overall_file['Ceiling'])
338
+ overall_median_gpu = np_array(overall_file['Median'])
339
+ overall_std_gpu = np_array(overall_file['STD'])
340
+ overall_salary_gpu = np_array(overall_file['Salary'])
341
+
342
+ data_shape = (len(overall_file['Player']), total_sims) # Example: 1000 rows
343
+ salary_array = np_zeros(data_shape)
344
+ sim_array = np_zeros(data_shape)
345
+
346
+ for x in range(0, total_sims):
347
+ result_gpu = overall_salary_gpu
348
+ salary_array[:, x] = result_gpu
349
+ cupy_array = salary_array
350
+
351
+ salary_file = salary_file.reset_index(drop=True)
352
+ salary_cupy = DataFrame(cupy_array, columns=list(range(0, total_sims)))
353
+ salary_check_file = pd_concat([salary_file, salary_cupy], axis=1)
354
+ except:
355
+ for x in range(0,total_sims):
356
+ salary_file[x] = salary_file['Salary']
357
+ salary_check_file = salary_file.copy()
358
+
359
+ salary_file=salary_check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
360
+
361
+ salary_file = salary_file.div(1000)
362
+
363
+ try:
364
+ for x in range(0, total_sims):
365
+ if distribution_type == 'normal':
366
+ # Normal distribution (existing logic)
367
+ result_gpu = np_random.normal(overall_median_gpu, overall_std_gpu)
368
+ elif distribution_type == 'poisson':
369
+ # Poisson distribution - using median as lambda
370
+ result_gpu = np_random.poisson(overall_median_gpu)
371
+ elif distribution_type == 'bimodal':
372
+ # Bimodal distribution - mixture of two normal distributions
373
+ # First peak centered at 80% of median, second at 120% of median
374
+ if np_random.random() < 0.5:
375
+ result_gpu = np_random.normal(overall_floor_gpu, overall_std_gpu)
376
+ else:
377
+ result_gpu = np_random.normal(overall_ceiling_gpu, overall_std_gpu)
378
+ else:
379
+ raise ValueError("Invalid distribution type. Must be 'normal', 'poisson', or 'bimodal'")
380
+
381
+ sim_array[:, x] = result_gpu
382
+ add_array = sim_array
383
+
384
+ overall_file = overall_file.reset_index(drop=True)
385
+ df2 = DataFrame(add_array, columns=list(range(0, total_sims)))
386
+ check_file = pd_concat([overall_file, df2], axis=1)
387
+ except:
388
+ for x in range(0,total_sims):
389
+ if distribution_type == 'normal':
390
+ overall_file[x] = np_random.normal(overall_file['Median'], overall_file['STD'])
391
+ elif distribution_type == 'poisson':
392
+ overall_file[x] = np_random.poisson(overall_file['Median'])
393
+ elif distribution_type == 'bimodal':
394
+ # Bimodal distribution fallback
395
+ if np_random.random() < 0.5:
396
+ overall_file[x] = np_random.normal(overall_file['Floor'], overall_file['STD'])
397
+ else:
398
+ overall_file[x] = np_random.normal(overall_file['Ceiling'], overall_file['STD'])
399
+ check_file = overall_file.copy()
400
+
401
+ overall_file=check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
402
+
403
+ players_only = hold_file[['Player']]
404
+ raw_lineups_file = players_only
405
+
406
+ for x in range(0,total_sims):
407
+ maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
408
+ raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
409
+ players_only[x] = raw_lineups_file[x].rank(ascending=False)
410
+
411
+ players_only=players_only.drop(['Player'], axis=1)
412
+
413
+ salary_4x_check = (overall_file - (salary_file*4))
414
+ salary_5x_check = (overall_file - (salary_file*5))
415
+ salary_6x_check = (overall_file - (salary_file*6))
416
+ gpp_check = (overall_file - ((salary_file*5)+10))
417
+
418
+ players_only['Average_Rank'] = players_only.mean(axis=1)
419
+ players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
420
+ players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
421
+ players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
422
+ players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
423
+ players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
424
+ players_only['5x%'] = salary_5x_check[salary_5x_check >= 1].count(axis=1)/float(total_sims)
425
+ players_only['6x%'] = salary_6x_check[salary_6x_check >= 1].count(axis=1)/float(total_sims)
426
+ players_only['GPP%'] = gpp_check[gpp_check >= 1].count(axis=1)/float(total_sims)
427
+
428
+ players_only['Player'] = hold_file[['Player']]
429
+
430
+ final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
431
+
432
+ final_Proj = pd_merge(hold_file, final_outcomes, on="Player")
433
+ final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
434
+
435
+ final_Proj['name_team'] = final_Proj['Player'] + final_Proj['Position']
436
+ final_Proj['Own'] = final_Proj['Player'].map(own_dict)
437
+ final_Proj['Small_Own'] = final_Proj['Player'].map(small_own_dict)
438
+ final_Proj['Large_Own'] = final_Proj['Player'].map(large_own_dict)
439
+ final_Proj['Cash_Own'] = final_Proj['Player'].map(cash_own_dict)
440
+ final_Proj['CPT_Own'] = final_Proj['Own'] / 6
441
+ final_Proj['LevX'] = ((final_Proj[['Top_finish', '6x%', 'Top_5_finish']].mean(axis=1))*100) - final_Proj['Own']
442
+ final_Proj['ValX'] = ((final_Proj[['5x%', '6x%']].mean(axis=1))*100) + final_Proj['LevX']
443
+
444
+ final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%', 'Own', 'Small_Own', 'Large_Own', 'Cash_Own', 'CPT_Own', 'LevX', 'ValX']]
445
+ final_Proj['Salary'] = final_Proj['Salary'].astype(int)
446
+ final_Proj = final_Proj.sort_values(by='Median', ascending=False)
447
+
448
+ return final_Proj.copy()