James McCool commited on
Commit
c188e02
·
1 Parent(s): a47ccc4

Add comprehensive NHL prop betting Streamlit application with data simulation and analysis

Browse files
Files changed (1) hide show
  1. app.py +433 -0
app.py ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ st.set_page_config(layout="wide")
3
+
4
+ for name in dir():
5
+ if not name.startswith('_'):
6
+ del globals()[name]
7
+
8
+ import pulp
9
+ import numpy as np
10
+ from numpy import where as np_where
11
+ import pandas as pd
12
+ import streamlit as st
13
+ import pymongo
14
+ import scipy.stats as stats
15
+
16
+ @st.cache_resource
17
+ def init_conn():
18
+ uri = st.secrets['mongo_uri']
19
+ client = pymongo.MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=500000)
20
+ db = client["NHL_Database"]
21
+
22
+ return db
23
+
24
+ db = init_conn()
25
+
26
+ prop_table_options = ['NHL_GAME_PLAYER_SHOTS_ON_GOAL', 'NHL_GAME_PLAYER_POINTS', 'NHL_GAME_PLAYER_SHOTS_BLOCKED', 'NHL_GAME_PLAYER_ASSISTS']
27
+ prop_format = {'L5 Success': '{:.2%}', 'L10_Success': '{:.2%}', 'L20_success': '{:.2%}', 'Matchup Boost': '{:.2%}', 'Trending Over': '{:.2%}', 'Trending Under': '{:.2%}',
28
+ 'Implied Over': '{:.2%}', 'Implied Under': '{:.2%}', 'Over Edge': '{:.2%}', 'Under Edge': '{:.2%}'}
29
+ all_sim_vars = ['NHL_GAME_PLAYER_SHOTS_ON_GOAL', 'NHL_GAME_PLAYER_POINTS', 'NHL_GAME_PLAYER_SHOTS_BLOCKED', 'NHL_GAME_PLAYER_ASSISTS']
30
+ pick6_sim_vars = ['Points', 'Shots on Goal', 'Assists', 'Blocks']
31
+ sim_all_hold = pd.DataFrame(columns=['Player', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge'])
32
+
33
+ @st.cache_resource(ttl=200)
34
+ def pull_baselines():
35
+ collection = db["Player_Level_ROO"]
36
+ cursor = collection.find()
37
+ raw_display = pd.DataFrame(cursor)
38
+ prop_display = raw_display[raw_display['Player'] != ""]
39
+ prop_display['Player Blocks'].replace("", np.nan, inplace=True)
40
+ prop_display['SOG Edge'].replace("", np.nan, inplace=True)
41
+ prop_display['Assist Edge'].replace("", np.nan, inplace=True)
42
+ prop_display['TP Edge'].replace("", np.nan, inplace=True)
43
+ prop_table = prop_display[['Player', 'Position', 'Team', 'Opp', 'Team_Total', 'Player SOG', 'Player Goals', 'Player Assists',
44
+ 'Player TP', 'Player Blocks', 'Player Saves']]
45
+ prop_table['Player'].replace(['JJ Peterka', 'Alexander Killorn', 'Matt Boldy', 'Nick Paul', 'Alex Kerfoot'],
46
+ ['John-Jason Peterka', 'Alex Killorn', 'Matthew Boldy', 'Nicholas Paul', 'Alexander Kerfoot'], inplace=True)
47
+ prop_table['Player'] = prop_table['Player'].str.strip()
48
+
49
+ stat_columns = ['Team_Total', 'Player SOG', 'Player Goals', 'Player Assists', 'Player TP', 'Player Blocks', 'Player Saves']
50
+ for stat in stat_columns:
51
+ prop_table[stat] = prop_table[stat].astype(float)
52
+
53
+ collection = db["prop_trends"]
54
+ cursor = collection.find()
55
+ raw_display = pd.DataFrame(cursor)
56
+ raw_display.replace('', np.nan, inplace=True)
57
+ prop_trends = raw_display.dropna(subset='Player')
58
+ prop_trends['Player'].replace(['JJ Peterka', 'Alexander Killorn', 'Matt Boldy', 'Nick Paul', 'Alex Kerfoot'],
59
+ ['John-Jason Peterka', 'Alex Killorn', 'Matthew Boldy', 'Nicholas Paul', 'Alexander Kerfoot'], inplace=True)
60
+
61
+ collection = db["Pick6_ingest"]
62
+ cursor = collection.find()
63
+ raw_display = pd.DataFrame(cursor)
64
+ raw_display.replace('', np.nan, inplace=True)
65
+ pick_frame = raw_display.dropna(subset='Player')
66
+ pick_frame['Player'].replace(['JJ Peterka', 'Alexander Killorn', 'Matt Boldy', 'Nick Paul', 'Alex Kerfoot'],
67
+ ['John-Jason Peterka', 'Alex Killorn', 'Matthew Boldy', 'Nicholas Paul', 'Alexander Kerfoot'], inplace=True)
68
+
69
+ team_dict = dict(zip(prop_table['Player'], prop_table['Team']))
70
+
71
+ return prop_table, prop_trends, pick_frame, team_dict
72
+
73
+ def calculate_poisson(row):
74
+ mean_val = row['Mean_Outcome']
75
+ threshold = row['Prop']
76
+ cdf_value = stats.poisson.cdf(threshold, mean_val)
77
+ probability = 1 - cdf_value
78
+ return probability
79
+
80
+ def convert_df_to_csv(df):
81
+ return df.to_csv().encode('utf-8')
82
+
83
+ prop_display, prop_trends, pick_frame, team_dict = pull_baselines()
84
+
85
+ tab1, tab2, tab3 = st.tabs(["Player Stat Table", 'Prop Trend Table', 'Stat Specific Simulations'])
86
+
87
+ with tab1:
88
+ if st.button("Reset Data", key='reset1'):
89
+ st.cache_data.clear()
90
+ prop_display, prop_trends, pick_frame, team_dict = pull_baselines()
91
+ prop_frame = prop_display
92
+ st.dataframe(prop_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True)
93
+
94
+ st.download_button(
95
+ label="Export Table",
96
+ data=convert_df_to_csv(prop_frame),
97
+ file_name='NHL_prop_stat_export.csv',
98
+ mime='text/csv',
99
+ key='prop_export',
100
+ )
101
+
102
+ with tab2:
103
+ if st.button("Reset Data", key='reset3'):
104
+ st.cache_data.clear()
105
+ prop_display, prop_trends, pick_frame, team_dict = pull_baselines()
106
+ split_var5 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var5')
107
+ if split_var5 == 'Specific Teams':
108
+ team_var5 = st.multiselect('Which teams would you like to include in the tables?', options = prop_trends['Team'].unique(), key='team_var5')
109
+ elif split_var5 == 'All':
110
+ team_var5 = prop_trends.Team.values.tolist()
111
+ book_split5 = st.radio("Would you like to view all books or specific ones?", ('All', 'Specific Books'), key='book_split5')
112
+ if book_split5 == 'Specific Books':
113
+ book_var5 = st.multiselect('Which books would you like to include in the tables?', options = ['BET_365', 'DRAFTKINGS', 'CONSENSUS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL'], key='book_var5')
114
+ elif book_split5 == 'All':
115
+ book_var5 = ['BET_365', 'DRAFTKINGS', 'CONSENSUS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL']
116
+ prop_type_var2 = st.selectbox('Select type of prop are you wanting to view', options = prop_table_options)
117
+ prop_frame_disp = prop_trends[prop_trends['Team'].isin(team_var5)]
118
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['book'].isin(book_var5)]
119
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['prop_type'] == prop_type_var2]
120
+ prop_frame_disp = prop_frame_disp.sort_values(by='Trending Over', ascending=False)
121
+ st.dataframe(prop_frame_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(prop_format, precision=2), use_container_width = True)
122
+ st.download_button(
123
+ label="Export Prop Trends Model",
124
+ data=convert_df_to_csv(prop_frame_disp),
125
+ file_name='NHL_prop_trends_export.csv',
126
+ mime='text/csv',
127
+ )
128
+
129
+ with tab3:
130
+ st.info('The Over and Under percentages are a composite percentage based on simulations, historical performance, and implied probabilities, and may be different than you would expect based purely on the median projection. Likewise, the Edge of a bet is not the only indicator of if you should make the bet or not as the suggestion is using a base acceptable threshold to determine how much edge you should have for each stat category.')
131
+ if st.button("Reset Data/Load Data", key='reset5'):
132
+ st.cache_data.clear()
133
+ prop_display, prop_trends, pick_frame, team_dict = pull_baselines()
134
+
135
+ settings_container = st.container()
136
+ df_hold_container = st.empty()
137
+ export_container = st.empty()
138
+
139
+ with settings_container.container():
140
+ col1, col2, col3, col4 = st.columns([3, 3, 3, 3])
141
+ with col1:
142
+ game_select_var = st.selectbox('Select prop source', options = ['Aggregate', 'Pick6'])
143
+ with col2:
144
+ book_select_var = st.selectbox('Select book', options = ['ALL', 'BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL'])
145
+ if book_select_var == 'ALL':
146
+ book_selections = ['BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL']
147
+ else:
148
+ book_selections = [book_select_var]
149
+ if game_select_var == 'Aggregate':
150
+ prop_df = prop_trends[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
151
+ elif game_select_var == 'Pick6':
152
+ prop_df = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
153
+ book_selections = ['Pick6']
154
+ with col3:
155
+ if game_select_var == 'Aggregate':
156
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'NHL_GAME_PLAYER_POINTS', 'NHL_GAME_PLAYER_SHOTS_ON_GOAL', 'NHL_GAME_PLAYER_ASSISTS', 'NHL_GAME_PLAYER_SHOTS_BLOCKED'])
157
+ elif game_select_var == 'Pick6':
158
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'Points', 'Shots on Goal', 'Assists', 'Blocks'])
159
+ with col4:
160
+ st.download_button(
161
+ label="Download Prop Source",
162
+ data=convert_df_to_csv(prop_df),
163
+ file_name='NHL_prop_source.csv',
164
+ mime='text/csv',
165
+ key='prop_source',
166
+ )
167
+
168
+ if st.button('Simulate Prop Category'):
169
+ with df_hold_container.container():
170
+ if prop_type_var == 'All Props':
171
+
172
+ if game_select_var == 'Aggregate':
173
+ prop_df_raw = prop_trends[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
174
+ sim_vars = ['NHL_GAME_PLAYER_POINTS', 'NHL_GAME_PLAYER_SHOTS_ON_GOAL', 'NHL_GAME_PLAYER_ASSISTS', 'NHL_GAME_PLAYER_SHOTS_BLOCKED']
175
+ elif game_select_var == 'Pick6':
176
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
177
+ sim_vars = ['Points', 'Shots on Goal', 'Assists', 'Blocks']
178
+
179
+ player_df = prop_display.copy()
180
+
181
+ for prop in sim_vars:
182
+
183
+ for books in book_selections:
184
+ prop_df = prop_df_raw[prop_df_raw['prop_type'] == prop]
185
+ prop_df = prop_df[prop_df['book'] == books]
186
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
187
+ prop_df.rename(columns={"over_prop": "Prop"}, inplace = True)
188
+ prop_df['Over'] = 1 / prop_df['over_line']
189
+ prop_df['Under'] = 1 / prop_df['under_line']
190
+
191
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
192
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
193
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
194
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
195
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
196
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
197
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
198
+
199
+ player_df['book'] = player_df['Player'].map(book_dict)
200
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
201
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
202
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
203
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
204
+
205
+ df = player_df.reset_index(drop=True)
206
+
207
+ team_dict = dict(zip(df.Player, df.Team))
208
+
209
+ total_sims = 1000
210
+
211
+ df.replace("", 0, inplace=True)
212
+
213
+ if prop == 'NHL_GAME_PLAYER_POINTS' or prop == 'Points':
214
+ df['Median'] = df['Player TP']
215
+ elif prop == 'NHL_GAME_PLAYER_SHOTS_ON_GOAL' or prop == 'Shots on Goal':
216
+ df['Median'] = df['Player SOG']
217
+ elif prop == 'NHL_GAME_PLAYER_ASSISTS' or prop == 'Assists':
218
+ df['Median'] = df['Player Assists']
219
+ elif prop == 'NHL_GAME_PLAYER_SHOTS_BLOCKED' or prop == 'Blocks':
220
+ df['Median'] = df['Player Blocks']
221
+
222
+ flex_file = df.copy()
223
+ flex_file['Floor'] = (flex_file['Median'] * .15)
224
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1)
225
+ flex_file['STD'] = (flex_file['Median']/3)
226
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
227
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
228
+
229
+ hold_file = flex_file.copy()
230
+ overall_file = flex_file.copy()
231
+ prop_file = flex_file.copy()
232
+
233
+ overall_players = overall_file[['Player']]
234
+
235
+ for x in range(0,total_sims):
236
+ prop_file[x] = prop_file['Prop']
237
+
238
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
239
+
240
+ for x in range(0,total_sims):
241
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
242
+
243
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
244
+
245
+ players_only = hold_file[['Player']]
246
+
247
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
248
+
249
+ prop_check = (overall_file - prop_file)
250
+
251
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
252
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
253
+ players_only['Book'] = players_only['Player'].map(book_dict)
254
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
255
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
256
+ players_only['over_adj'] = np_where((players_only['Mean_Outcome'] - players_only['Prop']) > 0, 1, (players_only['Mean_Outcome'] / players_only['Prop']))
257
+ players_only['under_adj'] = np_where((players_only['Prop'] - players_only['Mean_Outcome']) > 0, 1, (players_only['Prop'] / players_only['Mean_Outcome']))
258
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
259
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
260
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
261
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
262
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
263
+ players_only['Over%'] = (players_only['Over'] * 0.4) + (players_only['Trending Over'] * 0.4) + (players_only['Imp Over'] * 0.2)
264
+ players_only['Under'] = np_where(players_only['Prop'] <= 3, 1 - players_only['poisson_var'], prop_check[prop_check < 0].count(axis=1)/float(total_sims))
265
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
266
+ players_only['Under%'] = (players_only['Under'] * 0.4) + (players_only['Trending Under'] * 0.4) + (players_only['Imp Under'] * 0.2)
267
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
268
+ players_only['prop_threshold'] = .10
269
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
270
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
271
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
272
+ players_only['Bet_check'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], players_only['Over_diff'] * players_only['over_adj'], players_only['Under_diff'] * players_only['under_adj'])
273
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
274
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
275
+ players_only['Edge'] = players_only['Bet_check']
276
+ players_only['Prop Type'] = prop
277
+
278
+ players_only['Player'] = hold_file[['Player']]
279
+ players_only['Team'] = players_only['Player'].map(team_dict)
280
+
281
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
282
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
283
+
284
+ final_outcomes = sim_all_hold
285
+ st.write(f'finished {prop} for {books}')
286
+
287
+ elif prop_type_var != 'All Props':
288
+
289
+ player_df = prop_display.copy()
290
+
291
+ if game_select_var == 'Aggregate':
292
+ prop_df_raw = prop_trends[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
293
+ elif game_select_var == 'Pick6':
294
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
295
+
296
+ for books in book_selections:
297
+ prop_df = prop_df_raw[prop_df_raw['book'] == books]
298
+
299
+ if prop_type_var == "NHL_GAME_PLAYER_SHOTS_ON_GOAL":
300
+ prop_df = prop_df[prop_df['prop_type'] == 'NHL_GAME_PLAYER_SHOTS_ON_GOAL']
301
+ elif prop_type_var == 'Shots on Goal':
302
+ prop_df = prop_df[prop_df['prop_type'] == 'Player SOG']
303
+ elif prop_type_var == "NHL_GAME_PLAYER_POINTS":
304
+ prop_df = prop_df[prop_df['prop_type'] == 'NHL_GAME_PLAYER_POINTS']
305
+ elif prop_type_var == "Points":
306
+ prop_df = prop_df[prop_df['prop_type'] == 'Player TP']
307
+ elif prop_type_var == "NHL_GAME_PLAYER_ASSISTS":
308
+ prop_df = prop_df[prop_df['prop_type'] == 'NHL_GAME_PLAYER_ASSISTS']
309
+ elif prop_type_var == "Assists":
310
+ prop_df = prop_df[prop_df['prop_type'] == 'Player Assists']
311
+ elif prop_type_var == "NHL_GAME_PLAYER_SHOTS_BLOCKED":
312
+ prop_df = prop_df[prop_df['prop_type'] == 'NHL_GAME_PLAYER_SHOTS_BLOCKED']
313
+ elif prop_type_var == "Blocks":
314
+ prop_df = prop_df[prop_df['prop_type'] == 'Player Blocks']
315
+
316
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line']]
317
+ prop_df.rename(columns={"over_prop": "Prop"}, inplace = True)
318
+ prop_df['Over'] = 1 / prop_df['over_line']
319
+ prop_df['Under'] = 1 / prop_df['under_line']
320
+
321
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
322
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
323
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
324
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
325
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
326
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
327
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
328
+
329
+ player_df['book'] = player_df['Player'].map(book_dict)
330
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
331
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
332
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
333
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
334
+
335
+ df = player_df.reset_index(drop=True)
336
+
337
+ team_dict = dict(zip(df.Player, df.Team))
338
+
339
+ total_sims = 1000
340
+
341
+ df.replace("", 0, inplace=True)
342
+
343
+ if prop_type_var == 'NHL_GAME_PLAYER_POINTS' or prop_type_var == 'Points':
344
+ df['Median'] = df['Player TP']
345
+ elif prop_type_var == 'NHL_GAME_PLAYER_SHOTS_ON_GOAL' or prop_type_var == 'Shots on Goal':
346
+ df['Median'] = df['Player SOG']
347
+ elif prop_type_var == 'NHL_GAME_PLAYER_ASSISTS' or prop_type_var == 'Assists':
348
+ df['Median'] = df['Player Assists']
349
+ elif prop_type_var == 'NHL_GAME_PLAYER_SHOTS_BLOCKED' or prop_type_var == 'Blocks':
350
+ df['Median'] = df['Player Blocks']
351
+
352
+ flex_file = df.copy()
353
+ flex_file['Floor'] = (flex_file['Median'] * .15)
354
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1)
355
+ flex_file['STD'] = (flex_file['Median']/3)
356
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
357
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
358
+
359
+ hold_file = flex_file.copy()
360
+ overall_file = flex_file.copy()
361
+ prop_file = flex_file.copy()
362
+
363
+ overall_players = overall_file[['Player']]
364
+
365
+ for x in range(0,total_sims):
366
+ prop_file[x] = prop_file['Prop']
367
+
368
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
369
+
370
+ for x in range(0,total_sims):
371
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
372
+
373
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
374
+
375
+ players_only = hold_file[['Player']]
376
+
377
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
378
+
379
+ prop_check = (overall_file - prop_file)
380
+
381
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
382
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
383
+ players_only['Book'] = players_only['Player'].map(book_dict)
384
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
385
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
386
+ players_only['over_adj'] = np_where((players_only['Mean_Outcome'] - players_only['Prop']) > 0, 1, (players_only['Mean_Outcome'] / players_only['Prop']))
387
+ players_only['under_adj'] = np_where((players_only['Prop'] - players_only['Mean_Outcome']) > 0, 1, (players_only['Prop'] / players_only['Mean_Outcome']))
388
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
389
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
390
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
391
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
392
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
393
+ players_only['Over%'] = (players_only['Over'] * 0.4) + (players_only['Trending Over'] * 0.4) + (players_only['Imp Over'] * 0.2)
394
+ players_only['Under'] = np_where(players_only['Prop'] <= 3, 1 - players_only['poisson_var'], prop_check[prop_check < 0].count(axis=1)/float(total_sims))
395
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
396
+ players_only['Under%'] = (players_only['Under'] * 0.4) + (players_only['Trending Under'] * 0.4) + (players_only['Imp Under'] * 0.2)
397
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
398
+ players_only['prop_threshold'] = .10
399
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
400
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
401
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
402
+ players_only['Bet_check'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], players_only['Over_diff'] * players_only['over_adj'], players_only['Under_diff'] * players_only['under_adj'])
403
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
404
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
405
+ players_only['Edge'] = players_only['Bet_check']
406
+ players_only['Prop Type'] = prop_type_var
407
+
408
+ players_only['Player'] = hold_file[['Player']]
409
+ players_only['Team'] = players_only['Player'].map(team_dict)
410
+
411
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop', 'Prop Type', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
412
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
413
+
414
+ final_outcomes = sim_all_hold
415
+ st.write(f'finished {prop_type_var} for {books}')
416
+
417
+ final_outcomes = final_outcomes[final_outcomes['Prop'] > 0]
418
+ if game_select_var == 'Pick6':
419
+ final_outcomes = final_outcomes.drop_duplicates(subset=['Player', 'Prop Type'])
420
+ final_outcomes = final_outcomes.sort_values(by='Edge', ascending=False)
421
+
422
+ with df_hold_container:
423
+ df_hold_container = st.empty()
424
+ st.dataframe(final_outcomes.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True)
425
+ with export_container:
426
+ export_container = st.empty()
427
+ st.download_button(
428
+ label="Export Projections",
429
+ data=convert_df_to_csv(final_outcomes),
430
+ file_name='NHL_prop_proj.csv',
431
+ mime='text/csv',
432
+ key='prop_proj',
433
+ )