James McCool commited on
Commit
46e0542
·
1 Parent(s): 5b0ea87

Add Streamlit NBA prop betting analysis app with MongoDB integration

Browse files
Files changed (3) hide show
  1. app.py +762 -0
  2. app.yaml +10 -0
  3. requirements.txt +9 -0
app.py ADDED
@@ -0,0 +1,762 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 numpy as np
9
+ from numpy import where as np_where
10
+ import pandas as pd
11
+ import pymongo
12
+ import random
13
+ import gc
14
+ import scipy.stats as stats
15
+ from datetime import datetime
16
+
17
+ @st.cache_resource
18
+ def init_conn():
19
+
20
+ uri = st.secrets['mongo_uri']
21
+ client = pymongo.MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=100000)
22
+ db = client["NBA_DFS"]
23
+ prop_db = client["Props_DB"]
24
+
25
+ return db, prop_db
26
+
27
+ db, prop_db = init_conn()
28
+
29
+ game_format = {'Paydirt Win%': '{:.2%}', 'Vegas Win%': '{:.2%}'}
30
+ prop_format = {'L5 Success': '{:.2%}', 'L10_Success': '{:.2%}', 'L20_success': '{:.2%}', 'Matchup Boost': '{:.2%}', 'Trending Over': '{:.2%}', 'Trending Under': '{:.2%}',
31
+ 'Implied Over': '{:.2%}', 'Implied Under': '{:.2%}', 'Over Edge': '{:.2%}', 'Under Edge': '{:.2%}'}
32
+ sim_format = {'Trending Over': '{:.2%}', 'Trending Under': '{:.2%}', 'Imp Over': '{:.2%}', 'Imp Under': '{:.2%}', 'Over%': '{:.2%}', 'Under%': '{:.2%}', 'Edge': '{:.2%}'}
33
+ prop_table_options = ['NBA_GAME_PLAYER_POINTS', 'NBA_GAME_PLAYER_REBOUNDS', 'NBA_GAME_PLAYER_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS', 'NBA_GAME_PLAYER_POINTS_ASSISTS', 'NBA_GAME_PLAYER_REBOUNDS_ASSISTS']
34
+ all_sim_vars = ['NBA_GAME_PLAYER_POINTS', 'NBA_GAME_PLAYER_REBOUNDS', 'NBA_GAME_PLAYER_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS', 'NBA_GAME_PLAYER_POINTS_ASSISTS', 'NBA_GAME_PLAYER_REBOUNDS_ASSISTS']
35
+ pick6_sim_vars = ['Points', 'Rebounds', 'Assists', 'Points + Assists + Rebounds', 'Points + Assists', 'Points + Rebounds', 'Assists + Rebounds']
36
+ sim_all_hold = pd.DataFrame(columns=['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge'])
37
+
38
+ def calculate_poisson(row):
39
+ mean_val = row['Mean_Outcome']
40
+ threshold = row['Prop']
41
+ cdf_value = stats.poisson.cdf(threshold, mean_val)
42
+ probability = 1 - cdf_value
43
+ return probability
44
+
45
+ def add_column(df):
46
+ return_df = df
47
+ return_df['2P'] = return_df["Minutes"] * return_df["FG2M"]
48
+ return_df['3P'] = return_df["Minutes"] * return_df["Threes"]
49
+ return_df['FT'] = return_df["Minutes"] * return_df["FTM"]
50
+ return_df['Points'] = (return_df["2P"] * 2) + (return_df["3P"] * 3) + return_df['FT']
51
+ return_df['Rebounds'] = return_df["Minutes"] * return_df["TRB"]
52
+ return_df['Assists'] = return_df["Minutes"] * return_df["AST"]
53
+ return_df['PRA'] = return_df['Points'] + return_df['Rebounds'] + return_df['Assists']
54
+ return_df['PR'] = return_df['Points'] + return_df['Rebounds']
55
+ return_df['PA'] = return_df['Points'] + return_df['Assists']
56
+ return_df['RA'] = return_df['Rebounds'] + return_df['Assists']
57
+ return_df['Steals'] = return_df["Minutes"] * return_df["STL"]
58
+ return_df['Blocks'] = return_df["Minutes"] * return_df["BLK"]
59
+ return_df['Turnovers'] = return_df["Minutes"] * return_df["TOV"]
60
+ return_df['Fantasy'] = (return_df["2P"] * 3) + (return_df["3P"] * 3.5) + return_df['FT'] + (return_df["Rebounds"] * 1.25) + (return_df["Assists"] * 1.5) + (return_df["Steals"] * 2) + (return_df["Blocks"] * 2) + (return_df["Turnovers"] * -.5)
61
+
62
+ export_df = return_df[['Player', 'Position', 'Team', 'Opp', 'Minutes', '2P', '3P', 'FT', 'Points', 'Rebounds', 'Assists', 'PRA', 'PR', 'PA', 'RA', 'Steals', 'Blocks', 'Turnovers', 'Fantasy']]
63
+
64
+ return export_df
65
+
66
+ @st.cache_resource(ttl = 300)
67
+ def init_baselines():
68
+ collection = db["Game_Betting_Model"]
69
+ cursor = collection.find()
70
+
71
+ raw_display = pd.DataFrame(list(cursor))
72
+ raw_display = raw_display[['Team', 'Opp', 'PD Team Points', 'PD Opp Points', 'VEG Team Points', 'VEG Opp Points', 'PD Proj Total', 'VEG Proj Total', 'PD Over%', 'PD Over Odds', 'PD Under%', 'PD Under Odds',
73
+ 'PD Proj Winner', 'PD Proj Spread', 'PD W Spread', 'VEG W Spread', 'PD Win%', 'PD Odds']]
74
+ raw_display.replace('#DIV/0!', np.nan, inplace=True)
75
+ game_model = raw_display.dropna()
76
+
77
+ collection = db["Player_Stats"]
78
+ cursor = collection.find()
79
+
80
+ raw_display = pd.DataFrame(list(cursor))
81
+ raw_display.replace('', np.nan, inplace=True)
82
+ raw_display = raw_display.rename(columns={"Name": "Player"})
83
+ raw_baselines = raw_display[['Player', 'Position', 'Team', 'Opp', 'Minutes', 'FGM', 'FGA', 'FG2M', 'FG2A', 'Threes', 'FG3A', 'FTM', 'FTA', 'TRB', 'AST', 'STL', 'BLK', 'TOV', 'PRA', 'PR', 'PA', 'RA']]
84
+ raw_baselines = raw_baselines[raw_baselines['Minutes'] > 0]
85
+ raw_baselines['Player'].replace(['Jaren Jackson', 'Nic Claxton', 'Jabari Smith', 'Lu Dort', 'Moe Wagner', 'Kyle Kuzma', 'Trey Murphy', 'Cameron Thomas'],
86
+ ['Jaren Jackson Jr.', 'Nicolas Claxton', 'Jabari Smith Jr.', 'Luguentz Dort', 'Moritz Wagner', 'Kyle Kuzma Jr.',
87
+ 'Trey Murphy III', 'Cam Thomas'], inplace=True)
88
+
89
+ player_stats = raw_display[['Player', 'Position', 'Team', 'Opp', 'Minutes', '3P', 'Points', 'Rebounds', 'Assists', 'Steals', 'Blocks', 'Turnovers', 'Fantasy']]
90
+ player_stats = player_stats[player_stats['Minutes'] > 0]
91
+
92
+ player_stats['Player'].replace(['Jaren Jackson', 'Nic Claxton', 'Jabari Smith', 'Lu Dort', 'Moe Wagner', 'Kyle Kuzma', 'Trey Murphy', 'Cameron Thomas'],
93
+ ['Jaren Jackson Jr.', 'Nicolas Claxton', 'Jabari Smith Jr.', 'Luguentz Dort', 'Moritz Wagner', 'Kyle Kuzma Jr.',
94
+ 'Trey Murphy III', 'Cam Thomas'], inplace=True)
95
+
96
+
97
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
98
+
99
+ collection = db["Prop_Trends"]
100
+ cursor = collection.find()
101
+
102
+ raw_display = pd.DataFrame(list(cursor))
103
+ raw_display.replace('', np.nan, inplace=True)
104
+ raw_display = raw_display[['Name', 'over_prop', 'over_line', 'under_prop', 'under_line', 'OddsType', 'PropType', 'No Vig', 'Team', 'L5 Success', 'L10_Success', 'L20_success', 'L10 Avg', 'Projection',
105
+ 'Proj Diff', 'Matchup Boost', 'Implied Over', 'Trending Over', 'Over Edge', 'Implied Under', 'Trending Under', 'Under Edge']]
106
+ raw_display = raw_display.rename(columns={"Name": "Player", "OddsType": "book", "PropType": "prop_type"})
107
+ prop_frame = raw_display.dropna(subset='Player')
108
+
109
+ collection = db["Pick6_Trends"]
110
+ cursor = collection.find()
111
+
112
+ raw_display = pd.DataFrame(list(cursor))
113
+ raw_display = raw_display[['Player', 'over_prop', 'over_line', 'under_prop', 'under_line', 'book', 'prop_type', 'No Vig', 'Team', 'L5 Success', 'L10_Success', 'L20_success', 'L10 Avg', 'Projection',
114
+ 'Proj Diff', 'Matchup Boost', 'Implied Over', 'Trending Over', 'Over Edge', 'Implied Under', 'Trending Under', 'Under Edge']]
115
+ pick_frame = raw_display.drop_duplicates(subset=['Player', 'prop_type'], keep='first')
116
+ pick_frame = pick_frame.reset_index(drop=True)
117
+
118
+ prop_frame['Player'].replace(['Jaren Jackson', 'Nic Claxton', 'Jabari Smith', 'Lu Dort', 'Moe Wagner', 'Kyle Kuzma', 'Trey Murphy', 'Cameron Thomas'],
119
+ ['Jaren Jackson Jr.', 'Nicolas Claxton', 'Jabari Smith Jr.', 'Luguentz Dort', 'Moritz Wagner', 'Kyle Kuzma Jr.',
120
+ 'Trey Murphy III', 'Cam Thomas'], inplace=True)
121
+ pick_frame['Player'].replace(['Jaren Jackson', 'Nic Claxton', 'Jabari Smith', 'Lu Dort', 'Moe Wagner', 'Kyle Kuzma', 'Trey Murphy', 'Cameron Thomas'],
122
+ ['Jaren Jackson Jr.', 'Nicolas Claxton', 'Jabari Smith Jr.', 'Luguentz Dort', 'Moritz Wagner', 'Kyle Kuzma Jr.',
123
+ 'Trey Murphy III', 'Cam Thomas'], inplace=True)
124
+
125
+ collection = prop_db["NBA_Props"]
126
+ cursor = collection.find()
127
+
128
+ raw_display = pd.DataFrame(list(cursor))
129
+ market_props = raw_display[['Name', 'Position', 'Projection', 'PropType', 'OddsType', 'over_pay', 'under_pay']]
130
+ market_props['over_prop'] = market_props['Projection']
131
+ market_props['over_line'] = market_props['over_pay'].apply(lambda x: (x - 1) * 100 if x >= 2.0 else -100 / (x - 1))
132
+ market_props['under_prop'] = market_props['Projection']
133
+ market_props['under_line'] = market_props['under_pay'].apply(lambda x: (x - 1) * 100 if x >= 2.0 else -100 / (x - 1))
134
+
135
+ return game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp
136
+
137
+ def calculate_no_vig(row):
138
+ def implied_probability(american_odds):
139
+ if american_odds < 0:
140
+ return (-american_odds) / ((-american_odds) + 100)
141
+ else:
142
+ return 100 / (american_odds + 100)
143
+
144
+ over_line = row['over_line']
145
+ under_line = row['under_line']
146
+ over_prop = row['over_prop']
147
+
148
+ over_prob = implied_probability(over_line)
149
+ under_prob = implied_probability(under_line)
150
+
151
+ total_prob = over_prob + under_prob
152
+ no_vig_prob = (over_prob / total_prob + 0.5) * over_prop
153
+
154
+ return no_vig_prob
155
+
156
+ def convert_df_to_csv(df):
157
+ return df.to_csv().encode('utf-8')
158
+
159
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
160
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
161
+
162
+ tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(["Game Betting Model", 'Prop Market', "Player Projections", "Prop Trend Table", "Player Prop Simulations", "Stat Specific Simulations"])
163
+
164
+ with tab1:
165
+ st.info(t_stamp)
166
+ if st.button("Reset Data", key='reset1'):
167
+ st.cache_data.clear()
168
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
169
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
170
+ line_var1 = st.radio('How would you like to display odds?', options = ['Percentage', 'American'], key='line_var1')
171
+ team_frame = game_model
172
+ if line_var1 == 'Percentage':
173
+ team_frame = team_frame[['Team', 'Opp', 'PD Team Points', 'PD Opp Points', 'VEG Team Points', 'VEG Opp Points', 'PD Proj Total', 'VEG Proj Total', 'PD Over%', 'PD Under%', 'PD Proj Winner', 'PD Proj Spread', 'PD W Spread', 'VEG W Spread', 'PD Win%']]
174
+ team_frame = team_frame.set_index('Team')
175
+ st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(game_format, precision=2), use_container_width = True)
176
+ if line_var1 == 'American':
177
+ team_frame = team_frame[['Team', 'Opp', 'PD Team Points', 'PD Opp Points', 'VEG Team Points', 'VEG Opp Points', 'PD Proj Total', 'VEG Proj Total', 'PD Over Odds', 'PD Under Odds', 'PD Proj Winner', 'PD Proj Spread', 'PD W Spread', 'VEG W Spread', 'PD Odds']]
178
+ team_frame = team_frame.set_index('Team')
179
+ st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(game_format, precision=2), use_container_width = True)
180
+
181
+ st.download_button(
182
+ label="Export Team Model",
183
+ data=convert_df_to_csv(team_frame),
184
+ file_name='NBA_team_betting_export.csv',
185
+ mime='text/csv',
186
+ key='team_export',
187
+ )
188
+
189
+ with tab2:
190
+ st.info(t_stamp)
191
+ if st.button("Reset Data", key='reset2'):
192
+ st.cache_data.clear()
193
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
194
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
195
+ market_type = st.selectbox('Select type of prop are you wanting to view', options = prop_table_options, key = 'market_type_key')
196
+ disp_market = market_props.copy()
197
+ disp_market = disp_market[disp_market['PropType'] == market_type]
198
+ disp_market['No_Vig_Prop'] = disp_market.apply(calculate_no_vig, axis=1)
199
+ fanduel_frame = disp_market[disp_market['OddsType'] == 'FANDUEL']
200
+ fanduel_dict = dict(zip(fanduel_frame['Name'], fanduel_frame['No_Vig_Prop']))
201
+ draftkings_frame = disp_market[disp_market['OddsType'] == 'DRAFTKINGS']
202
+ draftkings_dict = dict(zip(draftkings_frame['Name'], draftkings_frame['No_Vig_Prop']))
203
+ mgm_frame = disp_market[disp_market['OddsType'] == 'MGM']
204
+ mgm_dict = dict(zip(mgm_frame['Name'], mgm_frame['No_Vig_Prop']))
205
+ bet365_frame = disp_market[disp_market['OddsType'] == 'BET_365']
206
+ bet365_dict = dict(zip(bet365_frame['Name'], bet365_frame['No_Vig_Prop']))
207
+
208
+ disp_market['FANDUEL'] = disp_market['Name'].map(fanduel_dict)
209
+ disp_market['DRAFTKINGS'] = disp_market['Name'].map(draftkings_dict)
210
+ disp_market['MGM'] = disp_market['Name'].map(mgm_dict)
211
+ disp_market['BET365'] = disp_market['Name'].map(bet365_dict)
212
+
213
+ disp_market = disp_market[['Name', 'Position','FANDUEL', 'DRAFTKINGS', 'MGM', 'BET365']]
214
+ disp_market = disp_market.drop_duplicates(subset=['Name'], keep='first', ignore_index=True)
215
+
216
+ st.dataframe(disp_market.style.background_gradient(axis=1, subset=['FANDUEL', 'DRAFTKINGS', 'MGM', 'BET365'], cmap='RdYlGn').format(prop_format, precision=2), height = 1000, use_container_width = True)
217
+ st.download_button(
218
+ label="Export Market Props",
219
+ data=convert_df_to_csv(disp_market),
220
+ file_name='NFL_market_props_export.csv',
221
+ mime='text/csv',
222
+ )
223
+
224
+ with tab3:
225
+ st.info(t_stamp)
226
+ if st.button("Reset Data", key='reset3'):
227
+ st.cache_data.clear()
228
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
229
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
230
+ split_var1 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var1')
231
+ if split_var1 == 'Specific Teams':
232
+ team_var1 = st.multiselect('Which teams would you like to include in the tables?', options = player_stats['Team'].unique(), key='team_var1')
233
+ elif split_var1 == 'All':
234
+ team_var1 = player_stats.Team.values.tolist()
235
+ player_stats = player_stats[player_stats['Team'].isin(team_var1)]
236
+ player_stats_disp = player_stats.set_index('Player')
237
+ player_stats_disp = player_stats_disp.sort_values(by='Fantasy', ascending=False)
238
+ st.dataframe(player_stats_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True)
239
+ st.download_button(
240
+ label="Export Prop Model",
241
+ data=convert_df_to_csv(player_stats),
242
+ file_name='NBA_stats_export.csv',
243
+ mime='text/csv',
244
+ )
245
+
246
+ with tab4:
247
+ st.info(t_stamp)
248
+ if st.button("Reset Data", key='reset4'):
249
+ st.cache_data.clear()
250
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
251
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
252
+ split_var5 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var5')
253
+ if split_var5 == 'Specific Teams':
254
+ team_var5 = st.multiselect('Which teams would you like to include in the tables?', options = player_stats['Team'].unique(), key='team_var5')
255
+ elif split_var5 == 'All':
256
+ team_var5 = player_stats.Team.values.tolist()
257
+ book_split5 = st.radio("Would you like to view all books or specific ones?", ('All', 'Specific Books'), key='book_split5')
258
+ if book_split5 == 'Specific Books':
259
+ 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')
260
+ elif book_split5 == 'All':
261
+ book_var5 = ['BET_365', 'DRAFTKINGS', 'CONSENSUS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL']
262
+ prop_type_var2 = st.selectbox('Select type of prop are you wanting to view', options = prop_table_options)
263
+ prop_frame_disp = prop_frame[prop_frame['Team'].isin(team_var5)]
264
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['book'].isin(book_var5)]
265
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['prop_type'] == prop_type_var2]
266
+ prop_frame_disp = prop_frame_disp.sort_values(by='Trending Over', ascending=False)
267
+ st.dataframe(prop_frame_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(prop_format, precision=2), use_container_width = True)
268
+ st.download_button(
269
+ label="Export Prop Trends Model",
270
+ data=convert_df_to_csv(prop_frame),
271
+ file_name='NBA_prop_trends_export.csv',
272
+ mime='text/csv',
273
+ )
274
+
275
+ with tab5:
276
+ st.info(t_stamp)
277
+ if st.button("Reset Data", key='reset5'):
278
+ st.cache_data.clear()
279
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
280
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
281
+ col1, col2 = st.columns([1, 5])
282
+
283
+ with col2:
284
+ df_hold_container = st.empty()
285
+ info_hold_container = st.empty()
286
+ plot_hold_container = st.empty()
287
+
288
+ with col1:
289
+ player_check = st.selectbox('Select player to simulate props', options = player_stats['Player'].unique())
290
+ prop_type_var = st.selectbox('Select type of prop to simulate', options = ['points', 'threes', 'rebounds', 'assists', 'blocks', 'steals',
291
+ 'PRA', 'points+rebounds', 'points+assists', 'rebounds+assists'])
292
+
293
+ ou_var = st.selectbox('Select wether it is an over or under', options = ['Over', 'Under'])
294
+ if prop_type_var == 'points':
295
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 50.5, value = 15.5, step = .5)
296
+ elif prop_type_var == 'threes':
297
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 5.5, value = 1.5, step = .5)
298
+ elif prop_type_var == 'rebounds':
299
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 25.5, value = 5.5, step = .5)
300
+ elif prop_type_var == 'assists':
301
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 25.5, value = 5.5, step = .5)
302
+ elif prop_type_var == 'blocks':
303
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 5.5, value = 1.5, step = .5)
304
+ elif prop_type_var == 'steals':
305
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 5.5, value = 1.5, step = .5)
306
+ elif prop_type_var == 'PRA':
307
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 65.5, value = 20.5, step = .5)
308
+ elif prop_type_var == 'points+rebounds':
309
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 45.5, value = 10.5, step = .5)
310
+ elif prop_type_var == 'points+assists':
311
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 45.5, value = 10.5, step = .5)
312
+ elif prop_type_var == 'rebounds+assists':
313
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 45.5, value = 10.5, step = .5)
314
+ line_var = st.number_input('Type in the line on the prop (i.e. -120)', min_value = -1500, max_value = 1500, value = -150, step = 1)
315
+ line_var = line_var + 1
316
+
317
+ if st.button('Simulate Prop'):
318
+ with col2:
319
+
320
+ with df_hold_container.container():
321
+
322
+ df = player_stats
323
+ st.write("sim started")
324
+
325
+ total_sims = 1000
326
+
327
+ df.replace("", 0, inplace=True)
328
+
329
+ player_var = df[df['Player'] == player_check]
330
+ player_var = player_var.reset_index()
331
+
332
+ if prop_type_var == 'points':
333
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce')
334
+ elif prop_type_var == 'threes':
335
+ df['Median'] = pd.to_numeric(df['3P'], errors='coerce')
336
+ elif prop_type_var == 'rebounds':
337
+ df['Median'] = pd.to_numeric(df['Rebounds'], errors='coerce')
338
+ elif prop_type_var == 'assists':
339
+ df['Median'] = pd.to_numeric(df['Assists'], errors='coerce')
340
+ elif prop_type_var == 'blocks':
341
+ df['Median'] = pd.to_numeric(df['Blocks'], errors='coerce')
342
+ elif prop_type_var == 'steals':
343
+ df['Median'] = pd.to_numeric(df['Steals'], errors='coerce')
344
+ elif prop_type_var == 'PRA':
345
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
346
+ elif prop_type_var == 'points+rebounds':
347
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce')
348
+ elif prop_type_var == 'points+assists':
349
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
350
+ elif prop_type_var == 'rebounds+assists':
351
+ df['Median'] = pd.to_numeric(df['Assists'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce')
352
+
353
+ flex_file = df
354
+ flex_file['Floor'] = (flex_file['Median'] * .25) + (flex_file['Minutes'] * .25)
355
+ flex_file['Ceiling'] = flex_file['Median'] + 10 + (flex_file['Minutes'] * .25)
356
+ flex_file['STD'] = (flex_file['Median']/4)
357
+ flex_file = flex_file[['Player', 'Floor', 'Median', 'Ceiling', 'STD']]
358
+
359
+ hold_file = flex_file
360
+ overall_file = flex_file
361
+ salary_file = flex_file
362
+
363
+ overall_players = overall_file[['Player']]
364
+
365
+ for x in range(0,total_sims):
366
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
367
+
368
+ overall_file=overall_file.drop(['Player', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
369
+
370
+ players_only = hold_file[['Player']]
371
+
372
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
373
+ st.write("sim finished, calculating outcomes")
374
+
375
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
376
+ players_only['Prop'] = prop_var
377
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
378
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
379
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
380
+ if ou_var == 'Over':
381
+ players_only['beat_prop'] = np.where(players_only['Prop'] <= 3, players_only['poisson_var'], overall_file[overall_file > prop_var].count(axis=1)/float(total_sims))
382
+ elif ou_var == 'Under':
383
+ players_only['beat_prop'] = np.where(players_only['Prop'] <= 3, 1 - players_only['poisson_var'], (overall_file[overall_file < prop_var].count(axis=1)/float(total_sims)))
384
+
385
+ players_only['implied_odds'] = np.where(line_var <= 0, (-(line_var)/((-(line_var))+100)), 100/(line_var+100))
386
+
387
+ players_only['Player'] = hold_file[['Player']]
388
+
389
+ final_outcomes = players_only[['Player', '10%', 'Mean_Outcome', '90%', 'implied_odds', 'beat_prop']]
390
+ final_outcomes['Bet?'] = np.where(final_outcomes['beat_prop'] - final_outcomes['implied_odds'] >= .10, "Bet", "No Bet")
391
+ final_outcomes = final_outcomes[final_outcomes['Player'] == player_check]
392
+ player_outcomes = player_outcomes[player_outcomes['Player'] == player_check]
393
+ player_outcomes = player_outcomes.drop(columns=['Player']).transpose()
394
+ player_outcomes = player_outcomes.reset_index()
395
+ player_outcomes.columns = ['Instance', 'Outcome']
396
+
397
+ x1 = player_outcomes.Outcome.to_numpy()
398
+
399
+ print(x1)
400
+
401
+ hist_data = [x1]
402
+
403
+ group_labels = ['player outcomes']
404
+
405
+ fig = px.histogram(
406
+ player_outcomes, x='Outcome')
407
+ fig.add_vline(x=prop_var, line_dash="dash", line_color="green")
408
+
409
+ with df_hold_container:
410
+ df_hold_container = st.empty()
411
+ format_dict = {'10%': '{:.2f}', 'Mean_Outcome': '{:.2f}','90%': '{:.2f}', 'beat_prop': '{:.2%}','implied_odds': '{:.2%}'}
412
+ st.dataframe(final_outcomes.style.format(format_dict), use_container_width = True)
413
+
414
+ with info_hold_container:
415
+ st.info('The Y-axis is the percent of times in simulations that the player reaches certain thresholds, while the X-axis is the threshold to be met. The Green dotted line is the prop you entered. You can hover over any spot and see the percent to reach that mark.')
416
+
417
+ with plot_hold_container:
418
+ st.dataframe(player_outcomes, use_container_width = True)
419
+ plot_hold_container = st.empty()
420
+ st.plotly_chart(fig, use_container_width=True)
421
+
422
+ with tab6:
423
+ st.info(t_stamp)
424
+ st.info('The Over and Under percentages are a compositve 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.')
425
+ if st.button("Reset Data/Load Data", key='reset6'):
426
+ st.cache_data.clear()
427
+ game_model, raw_baselines, player_stats, prop_frame, pick_frame, market_props, timestamp = init_baselines()
428
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
429
+
430
+ settings_container = st.empty()
431
+ df_hold_container = st.empty()
432
+ export_container = st.empty()
433
+
434
+ with settings_container.container():
435
+ col1, col2, col3, col4 = st.columns([3, 3, 3, 3])
436
+ with col1:
437
+ game_select_var = st.selectbox('Select prop source', options = ['Aggregate', 'Pick6'])
438
+ with col2:
439
+ book_select_var = st.selectbox('Select book', options = ['ALL', 'BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL'])
440
+ if book_select_var == 'ALL':
441
+ book_selections = ['BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL']
442
+ else:
443
+ book_selections = [book_select_var]
444
+ if game_select_var == 'Aggregate':
445
+ prop_df = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
446
+ elif game_select_var == 'Pick6':
447
+ prop_df = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
448
+ book_selections = ['Pick6']
449
+ with col3:
450
+ if game_select_var == 'Aggregate':
451
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'NBA_GAME_PLAYER_POINTS', 'NBA_GAME_PLAYER_REBOUNDS', 'NBA_GAME_PLAYER_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS',
452
+ 'NBA_GAME_PLAYER_POINTS_REBOUNDS', 'NBA_GAME_PLAYER_POINTS_ASSISTS', 'NBA_GAME_PLAYER_REBOUNDS_ASSISTS', 'NBA_GAME_PLAYER_3_POINTERS_MADE'])
453
+ elif game_select_var == 'Pick6':
454
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'Points', 'Rebounds', 'Assists', 'Points + Assists + Rebounds', 'Points + Assists', 'Points + Rebounds', 'Assists + Rebounds', '3-Pointers Made'])
455
+ with col4:
456
+ st.download_button(
457
+ label="Download Prop Source",
458
+ data=convert_df_to_csv(prop_df),
459
+ file_name='Nba_prop_source.csv',
460
+ mime='text/csv',
461
+ key='prop_source',
462
+ )
463
+
464
+ if st.button('Simulate Prop Category'):
465
+
466
+ with df_hold_container.container():
467
+ if prop_type_var == 'All Props':
468
+ if game_select_var == 'Aggregate':
469
+ prop_df_raw = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
470
+ sim_vars = ['NBA_GAME_PLAYER_POINTS', 'NBA_GAME_PLAYER_REBOUNDS', 'NBA_GAME_PLAYER_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS', 'NBA_GAME_PLAYER_POINTS_REBOUNDS',
471
+ 'NBA_GAME_PLAYER_POINTS_ASSISTS', 'NBA_GAME_PLAYER_REBOUNDS_ASSISTS', 'NBA_GAME_PLAYER_3_POINTERS_MADE']
472
+ elif game_select_var == 'Pick6':
473
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
474
+ sim_vars = ['Points', 'Rebounds', 'Assists', 'Points + Assists + Rebounds', 'Points + Assists', 'Points + Rebounds', 'Assists + Rebounds', '3-Pointers Made']
475
+
476
+ player_df = player_stats.copy()
477
+
478
+ for prop in sim_vars:
479
+
480
+ for books in book_selections:
481
+ prop_df = prop_df_raw[prop_df_raw['prop_type'] == prop]
482
+ prop_df = prop_df[prop_df['book'] == books]
483
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
484
+ prop_df.rename(columns={"over_prop": "Prop"}, inplace = True)
485
+ prop_df['Over'] = 1 / prop_df['over_line']
486
+ prop_df['Under'] = 1 / prop_df['under_line']
487
+
488
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
489
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
490
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
491
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
492
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
493
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
494
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
495
+
496
+ player_df['book'] = player_df['Player'].map(book_dict)
497
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
498
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
499
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
500
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
501
+
502
+ df = player_df.reset_index(drop=True)
503
+
504
+ team_dict = dict(zip(df.Player, df.Team))
505
+
506
+ total_sims = 1000
507
+
508
+ df.replace("", 0, inplace=True)
509
+
510
+ if prop == "NBA_GAME_PLAYER_POINTS" or prop == "Points":
511
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce')
512
+ elif prop == "NBA_GAME_PLAYER_REBOUNDS" or prop == "Rebounds":
513
+ df['Median'] = pd.to_numeric(df['Rebounds'], errors='coerce')
514
+ elif prop == "NBA_GAME_PLAYER_ASSISTS" or prop == "Assists":
515
+ df['Median'] = pd.to_numeric(df['Assists'], errors='coerce')
516
+ elif prop == "NBA_GAME_PLAYER_3_POINTERS_MADE" or prop == "3-Pointers Made":
517
+ df['Median'] = pd.to_numeric(df['3P'], errors='coerce')
518
+ elif prop == "NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS" or prop == "Points + Assists + Rebounds":
519
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
520
+ elif prop == "NBA_GAME_PLAYER_POINTS_REBOUNDS" or prop == "Points + Rebounds":
521
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce')
522
+ elif prop == "NBA_GAME_PLAYER_POINTS_ASSISTS" or prop == "Points + Assists":
523
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
524
+ elif prop == "NBA_GAME_PLAYER_REBOUNDS_ASSISTS" or prop == "Assists + Rebounds":
525
+ df['Median'] = pd.to_numeric(df['Rebounds'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
526
+
527
+ flex_file = df.copy()
528
+ flex_file['Floor'] = flex_file['Median'] * .25
529
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1.75)
530
+ flex_file['STD'] = flex_file['Median'] / 4
531
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
532
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
533
+
534
+ hold_file = flex_file.copy()
535
+ overall_file = flex_file.copy()
536
+ prop_file = flex_file.copy()
537
+
538
+ overall_players = overall_file[['Player']]
539
+
540
+ for x in range(0,total_sims):
541
+ prop_file[x] = prop_file['Prop']
542
+
543
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
544
+
545
+ for x in range(0,total_sims):
546
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
547
+
548
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
549
+
550
+ players_only = hold_file[['Player']]
551
+
552
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
553
+
554
+ prop_check = (overall_file - prop_file)
555
+
556
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
557
+ players_only['Book'] = players_only['Player'].map(book_dict)
558
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
559
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
560
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
561
+ players_only['over_adj'] = np_where((players_only['Mean_Outcome'] - players_only['Prop']) > 0, 1, (players_only['Mean_Outcome'] / players_only['Prop']))
562
+ players_only['under_adj'] = np_where((players_only['Prop'] - players_only['Mean_Outcome']) > 0, 1, (players_only['Prop'] / players_only['Mean_Outcome']))
563
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
564
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
565
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
566
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
567
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
568
+ players_only['Over%'] = players_only[["Over", "Imp Over", "Trending Over"]].mean(axis=1)
569
+ 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))
570
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
571
+ players_only['Under%'] = players_only[["Under", "Imp Under", "Trending Under"]].mean(axis=1)
572
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
573
+ players_only['prop_threshold'] = .10
574
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
575
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
576
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
577
+ 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'])
578
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
579
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
580
+ players_only['Edge'] = players_only['Bet_check']
581
+ players_only['Prop Type'] = prop
582
+
583
+ players_only['Player'] = hold_file[['Player']]
584
+ players_only['Team'] = players_only['Player'].map(team_dict)
585
+
586
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
587
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
588
+
589
+ final_outcomes = sim_all_hold
590
+ st.write(f'finished {prop} for {books}')
591
+
592
+ elif prop_type_var != 'All Props':
593
+
594
+ player_df = player_stats.copy()
595
+
596
+ if game_select_var == 'Aggregate':
597
+ prop_df_raw = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
598
+ elif game_select_var == 'Pick6':
599
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
600
+
601
+ for books in book_selections:
602
+ prop_df = prop_df_raw[prop_df_raw['book'] == books]
603
+
604
+ if prop_type_var == "NBA_GAME_PLAYER_POINTS":
605
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_POINTS']
606
+ elif prop_type_var == "Points":
607
+ prop_df = prop_df[prop_df['prop_type'] == 'Points']
608
+ elif prop_type_var == "NBA_GAME_PLAYER_REBOUNDS":
609
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_REBOUNDS']
610
+ elif prop_type_var == "Rebounds":
611
+ prop_df = prop_df[prop_df['prop_type'] == 'Rebounds']
612
+ elif prop_type_var == "NBA_GAME_PLAYER_ASSISTS":
613
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_ASSISTS']
614
+ elif prop_type_var == "Assists":
615
+ prop_df = prop_df[prop_df['prop_type'] == 'Assists']
616
+ elif prop_type_var == "NBA_GAME_PLAYER_3_POINTERS_MADE":
617
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_3_POINTERS_MADE']
618
+ elif prop_type_var == "3-Pointers Made":
619
+ prop_df = prop_df[prop_df['prop_type'] == '3-Pointers Made']
620
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS":
621
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS']
622
+ elif prop_type_var == "Points + Assists + Rebounds":
623
+ prop_df = prop_df[prop_df['prop_type'] == 'Points + Assists + Rebounds']
624
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_REBOUNDS":
625
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_POINTS_REBOUNDS']
626
+ elif prop_type_var == "Points + Rebounds":
627
+ prop_df = prop_df[prop_df['prop_type'] == 'Points + Rebounds']
628
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_ASSISTS":
629
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_POINTS_ASSISTS']
630
+ elif prop_type_var == "Points + Assists":
631
+ prop_df = prop_df[prop_df['prop_type'] == 'Points + Assists']
632
+ elif prop_type_var == "NBA_GAME_PLAYER_REBOUNDS_ASSISTS":
633
+ prop_df = prop_df[prop_df['prop_type'] == 'NBA_GAME_PLAYER_REBOUNDS_ASSISTS']
634
+ elif prop_type_var == "Assists + Rebounds":
635
+ prop_df = prop_df[prop_df['prop_type'] == 'Assists + Rebounds']
636
+
637
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
638
+ prop_df = prop_df.rename(columns={"over_prop": "Prop"})
639
+ prop_df['Over'] = 1 / prop_df['over_line']
640
+ prop_df['Under'] = 1 / prop_df['under_line']
641
+
642
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
643
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
644
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
645
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
646
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
647
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
648
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
649
+
650
+ player_df['book'] = player_df['Player'].map(book_dict)
651
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
652
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
653
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
654
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
655
+
656
+ df = player_df.reset_index(drop=True)
657
+
658
+ team_dict = dict(zip(df.Player, df.Team))
659
+
660
+ total_sims = 1000
661
+
662
+ df.replace("", 0, inplace=True)
663
+
664
+ if prop_type_var == "NBA_GAME_PLAYER_POINTS" or prop_type_var == "Points":
665
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce')
666
+ elif prop_type_var == "NBA_GAME_PLAYER_REBOUNDS" or prop_type_var == "Rebounds":
667
+ df['Median'] = pd.to_numeric(df['Rebounds'], errors='coerce')
668
+ elif prop_type_var == "NBA_GAME_PLAYER_ASSISTS" or prop_type_var == "Assists":
669
+ df['Median'] = pd.to_numeric(df['Assists'], errors='coerce')
670
+ elif prop_type_var == "NBA_GAME_PLAYER_3_POINTERS_MADE" or prop_type_var == "3-Pointers Made":
671
+ df['Median'] = pd.to_numeric(df['3P'], errors='coerce')
672
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_REBOUNDS_ASSISTS" or prop_type_var == "Points + Assists + Rebounds":
673
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
674
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_REBOUNDS" or prop_type_var == "Points + Rebounds":
675
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Rebounds'], errors='coerce')
676
+ elif prop_type_var == "NBA_GAME_PLAYER_POINTS_ASSISTS" or prop_type_var == "Points + Assists":
677
+ df['Median'] = pd.to_numeric(df['Points'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
678
+ elif prop_type_var == "NBA_GAME_PLAYER_REBOUNDS_ASSISTS" or prop_type_var == "Assists + Rebounds":
679
+ df['Median'] = pd.to_numeric(df['Rebounds'], errors='coerce') + pd.to_numeric(df['Assists'], errors='coerce')
680
+
681
+ flex_file = df.copy()
682
+ flex_file['Floor'] = flex_file['Median'] * .25
683
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1.75)
684
+ flex_file['STD'] = flex_file['Median'] / 4
685
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
686
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
687
+
688
+ hold_file = flex_file.copy()
689
+ overall_file = flex_file.copy()
690
+ prop_file = flex_file.copy()
691
+
692
+ overall_players = overall_file[['Player']]
693
+
694
+ for x in range(0,total_sims):
695
+ prop_file[x] = prop_file['Prop']
696
+
697
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
698
+
699
+ for x in range(0,total_sims):
700
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
701
+
702
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
703
+
704
+ players_only = hold_file[['Player']]
705
+
706
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
707
+
708
+ prop_check = (overall_file - prop_file)
709
+
710
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
711
+ players_only['Book'] = players_only['Player'].map(book_dict)
712
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
713
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
714
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
715
+ players_only['over_adj'] = np_where((players_only['Mean_Outcome'] - players_only['Prop']) > 0, 1, (players_only['Mean_Outcome'] / players_only['Prop']))
716
+ players_only['under_adj'] = np_where((players_only['Prop'] - players_only['Mean_Outcome']) > 0, 1, (players_only['Prop'] / players_only['Mean_Outcome']))
717
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
718
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
719
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
720
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
721
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
722
+ players_only['Over%'] = players_only[["Over", "Imp Over", "Trending Over"]].mean(axis=1)
723
+ 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))
724
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
725
+ players_only['Under%'] = players_only[["Under", "Imp Under", "Trending Under"]].mean(axis=1)
726
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
727
+ players_only['prop_threshold'] = .10
728
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
729
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
730
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
731
+ 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'])
732
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
733
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
734
+ players_only['Edge'] = players_only['Bet_check']
735
+ players_only['Prop Type'] = prop_type_var
736
+
737
+ players_only['Player'] = hold_file[['Player']]
738
+ players_only['Team'] = players_only['Player'].map(team_dict)
739
+
740
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
741
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
742
+
743
+ final_outcomes = sim_all_hold
744
+ st.write(f'finished {prop_type_var} for {books}')
745
+
746
+ final_outcomes = final_outcomes.dropna()
747
+ if game_select_var == 'Pick6':
748
+ final_outcomes = final_outcomes.drop_duplicates(subset=['Player', 'Prop Type'])
749
+ final_outcomes = final_outcomes.sort_values(by='Edge', ascending=False)
750
+
751
+ with df_hold_container:
752
+ df_hold_container = st.empty()
753
+ st.dataframe(final_outcomes.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(sim_format, precision=2), height=500, use_container_width = True)
754
+ with export_container:
755
+ export_container = st.empty()
756
+ st.download_button(
757
+ label="Export Projections",
758
+ data=convert_df_to_csv(final_outcomes),
759
+ file_name='NBA_prop_proj.csv',
760
+ mime='text/csv',
761
+ key='prop_proj',
762
+ )
app.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ runtime: python
2
+ env: flex
3
+
4
+ runtime_config:
5
+ python_version: 3
6
+
7
+ entrypoint: streamlit run streamlit-app.py --server.port $PORT
8
+
9
+ automatic_scaling:
10
+ max_num_instances: 200
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ gspread
3
+ openpyxl
4
+ matplotlib
5
+ pymongo
6
+ pulp
7
+ docker
8
+ plotly
9
+ scipy