James McCool commited on
Commit
650863e
·
1 Parent(s): bb0fad3

Initial commit to create app

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