James McCool commited on
Commit
711ca4e
·
1 Parent(s): a28f220

Initial commit and dockerization

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
+ from pymongo import MongoClient
3
+ import os
4
+
5
+ @st.cache_resource
6
+ def init_conn():
7
+ uri = os.getenv('MONGO_URI')
8
+ if not uri:
9
+ uri = st.secrets['mongo_uri']
10
+ client = MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=100000)
11
+ dfs_db = client["NFL_Database"]
12
+ props_db = client["Props_DB"]
13
+
14
+ return props_db, dfs_db
15
+
16
+ # Initialize the database connection
17
+ props_db, dfs_db = init_conn()
src/streamlit_app.py CHANGED
@@ -1,40 +1,822 @@
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
  import numpy as np
3
+ from numpy import where as np_where
4
  import pandas as pd
5
+ import plotly.express as px
6
+ import scipy.stats as stats
7
+ st.set_page_config(layout="wide")
8
+
9
+ from database import props_db, dfs_db
10
+
11
+ game_format = {'Win%': '{:.2%}', 'Vegas': '{:.2%}', 'Win% Diff': '{:.2%}'}
12
+ american_format = {'First Inning Lead Percentage': '{:.2%}', 'Fifth Inning Lead Percentage': '{:.2%}'}
13
+
14
+ st.markdown("""
15
+ <style>
16
+ /* Tab styling */
17
+ .stElementContainer [data-baseweb="button-group"] {
18
+ gap: 2.000rem;
19
+ padding: 4px;
20
+ }
21
+ .stElementContainer [kind="segmented_control"] {
22
+ height: 2.000rem;
23
+ white-space: pre-wrap;
24
+ background-color: #68B1E7;
25
+ color: white;
26
+ border-radius: 20px;
27
+ gap: 1px;
28
+ padding: 10px 20px;
29
+ font-weight: bold;
30
+ transition: all 0.3s ease;
31
+ }
32
+ .stElementContainer [kind="segmented_controlActive"] {
33
+ height: 3.000rem;
34
+ background-color: #68B1E7;
35
+ border: 3px solid #4FB286;
36
+ border-radius: 10px;
37
+ color: black;
38
+ }
39
+ .stElementContainer [kind="segmented_control"]:hover {
40
+ background-color: #4FB286;
41
+ cursor: pointer;
42
+ }
43
+
44
+ div[data-baseweb="select"] > div {
45
+ background-color: #68B1E7;
46
+ color: white;
47
+ }
48
+
49
+ </style>""", unsafe_allow_html=True)
50
+
51
+ def calculate_poisson(row):
52
+ mean_val = row['Mean_Outcome']
53
+ threshold = row['Prop']
54
+ cdf_value = stats.poisson.cdf(threshold, mean_val)
55
+ probability = 1 - cdf_value
56
+ return probability
57
+
58
+ @st.cache_resource(ttl=600)
59
+ def init_baselines():
60
+ collection = dfs_db["Game_Betting_Model"]
61
+ cursor = collection.find()
62
+ raw_display = pd.DataFrame(list(cursor))
63
+ game_model = raw_display[['Team', 'Opp', 'Win%', 'Vegas', 'Win% Diff', 'Win Line', 'Vegas Line', 'Line Diff', 'PD Spread', 'Vegas Spread', 'Spread Diff']]
64
+
65
+ collection = dfs_db["Player_Stats"]
66
+ cursor = collection.find()
67
+ raw_display = pd.DataFrame(list(cursor))
68
+ overall_stats = raw_display[['Player', 'Position', 'Team', 'Opp', 'rush_att', 'rec', 'dropbacks', 'rush_yards', 'rush_tds', 'rec_yards', 'rec_tds', 'pass_att', 'pass_yards', 'pass_tds', 'PPR', 'Half_PPR']]
69
+
70
+ collection = dfs_db["Prop_Trends"]
71
+ cursor = collection.find()
72
+ raw_display = pd.DataFrame(list(cursor))
73
+ prop_trends = raw_display[['Player', 'over_prop', 'over_line', 'under_prop', 'under_line', 'book', 'prop_type', 'No Vig', 'Team', 'L3 Success', 'L6_Success', 'L10_success', 'L6 Avg', 'Projection',
74
+ 'Proj Diff', 'Implied Over', 'Trending Over', 'Over Edge', 'Implied Under', 'Trending Under', 'Under Edge']]
75
+
76
+ collection = dfs_db["DK_NFL_ROO"]
77
+ cursor = collection.find()
78
+
79
+ raw_display = pd.DataFrame(list(cursor))
80
+ raw_display = raw_display[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%',
81
+ 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX', 'version', 'slate', 'timestamp', 'player_ID', 'site']]
82
+ load_display = raw_display[raw_display['Position'] != 'K']
83
+ timestamp = load_display['timestamp'][0]
84
+
85
+ collection = dfs_db["Prop_Trends"]
86
+ cursor = collection.find()
87
+ raw_display = pd.DataFrame(list(cursor))
88
+ prop_frame = raw_display[['Player', 'over_prop', 'over_line', 'under_prop', 'under_line', 'book', 'prop_type', 'No Vig', 'Team', 'L3 Success', 'L6_Success', 'L10_success', 'L6 Avg', 'Projection',
89
+ 'Proj Diff', 'Implied Over', 'Trending Over', 'Over Edge', 'Implied Under', 'Trending Under', 'Under Edge']]
90
+
91
+ collection = dfs_db['Pick6_Trends']
92
+ cursor = collection.find()
93
+ raw_display = pd.DataFrame(list(cursor))
94
+ pick_frame = raw_display[['Player', 'over_prop', 'over_line', 'under_prop', 'under_line', 'book', 'prop_type', 'No Vig', 'Team', 'L3 Success', 'L6_Success', 'L10_success', 'L6 Avg', 'Projection',
95
+ 'Proj Diff', 'Implied Over', 'Trending Over', 'Over Edge', 'Implied Under', 'Trending Under', 'Under Edge', 'last_name', 'P6_name', 'Full_name']]
96
+
97
+ collection = props_db["NFL_Props"]
98
+ cursor = collection.find()
99
+
100
+ raw_display = pd.DataFrame(list(cursor))
101
+ market_props = raw_display[['Name', 'Position', 'Projection', 'PropType', 'OddsType', 'over_pay', 'under_pay']]
102
+ market_props['over_prop'] = market_props['Projection']
103
+ market_props['over_line'] = market_props['over_pay'].apply(lambda x: (x - 1) * 100 if x >= 2.0 else -100 / (x - 1))
104
+ market_props['under_prop'] = market_props['Projection']
105
+ market_props['under_line'] = market_props['under_pay'].apply(lambda x: (x - 1) * 100 if x >= 2.0 else -100 / (x - 1))
106
+
107
+ return game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props
108
+
109
+ def calculate_no_vig(row):
110
+ def implied_probability(american_odds):
111
+ if american_odds < 0:
112
+ return (-american_odds) / ((-american_odds) + 100)
113
+ else:
114
+ return 100 / (american_odds + 100)
115
+
116
+ over_line = row['over_line']
117
+ under_line = row['under_line']
118
+ over_prop = row['over_prop']
119
+
120
+ over_prob = implied_probability(over_line)
121
+ under_prob = implied_probability(under_line)
122
+
123
+ total_prob = over_prob + under_prob
124
+ no_vig_prob = (over_prob / total_prob + 0.5) * over_prop
125
+
126
+ return no_vig_prob
127
+
128
+ def convert_df_to_csv(df):
129
+ return df.to_csv().encode('utf-8')
130
+
131
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
132
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
133
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
134
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
135
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
136
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
137
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
138
+
139
+ prop_table_options = ['NFL_GAME_PLAYER_PASSING_YARDS', 'NFL_GAME_PLAYER_RUSHING_YARDS', 'NFL_GAME_PLAYER_PASSING_ATTEMPTS', 'NFL_GAME_PLAYER_PASSING_TOUCHDOWNS', 'NFL_GAME_PLAYER_PASSING_COMPLETIONS', 'NFL_GAME_PLAYER_RUSHING_ATTEMPTS',
140
+ 'NFL_GAME_PLAYER_RECEIVING_RECEPTIONS', 'NFL_GAME_PLAYER_RECEIVING_YARDS', 'NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS']
141
+ prop_format = {'L3 Success': '{:.2%}', 'L6_Success': '{:.2%}', 'L10_success': '{:.2%}', 'Trending Over': '{:.2%}', 'Trending Under': '{:.2%}',
142
+ 'Implied Over': '{:.2%}', 'Implied Under': '{:.2%}', 'Over Edge': '{:.2%}', 'Under Edge': '{:.2%}'}
143
+ all_sim_vars = ['NFL_GAME_PLAYER_PASSING_YARDS', 'NFL_GAME_PLAYER_RUSHING_YARDS', 'NFL_GAME_PLAYER_PASSING_ATTEMPTS', 'NFL_GAME_PLAYER_PASSING_TOUCHDOWNS', 'NFL_GAME_PLAYER_PASSING_COMPLETIONS', 'NFL_GAME_PLAYER_RUSHING_ATTEMPTS',
144
+ 'NFL_GAME_PLAYER_RECEIVING_RECEPTIONS', 'NFL_GAME_PLAYER_RECEIVING_YARDS', 'NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS']
145
+ pick6_sim_vars = ['Rush + Rec Yards', 'Rush + Rec TDs', 'Passing Yards', 'Passing Attempts', 'Passing TDs', 'Completions', 'Rushing Yards', 'Receptions', 'Receiving Yards']
146
+ 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'])
147
+
148
+ selected_tab = st.segmented_control(
149
+ "Select Tab",
150
+ options=["Game Betting Model", 'Prop Market', "QB Projections", "RB/WR/TE Projections", "Player Prop Trends", "Player Prop Simulations", "Stat Specific Simulations"],
151
+ selection_mode='single',
152
+ default='Game Betting Model',
153
+ width='stretch',
154
+ label_visibility='collapsed',
155
+ key='tab_selector'
156
+ )
157
+
158
+ if selected_tab == 'Game Betting Model':
159
+ st.info(t_stamp)
160
+ if st.button("Reset Data", key='reset1'):
161
+ st.cache_data.clear()
162
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
163
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
164
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
165
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
166
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
167
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
168
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
169
+ line_var1 = st.radio('How would you like to display odds?', options = ['Percentage', 'American'], key='line_var1')
170
+ team_frame = game_model
171
+ if line_var1 == 'Percentage':
172
+ team_frame = team_frame[['Team', 'Opp', 'Win%', 'Vegas', 'Win% Diff', 'PD Spread', 'Vegas Spread', 'Spread Diff']]
173
+ team_frame = team_frame.set_index('Team')
174
+ try:
175
+ st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(game_format, precision=2), use_container_width = True)
176
+ except:
177
+ st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True)
178
+ if line_var1 == 'American':
179
+ team_frame = team_frame[['Team', 'Opp', 'Win Line', 'Vegas Line', 'Line Diff', 'PD Spread', 'Vegas Spread', 'Spread Diff']]
180
+ team_frame = team_frame.set_index('Team')
181
+ st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height = 1000, use_container_width = True)
182
+
183
+ st.download_button(
184
+ label="Export Team Model",
185
+ data=convert_df_to_csv(team_frame),
186
+ file_name='NFL_team_betting_export.csv',
187
+ mime='text/csv',
188
+ key='team_export',
189
+ )
190
+
191
+ if selected_tab == 'Prop Market':
192
+ st.info(t_stamp)
193
+ if st.button("Reset Data", key='reset4'):
194
+ st.cache_data.clear()
195
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
196
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
197
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
198
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
199
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
200
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
201
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
202
+ market_type = st.selectbox('Select type of prop are you wanting to view', options = prop_table_options, key = 'market_type_key')
203
+ disp_market = market_props.copy()
204
+ disp_market = disp_market[disp_market['PropType'] == market_type]
205
+ disp_market['No_Vig_Prop'] = disp_market.apply(calculate_no_vig, axis=1)
206
+ fanduel_frame = disp_market[disp_market['OddsType'] == 'FANDUEL']
207
+ fanduel_dict = dict(zip(fanduel_frame['Name'], fanduel_frame['No_Vig_Prop']))
208
+ draftkings_frame = disp_market[disp_market['OddsType'] == 'DRAFTKINGS']
209
+ draftkings_dict = dict(zip(draftkings_frame['Name'], draftkings_frame['No_Vig_Prop']))
210
+ mgm_frame = disp_market[disp_market['OddsType'] == 'MGM']
211
+ mgm_dict = dict(zip(mgm_frame['Name'], mgm_frame['No_Vig_Prop']))
212
+ bet365_frame = disp_market[disp_market['OddsType'] == 'BET_365']
213
+ bet365_dict = dict(zip(bet365_frame['Name'], bet365_frame['No_Vig_Prop']))
214
+
215
+ disp_market['FANDUEL'] = disp_market['Name'].map(fanduel_dict)
216
+ disp_market['DRAFTKINGS'] = disp_market['Name'].map(draftkings_dict)
217
+ disp_market['MGM'] = disp_market['Name'].map(mgm_dict)
218
+ disp_market['BET365'] = disp_market['Name'].map(bet365_dict)
219
+
220
+ disp_market = disp_market[['Name', 'Position','FANDUEL', 'DRAFTKINGS', 'MGM', 'BET365']]
221
+ disp_market = disp_market.drop_duplicates(subset=['Name'], keep='first', ignore_index=True)
222
+
223
+ 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)
224
+ st.download_button(
225
+ label="Export Market Props",
226
+ data=convert_df_to_csv(disp_market),
227
+ file_name='NFL_market_props_export.csv',
228
+ mime='text/csv',
229
+ )
230
+
231
+ if selected_tab == 'QB Projections':
232
+ st.info(t_stamp)
233
+ if st.button("Reset Data", key='reset2'):
234
+ st.cache_data.clear()
235
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
236
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
237
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
238
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
239
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
240
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
241
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
242
+ split_var1 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var1')
243
+ if split_var1 == 'Specific Teams':
244
+ team_var1 = st.multiselect('Which teams would you like to include in the tables?', options = qb_stats['Team'].unique(), key='team_var1')
245
+ elif split_var1 == 'All':
246
+ team_var1 = qb_stats.Team.values.tolist()
247
+ qb_stats = qb_stats[qb_stats['Team'].isin(team_var1)]
248
+ qb_stats_disp = qb_stats.set_index('Player')
249
+ qb_stats_disp = qb_stats_disp.sort_values(by='PPR', ascending=False)
250
+ st.dataframe(qb_stats_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height = 1000, use_container_width = True)
251
+ st.download_button(
252
+ label="Export Prop Model",
253
+ data=convert_df_to_csv(qb_stats_disp),
254
+ file_name='NFL_qb_stats_export.csv',
255
+ mime='text/csv',
256
+ key='NFL_qb_stats_export',
257
+ )
258
+
259
+ if selected_tab == 'RB/WR/TE Projections':
260
+ st.info(t_stamp)
261
+ if st.button("Reset Data", key='reset3'):
262
+ st.cache_data.clear()
263
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
264
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
265
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
266
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
267
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
268
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
269
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
270
+ split_var2 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var2')
271
+ if split_var2 == 'Specific Teams':
272
+ team_var2 = st.multiselect('Which teams would you like to include in the tables?', options = non_qb_stats['Team'].unique(), key='team_var2')
273
+ elif split_var2 == 'All':
274
+ team_var2 = non_qb_stats.Team.values.tolist()
275
+ non_qb_stats = non_qb_stats[non_qb_stats['Team'].isin(team_var2)]
276
+ non_qb_stats_disp = non_qb_stats.set_index('Player')
277
+ non_qb_stats_disp = non_qb_stats_disp.sort_values(by='PPR', ascending=False)
278
+ st.dataframe(non_qb_stats_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height = 1000, use_container_width = True)
279
+ st.download_button(
280
+ label="Export Prop Model",
281
+ data=convert_df_to_csv(non_qb_stats_disp),
282
+ file_name='NFL_nonqb_stats_export.csv',
283
+ mime='text/csv',
284
+ key='NFL_nonqb_stats_export',
285
+ )
286
+
287
+ if selected_tab == 'Player Prop Trends':
288
+ st.info(t_stamp)
289
+ if st.button("Reset Data", key='reset5'):
290
+ st.cache_data.clear()
291
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
292
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
293
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
294
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
295
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
296
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
297
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
298
+ split_var5 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var5')
299
+ if split_var5 == 'Specific Teams':
300
+ team_var5 = st.multiselect('Which teams would you like to include in the tables?', options = prop_trends['Team'].unique(), key='team_var5')
301
+ elif split_var5 == 'All':
302
+ team_var5 = prop_trends.Team.values.tolist()
303
+ prop_type_var2 = st.selectbox('Select type of prop are you wanting to view', options = prop_table_options)
304
+ book_var2 = st.selectbox('Select type of book do you want to view?', options = ['FANDUEL', 'BET365', 'DRAFTKINGS', 'CONSENSUS'])
305
+ prop_frame_disp = prop_trends[prop_trends['Team'].isin(team_var5)]
306
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['book'] == book_var2]
307
+ prop_frame_disp = prop_frame_disp[prop_frame_disp['prop_type'] == prop_type_var2]
308
+ #prop_frame_disp = prop_frame_disp.set_index('Player')
309
+ prop_frame_disp = prop_frame_disp.sort_values(by='Trending Over', ascending=False)
310
+ st.dataframe(prop_frame_disp.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(prop_format, precision=2), height = 1000, use_container_width = True)
311
+ st.download_button(
312
+ label="Export Prop Trends Model",
313
+ data=convert_df_to_csv(prop_frame_disp),
314
+ file_name='NFL_prop_trends_export.csv',
315
+ mime='text/csv',
316
+ )
317
+
318
+ if selected_tab == 'Player Prop Simulations':
319
+ st.info(t_stamp)
320
+ if st.button("Reset Data", key='reset6'):
321
+ st.cache_data.clear()
322
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
323
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
324
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
325
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
326
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
327
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
328
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
329
+ col1, col2 = st.columns([1, 5])
330
+
331
+ with col2:
332
+ df_hold_container = st.empty()
333
+ info_hold_container = st.empty()
334
+ plot_hold_container = st.empty()
335
+
336
+ with col1:
337
+ player_check = st.selectbox('Select player to simulate props', options = overall_stats['Player'].unique())
338
+ prop_type_var = st.selectbox('Select type of prop to simulate', options = ['Pass Yards', 'Pass TDs', 'Rush Yards', 'Rush TDs', 'Receptions', 'Rec Yards', 'Rec TDs', 'Fantasy', 'FD Fantasy', 'PrizePicks'])
339
+
340
+ ou_var = st.selectbox('Select wether it is an over or under', options = ['Over', 'Under'])
341
+ if prop_type_var == 'Pass Yards':
342
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 100.0, max_value = 400.5, value = 250.5, step = .5)
343
+ elif prop_type_var == 'Pass TDs':
344
+ 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)
345
+ elif prop_type_var == 'Rush Yards':
346
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 155.5, value = 25.5, step = .5)
347
+ elif prop_type_var == 'Rush TDs':
348
+ 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)
349
+ elif prop_type_var == 'Receptions':
350
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 15.5, value = 5.5, step = .5)
351
+ elif prop_type_var == 'Rec Yards':
352
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 155.5, value = 25.5, step = .5)
353
+ elif prop_type_var == 'Rec TDs':
354
+ 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)
355
+ elif prop_type_var == 'Fantasy':
356
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 50.5, value = 10.5, step = .5)
357
+ elif prop_type_var == 'FD Fantasy':
358
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 50.5, value = 10.5, step = .5)
359
+ elif prop_type_var == 'PrizePicks':
360
+ prop_var = st.number_input('Type in the prop offered (i.e 5.5)', min_value = 0.0, max_value = 50.5, value = 10.5, step = .5)
361
+ line_var = st.number_input('Type in the line on the prop (i.e. -120)', min_value = -1000, max_value = 1000, value = -150, step = 1)
362
+ line_var = line_var + 1
363
+
364
+ if st.button('Simulate Prop'):
365
+ with col2:
366
+
367
+ with df_hold_container.container():
368
+
369
+ df = overall_stats
370
+
371
+ total_sims = 5000
372
+
373
+ df.replace("", 0, inplace=True)
374
+
375
+ player_var = df[df['Player'] == player_check]
376
+ player_var = player_var.reset_index()
377
+
378
+ if prop_type_var == 'Pass Yards':
379
+ df['Median'] = df['pass_yards']
380
+ elif prop_type_var == 'Pass TDs':
381
+ df['Median'] = df['pass_tds']
382
+ elif prop_type_var == 'Rush Yards':
383
+ df['Median'] = df['rush_yards']
384
+ elif prop_type_var == 'Rush TDs':
385
+ df['Median'] = df['rush_tds']
386
+ elif prop_type_var == 'Receptions':
387
+ df['Median'] = df['rec']
388
+ elif prop_type_var == 'Rec Yards':
389
+ df['Median'] = df['rec_yards']
390
+ elif prop_type_var == 'Rec TDs':
391
+ df['Median'] = df['rec_tds']
392
+ elif prop_type_var == 'Fantasy':
393
+ df['Median'] = df['PPR']
394
+ elif prop_type_var == 'FD Fantasy':
395
+ df['Median'] = df['Half_PPF']
396
+ elif prop_type_var == 'PrizePicks':
397
+ df['Median'] = df['Half_PPF']
398
+
399
+ flex_file = df
400
+ flex_file['Floor'] = flex_file['Median'] * .25
401
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1.75)
402
+ flex_file['STD'] = flex_file['Median'] / 4
403
+ flex_file = flex_file[['Player', 'Floor', 'Median', 'Ceiling', 'STD']]
404
+
405
+ hold_file = flex_file
406
+ overall_file = flex_file
407
+ salary_file = flex_file
408
+
409
+ overall_players = overall_file[['Player']]
410
+
411
+ for x in range(0,total_sims):
412
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
413
+
414
+ overall_file=overall_file.drop(['Player', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
415
+ overall_file.astype('int').dtypes
416
+
417
+ players_only = hold_file[['Player']]
418
+
419
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
420
+
421
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
422
+ players_only['Prop'] = prop_var
423
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
424
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
425
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
426
+ if ou_var == 'Over':
427
+ 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))
428
+ elif ou_var == 'Under':
429
+ 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)))
430
+
431
+ players_only['implied_odds'] = np.where(line_var <= 0, (-(line_var)/((-(line_var))+100)), 100/(line_var+100))
432
+
433
+ players_only['Player'] = hold_file[['Player']]
434
+
435
+ final_outcomes = players_only[['Player', '10%', 'Mean_Outcome', '90%', 'implied_odds', 'beat_prop']]
436
+ final_outcomes['Bet?'] = np.where(final_outcomes['beat_prop'] - final_outcomes['implied_odds'] >= .10, "Bet", "No Bet")
437
+ final_outcomes = final_outcomes[final_outcomes['Player'] == player_check]
438
+ player_outcomes = player_outcomes[player_outcomes['Player'] == player_check]
439
+ player_outcomes = player_outcomes.drop(columns=['Player']).transpose()
440
+ player_outcomes = player_outcomes.reset_index()
441
+ player_outcomes.columns = ['Instance', 'Outcome']
442
+
443
+ x1 = player_outcomes.Outcome.to_numpy()
444
+
445
+ print(x1)
446
+
447
+ hist_data = [x1]
448
+
449
+ group_labels = ['player outcomes']
450
+
451
+ fig = px.histogram(
452
+ player_outcomes, x='Outcome')
453
+ fig.add_vline(x=prop_var, line_dash="dash", line_color="green")
454
+
455
+ with df_hold_container:
456
+ df_hold_container = st.empty()
457
+ format_dict = {'10%': '{:.2f}', 'Mean_Outcome': '{:.2f}','90%': '{:.2f}', 'beat_prop': '{:.2%}','implied_odds': '{:.2%}'}
458
+ st.dataframe(final_outcomes.style.format(format_dict), use_container_width = True)
459
+
460
+ with info_hold_container:
461
+ 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.')
462
+
463
+ with plot_hold_container:
464
+ st.dataframe(player_outcomes, use_container_width = True)
465
+ plot_hold_container = st.empty()
466
+ st.plotly_chart(fig, use_container_width=True)
467
+
468
+ if selected_tab == 'Stat Specific Simulations':
469
+ st.info(t_stamp)
470
+ 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.')
471
+ if st.button("Reset Data/Load Data", key='reset7'):
472
+ st.cache_data.clear()
473
+ game_model, overall_stats, timestamp, prop_frame, prop_trends, pick_frame, market_props = init_baselines()
474
+ qb_stats = overall_stats[overall_stats['Position'] == 'QB']
475
+ qb_stats = qb_stats.drop_duplicates(subset=['Player', 'Position'])
476
+ non_qb_stats = overall_stats[overall_stats['Position'] != 'QB']
477
+ non_qb_stats = non_qb_stats.drop_duplicates(subset=['Player', 'Position'])
478
+ team_dict = dict(zip(prop_frame['Player'], prop_frame['Team']))
479
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
480
+
481
+ settings_container = st.empty()
482
+ df_hold_container = st.empty()
483
+ export_container = st.empty()
484
+
485
+ with settings_container.container():
486
+ col1, col2, col3, col4 = st.columns([3, 3, 3, 3])
487
+ with col1:
488
+ game_select_var = st.selectbox('Select prop source', options = ['Aggregate', 'Pick6'])
489
+ with col2:
490
+ book_select_var = st.selectbox('Select book', options = ['ALL', 'BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL'])
491
+ if book_select_var == 'ALL':
492
+ book_selections = ['BET_365', 'DRAFTKINGS', 'FANDUEL', 'MGM', 'UNIBET', 'WILLIAM_HILL']
493
+ else:
494
+ book_selections = [book_select_var]
495
+ if game_select_var == 'Aggregate':
496
+ prop_df = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
497
+ elif game_select_var == 'Pick6':
498
+ prop_df = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
499
+ book_selections = ['Pick6']
500
+ with col3:
501
+ if game_select_var == 'Aggregate':
502
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'NFL_GAME_PLAYER_PASSING_YARDS', 'NFL_GAME_PLAYER_RUSHING_YARDS', 'NFL_GAME_PLAYER_PASSING_ATTEMPTS', 'NFL_GAME_PLAYER_PASSING_TOUCHDOWNS', 'NFL_GAME_PLAYER_RUSHING_ATTEMPTS',
503
+ 'NFL_GAME_PLAYER_RECEIVING_RECEPTIONS', 'NFL_GAME_PLAYER_RECEIVING_YARDS', 'NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS'])
504
+ elif game_select_var == 'Pick6':
505
+ prop_type_var = st.selectbox('Select prop category', options = ['All Props', 'Rush + Rec Yards', 'Rush + Rec TDs', 'Passing Yards', 'Passing Attempts', 'Passing TDs', 'Rushing Attempts', 'Rushing Yards', 'Receptions', 'Receiving Yards', 'Receiving TDs'])
506
+ with col4:
507
+ st.download_button(
508
+ label="Download Prop Source",
509
+ data=convert_df_to_csv(prop_df),
510
+ file_name='NFL_prop_source.csv',
511
+ mime='text/csv',
512
+ key='prop_source',
513
+ )
514
+
515
+ if st.button('Simulate Prop Category'):
516
+
517
+ with df_hold_container.container():
518
+ if prop_type_var == 'All Props':
519
+ if game_select_var == 'Aggregate':
520
+ prop_df_raw = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
521
+ sim_vars = ['NFL_GAME_PLAYER_PASSING_YARDS', 'NFL_GAME_PLAYER_RUSHING_YARDS', 'NFL_GAME_PLAYER_PASSING_ATTEMPTS', 'NFL_GAME_PLAYER_PASSING_TOUCHDOWNS', 'NFL_GAME_PLAYER_RUSHING_ATTEMPTS',
522
+ 'NFL_GAME_PLAYER_RECEIVING_RECEPTIONS', 'NFL_GAME_PLAYER_RECEIVING_YARDS', 'NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS']
523
+ elif game_select_var == 'Pick6':
524
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
525
+ sim_vars = ['Rush + Rec Yards', 'Rush + Rec TDs', 'Passing Yards', 'Passing Attempts', 'Passing TDs', 'Rushing Attempts', 'Rushing Yards', 'Receptions', 'Receiving Yards', 'Receiving TDs']
526
+
527
+ player_df = overall_stats.copy()
528
+
529
+ for prop in sim_vars:
530
+
531
+ for books in book_selections:
532
+ prop_df = prop_df_raw[prop_df_raw['book'] == books]
533
+ prop_df = prop_df[prop_df['prop_type'] == prop]
534
+ prop_df = prop_df[~((prop_df['over_prop'] < 15) & (prop_df['prop_type'] == 'NFL_GAME_PLAYER_RUSHING_YARDS'))]
535
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
536
+ prop_df.rename(columns={"over_prop": "Prop"}, inplace = True)
537
+ prop_df['Over'] = 1 / prop_df['over_line']
538
+ prop_df['Under'] = 1 / prop_df['under_line']
539
+
540
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
541
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
542
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
543
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
544
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
545
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
546
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
547
+
548
+ player_df['book'] = player_df['Player'].map(book_dict)
549
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
550
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
551
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
552
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
553
+
554
+ df = player_df.reset_index(drop=True)
555
+
556
+ team_dict = dict(zip(df.Player, df.Team))
557
+
558
+ total_sims = 1000
559
+
560
+ df.replace("", 0, inplace=True)
561
+
562
+ if prop == "NFL_GAME_PLAYER_PASSING_YARDS" or prop == "Passing Yards":
563
+ df['Median'] = df['pass_yards']
564
+ elif prop == "NFL_GAME_PLAYER_RUSHING_YARDS" or prop == "Rushing Yards":
565
+ df['Median'] = df['rush_yards']
566
+ elif prop == "NFL_GAME_PLAYER_PASSING_ATTEMPTS" or prop == "Passing Attempts":
567
+ df['Median'] = df['pass_att']
568
+ elif prop == "NFL_GAME_PLAYER_PASSING_TOUCHDOWNS" or prop == "Passing TDs":
569
+ df['Median'] = df['pass_tds']
570
+ elif prop == "NFL_GAME_PLAYER_RUSHING_ATTEMPTS" or prop == "Rushing Attempts":
571
+ df['Median'] = df['rush_att']
572
+ elif prop == "NFL_GAME_PLAYER_RECEIVING_RECEPTIONS" or prop == "Receptions":
573
+ df['Median'] = df['rec']
574
+ elif prop == "NFL_GAME_PLAYER_RECEIVING_YARDS" or prop == "Receiving Yards":
575
+ df['Median'] = df['rec_yards']
576
+ elif prop == "NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS" or prop == "Receiving TDs":
577
+ df['Median'] = df['rec_tds']
578
+ elif prop == "Rush + Rec Yards":
579
+ df['Median'] = df['rush_yards'] + df['rec_yards']
580
+ elif prop == "Rush + Rec TDs":
581
+ df['Median'] = df['rush_tds'] + df['rec_tds']
582
+
583
+ flex_file = df.copy()
584
+ flex_file['Floor'] = flex_file['Median'] * .25
585
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1.75)
586
+ flex_file['STD'] = flex_file['Median'] / 4
587
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
588
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
589
+
590
+ hold_file = flex_file.copy()
591
+ overall_file = flex_file.copy()
592
+ prop_file = flex_file.copy()
593
+
594
+ overall_players = overall_file[['Player']]
595
+
596
+ for x in range(0,total_sims):
597
+ prop_file[x] = prop_file['Prop']
598
+
599
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
600
+
601
+ for x in range(0,total_sims):
602
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
603
+
604
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
605
+
606
+ players_only = hold_file[['Player']]
607
+
608
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
609
+
610
+ prop_check = (overall_file - prop_file)
611
+
612
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
613
+ players_only['Book'] = players_only['Player'].map(book_dict)
614
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
615
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
616
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
617
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
618
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
619
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
620
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
621
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
622
+ players_only['Over%'] = players_only[["Over", "Imp Over", "Trending Over"]].mean(axis=1)
623
+ 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))
624
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
625
+ players_only['Under%'] = players_only[["Under", "Imp Under", "Trending Under"]].mean(axis=1)
626
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
627
+ players_only['prop_threshold'] = .10
628
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
629
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
630
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
631
+ players_only['Bet_check'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], players_only['Over_diff'] , players_only['Under_diff'])
632
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
633
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
634
+ players_only['Edge'] = players_only['Bet_check']
635
+ players_only['Prop Type'] = prop
636
+
637
+ players_only['Player'] = hold_file[['Player']]
638
+ players_only['Team'] = players_only['Player'].map(team_dict)
639
+
640
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
641
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
642
+
643
+ final_outcomes = sim_all_hold
644
+ st.write(f'finished {prop} for {books}')
645
+
646
+ elif prop_type_var != 'All Props':
647
+
648
+ player_df = overall_stats.copy()
649
+
650
+ if game_select_var == 'Aggregate':
651
+ prop_df_raw = prop_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
652
+ elif game_select_var == 'Pick6':
653
+ prop_df_raw = pick_frame[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
654
+
655
+ for books in book_selections:
656
+ prop_df = prop_df_raw[prop_df_raw['book'] == books]
657
+
658
+ if prop_type_var == "NFL_GAME_PLAYER_PASSING_YARDS":
659
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_PASSING_YARDS']
660
+ elif prop_type_var == "Passing Yards":
661
+ prop_df = prop_df[prop_df['prop_type'] == 'Passing Yards']
662
+ elif prop_type_var == "NFL_GAME_PLAYER_RUSHING_YARDS":
663
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_RUSHING_YARDS']
664
+ elif prop_type_var == "Rushing Yards":
665
+ prop_df = prop_df[prop_df['prop_type'] == 'Rushing Yards']
666
+ elif prop_type_var == "NFL_GAME_PLAYER_PASSING_ATTEMPTS":
667
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_PASSING_ATTEMPTS']
668
+ elif prop_type_var == "Passing Attempts":
669
+ prop_df = prop_df[prop_df['prop_type'] == 'Passing Attempts']
670
+ elif prop_type_var == "NFL_GAME_PLAYER_PASSING_TOUCHDOWNS":
671
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_PASSING_TOUCHDOWNS']
672
+ elif prop_type_var == "Passing TDs":
673
+ prop_df = prop_df[prop_df['prop_type'] == 'Passing TDs']
674
+ elif prop_type_var == "NFL_GAME_PLAYER_RUSHING_ATTEMPTS":
675
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_RUSHING_ATTEMPTS']
676
+ elif prop_type_var == "Rushing Attempts":
677
+ prop_df = prop_df[prop_df['prop_type'] == 'Rushing Attempts']
678
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_RECEPTIONS":
679
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_RECEIVING_RECEPTIONS']
680
+ elif prop_type_var == "Receptions":
681
+ prop_df = prop_df[prop_df['prop_type'] == 'Receptions']
682
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_YARDS":
683
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_RECEIVING_YARDS']
684
+ elif prop_type_var == "Receiving Yards":
685
+ prop_df = prop_df[prop_df['prop_type'] == 'Receiving Yards']
686
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS":
687
+ prop_df = prop_df[prop_df['prop_type'] == 'NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS']
688
+ elif prop_type_var == "Receiving TDs":
689
+ prop_df = prop_df[prop_df['prop_type'] == 'Receiving TDs']
690
+ elif prop_type_var == "Rush + Rec Yards":
691
+ prop_df = prop_df[prop_df['prop_type'] == 'Rush + Rec Yards']
692
+ elif prop_type_var == "Rush + Rec TDs":
693
+ prop_df = prop_df[prop_df['prop_type'] == 'Rush + Rec TDs']
694
+
695
+ prop_df = prop_df[['Player', 'book', 'over_prop', 'over_line', 'under_line', 'prop_type', 'Trending Over', 'Trending Under']]
696
+ prop_df = prop_df.rename(columns={"over_prop": "Prop"})
697
+ prop_df['Over'] = 1 / prop_df['over_line']
698
+ prop_df['Under'] = 1 / prop_df['under_line']
699
+
700
+ prop_dict = dict(zip(prop_df.Player, prop_df.Prop))
701
+ prop_type_dict = dict(zip(prop_df.Player, prop_df.prop_type))
702
+ book_dict = dict(zip(prop_df.Player, prop_df.book))
703
+ over_dict = dict(zip(prop_df.Player, prop_df.Over))
704
+ under_dict = dict(zip(prop_df.Player, prop_df.Under))
705
+ trending_over_dict = dict(zip(prop_df.Player, prop_df['Trending Over']))
706
+ trending_under_dict = dict(zip(prop_df.Player, prop_df['Trending Under']))
707
+
708
+ player_df['book'] = player_df['Player'].map(book_dict)
709
+ player_df['Prop'] = player_df['Player'].map(prop_dict)
710
+ player_df['prop_type'] = player_df['Player'].map(prop_type_dict)
711
+ player_df['Trending Over'] = player_df['Player'].map(trending_over_dict)
712
+ player_df['Trending Under'] = player_df['Player'].map(trending_under_dict)
713
+
714
+ df = player_df.reset_index(drop=True)
715
+
716
+ team_dict = dict(zip(df.Player, df.Team))
717
+
718
+ total_sims = 1000
719
+
720
+ df.replace("", 0, inplace=True)
721
+
722
+ if prop_type_var == "NFL_GAME_PLAYER_PASSING_YARDS" or prop_type_var == "Passing Yards":
723
+ df['Median'] = df['pass_yards']
724
+ elif prop_type_var == "NFL_GAME_PLAYER_RUSHING_YARDS" or prop_type_var == "Rushing Yards":
725
+ df['Median'] = df['rush_yards']
726
+ elif prop_type_var == "NFL_GAME_PLAYER_PASSING_ATTEMPTS" or prop_type_var == "Passing Attempts":
727
+ df['Median'] = df['pass_att']
728
+ elif prop_type_var == "NFL_GAME_PLAYER_PASSING_TOUCHDOWNS" or prop_type_var == "Passing TDs":
729
+ df['Median'] = df['pass_tds']
730
+ elif prop_type_var == "NFL_GAME_PLAYER_RUSHING_ATTEMPTS" or prop_type_var == "Rushing Attempts":
731
+ df['Median'] = df['rush_att']
732
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_RECEPTIONS" or prop_type_var == "Receptions":
733
+ df['Median'] = df['rec']
734
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_YARDS" or prop_type_var == "Receiving Yards":
735
+ df['Median'] = df['rec_yards']
736
+ elif prop_type_var == "NFL_GAME_PLAYER_RECEIVING_TOUCHDOWNS" or prop_type_var == "Receiving TDs":
737
+ df['Median'] = df['rec_tds']
738
+ elif prop_type_var == "Rush + Rec Yards":
739
+ df['Median'] = df['rush_yards'] + df['rec_yards']
740
+ elif prop_type_var == "Rush + Rec TDs":
741
+ df['Median'] = df['rush_tds'] + df['rec_tds']
742
+
743
+ flex_file = df.copy()
744
+ flex_file['Floor'] = flex_file['Median'] * .25
745
+ flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * 1.75)
746
+ flex_file['STD'] = flex_file['Median'] / 4
747
+ flex_file['Prop'] = flex_file['Player'].map(prop_dict)
748
+ flex_file = flex_file[['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']]
749
+
750
+ hold_file = flex_file.copy()
751
+ overall_file = flex_file.copy()
752
+ prop_file = flex_file.copy()
753
+
754
+ overall_players = overall_file[['Player']]
755
+
756
+ for x in range(0,total_sims):
757
+ prop_file[x] = prop_file['Prop']
758
+
759
+ prop_file = prop_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
760
+
761
+ for x in range(0,total_sims):
762
+ overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD'])
763
+
764
+ overall_file=overall_file.drop(['Player', 'book', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
765
+
766
+ players_only = hold_file[['Player']]
767
+
768
+ player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True)
769
+
770
+ prop_check = (overall_file - prop_file)
771
+
772
+ players_only['Mean_Outcome'] = overall_file.mean(axis=1)
773
+ players_only['Book'] = players_only['Player'].map(book_dict)
774
+ players_only['Prop'] = players_only['Player'].map(prop_dict)
775
+ players_only['Trending Over'] = players_only['Player'].map(trending_over_dict)
776
+ players_only['Trending Under'] = players_only['Player'].map(trending_under_dict)
777
+ players_only['poisson_var'] = players_only.apply(calculate_poisson, axis=1)
778
+ players_only['10%'] = overall_file.quantile(0.1, axis=1)
779
+ players_only['90%'] = overall_file.quantile(0.9, axis=1)
780
+ players_only['Over'] = np_where(players_only['Prop'] <= 3, players_only['poisson_var'], prop_check[prop_check > 0].count(axis=1)/float(total_sims))
781
+ players_only['Imp Over'] = players_only['Player'].map(over_dict)
782
+ players_only['Over%'] = players_only[["Over", "Imp Over", "Trending Over"]].mean(axis=1)
783
+ 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))
784
+ players_only['Imp Under'] = players_only['Player'].map(under_dict)
785
+ players_only['Under%'] = players_only[["Under", "Imp Under", "Trending Under"]].mean(axis=1)
786
+ players_only['Prop_avg'] = players_only['Prop'].mean() / 100
787
+ players_only['prop_threshold'] = .10
788
+ players_only = players_only[players_only['Mean_Outcome'] > 0]
789
+ players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over']
790
+ players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under']
791
+ players_only['Bet_check'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], players_only['Over_diff'] , players_only['Under_diff'])
792
+ players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under")
793
+ players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet")
794
+ players_only['Edge'] = players_only['Bet_check']
795
+ players_only['Prop Type'] = prop_type_var
796
+
797
+ players_only['Player'] = hold_file[['Player']]
798
+ players_only['Team'] = players_only['Player'].map(team_dict)
799
+
800
+ leg_outcomes = players_only[['Player', 'Team', 'Book', 'Prop Type', 'Prop', 'Mean_Outcome', 'Imp Over', 'Trending Over', 'Over%', 'Imp Under', 'Trending Under', 'Under%', 'Bet?', 'Edge']]
801
+ sim_all_hold = pd.concat([sim_all_hold, leg_outcomes], ignore_index=True)
802
+
803
+ final_outcomes = sim_all_hold
804
+ st.write(f'finished {prop_type_var} for {books}')
805
+
806
+ final_outcomes = final_outcomes.dropna()
807
+ if game_select_var == 'Pick6':
808
+ final_outcomes = final_outcomes.drop_duplicates(subset=['Player', 'Prop Type'])
809
+ final_outcomes = final_outcomes.sort_values(by='Edge', ascending=False)
810
 
811
+ with df_hold_container:
812
+ df_hold_container = st.empty()
813
+ st.dataframe(final_outcomes.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True)
814
+ with export_container:
815
+ export_container = st.empty()
816
+ st.download_button(
817
+ label="Export Projections",
818
+ data=convert_df_to_csv(final_outcomes),
819
+ file_name='NFL_prop_proj.csv',
820
+ mime='text/csv',
821
+ key='prop_proj',
822
+ )