import pulp import numpy as np import pandas as pd import random import sys import openpyxl import re import time import streamlit as st import matplotlib from matplotlib.colors import LinearSegmentedColormap from st_aggrid import GridOptionsBuilder, AgGrid, GridUpdateMode, DataReturnMode import json import requests import gspread import plotly.figure_factory as ff scope = ['https://www.googleapis.com/auth/spreadsheets', "https://www.googleapis.com/auth/drive"] credentials = { "type": "service_account", "project_id": "sheets-api-connect-378620", "private_key_id": "1005124050c80d085e2c5b344345715978dd9cc9", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtKa01beXwc88R\nnPZVQTNPVQuBnbwoOfc66gW3547ja/UEyIGAF112dt/VqHprRafkKGmlg55jqJNt\na4zceLKV+wTm7vBu7lDISTJfGzCf2TrxQYNqwMKE2LOjI69dBM8u4Dcb4k0wcp9v\ntW1ZzLVVuwTvmrg7JBHjiSaB+x5wxm/r3FOiJDXdlAgFlytzqgcyeZMJVKKBQHyJ\njEGg/1720A0numuOCt71w/2G0bDmijuj1e6tH32MwRWcvRNZ19K9ssyDz2S9p68s\nYDhIxX69OWxwScTIHLY6J2t8txf/XMivL/636fPlDADvBEVTdlT606n8CcKUVQeq\npUVdG+lfAgMBAAECggEAP38SUA7B69eTfRpo658ycOs3Amr0JW4H/bb1rNeAul0K\nZhwd/HnU4E07y81xQmey5kN5ZeNrD5EvqkZvSyMJHV0EEahZStwhjCfnDB/cxyix\nZ+kFhv4y9eK+kFpUAhBy5nX6T0O+2T6WvzAwbmbVsZ+X8kJyPuF9m8ldcPlD0sce\ntj8NwVq1ys52eosqs7zi2vjt+eMcaY393l4ls+vNq8Yf27cfyFw45W45CH/97/Nu\n5AmuzlCOAfFF+z4OC5g4rei4E/Qgpxa7/uom+BVfv9G0DIGW/tU6Sne0+37uoGKt\nW6DzhgtebUtoYkG7ZJ05BTXGp2lwgVcNRoPwnKJDxQKBgQDT5wYPUBDW+FHbvZSp\nd1m1UQuXyerqOTA9smFaM8sr/UraeH85DJPEIEk8qsntMBVMhvD3Pw8uIUeFNMYj\naLmZFObsL+WctepXrVo5NB6RtLB/jZYxiKMatMLUJIYtcKIp+2z/YtKiWcLnwotB\nWdCjVnPTxpkurmF2fWP/eewZ+wKBgQDRMtJg7etjvKyjYNQ5fARnCc+XsI3gkBe1\nX9oeXfhyfZFeBXWnZzN1ITgFHplDznmBdxAyYGiQdbbkdKQSghviUQ0igBvoDMYy\n1rWcy+a17Mj98uyNEfmb3X2cC6WpvOZaGHwg9+GY67BThwI3FqHIbyk6Ko09WlTX\nQpRQjMzU7QKBgAfi1iflu+q0LR+3a3vvFCiaToskmZiD7latd9AKk2ocsBd3Woy9\n+hXXecJHPOKV4oUJlJgvAZqe5HGBqEoTEK0wyPNLSQlO/9ypd+0fEnArwFHO7CMF\nycQprAKHJXM1eOOFFuZeQCaInqdPZy1UcV5Szla4UmUZWkk1m24blHzXAoGBAMcA\nyH4qdbxX9AYrC1dvsSRvgcnzytMvX05LU0uF6tzGtG0zVlub4ahvpEHCfNuy44UT\nxRWW/oFFaWjjyFxO5sWggpUqNuHEnRopg3QXx22SRRTGbN45li/+QAocTkgsiRh1\nqEcYZsO4mPCsQqAy6E2p6RcK+Xa+omxvSnVhq0x1AoGAKr8GdkCl4CF6rieLMAQ7\nLNBuuoYGaHoh8l5E2uOQpzwxVy/nMBcAv+2+KqHEzHryUv1owOi6pMLv7A9mTFoS\n18B0QRLuz5fSOsVnmldfC9fpUc6H8cH1SINZpzajqQA74bPwELJjnzrCnH79TnHG\nJuElxA33rFEjbgbzdyrE768=\n-----END PRIVATE KEY-----\n", "client_email": "gspread-connection@sheets-api-connect-378620.iam.gserviceaccount.com", "client_id": "106625872877651920064", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gspread-connection%40sheets-api-connect-378620.iam.gserviceaccount.com" } gc = gspread.service_account_from_dict(credentials) st.set_page_config(layout="wide") roo_format = {'Top_finish': '{:.2%}','Top_5_finish': '{:.2%}', 'Top_10_finish': '{:.2%}', '120+%': '{:.2%}','10x%': '{:.2%}','11x%': '{:.2%}','12x%': '{:.2%}','Own': '{:.2%}','LevX': '{:.2%}'} stat_format = {'Win%': '{:.2%}'} game_betting_model = 'https://docs.google.com/spreadsheets/d/1Yq0vGriWK-bS79e-bD6_u9pqrYE6Yrlbb_wEkmH-ot0/edit#gid=172632260' props_overall = 'DK_NBA_Props' player_overall = 'https://docs.google.com/spreadsheets/d/1Yq0vGriWK-bS79e-bD6_u9pqrYE6Yrlbb_wEkmH-ot0/edit#gid=172632260' points_overall = 'DK_Points_Props' assists_overall = 'DK_Assists_Props' rebounds_overall = 'DK_Rebounds_Props' pa_overall = 'DK_PA_Props' pr_overall = 'DK_PR_Props' pra_overall = 'DK_PRA_Props' @st.cache_data def create_player_props(URL): sh = gc.open_by_url(URL) worksheet = sh.get_worksheet(8) load_display = pd.DataFrame(worksheet.get_all_records()) overall_data = load_display[['Name', 'Position', 'Team', '3P', 'Points', 'Rebounds', 'Assists', 'Steals', 'Blocks']] overall_data.rename(columns={"Name": "player"}, inplace = True) overall_data['Points + Rebounds'] = overall_data['Points'] + overall_data['Rebounds'] overall_data['Points + Assists'] = overall_data['Points'] + overall_data['Assists'] overall_data['Points + Rebounds + Assists'] = overall_data['Points'] + overall_data['Rebounds'] + overall_data['Assists'] return overall_data @st.cache_data def load_game_betting(URL): sh = gc.open_by_url(URL) worksheet = sh.get_worksheet(1) raw_display = pd.DataFrame(worksheet.get_all_records()) return raw_display @st.cache_data def load_props(URL): sh = gc.open(URL) worksheet = sh.get_worksheet(0) raw_display = pd.DataFrame(worksheet.get_all_records()) raw_display.rename(columns={"player": "Player"}, inplace = True) return raw_display @st.cache_data def load_player_baselines(URL): sh = gc.open(URL) worksheet = sh.get_worksheet(0) raw_display = pd.DataFrame(worksheet.get_all_records()) return raw_display @st.cache_data def load_stat_specific(URL): sh = gc.open(URL) worksheet = sh.get_worksheet(0) raw_display = pd.DataFrame(worksheet.get_all_records()) raw_display.rename(columns={"player": "Player"}, inplace = True) raw_display = raw_display.drop(columns=['Model Probability', 'short%', 'mid%', 'long%', 's_weighted%', 'm_weighted%', 'l_weighted%', 'weighted prob%']) return raw_display team_frame = load_game_betting(game_betting_model) props_frame = create_player_props(player_overall) tab1, tab2, tab3, tab4 = st.tabs(["Game Betting Model", "Player Prop Baselines", "Stat Specific Props Projections", "Player Prop Simulations"]) def convert_df_to_csv(df): return df.to_csv().encode('utf-8') with tab1: if st.button("Reset Data/Load Data", key='reset1'): # Clear values from *all* all in-memory and on-disk data caches: # i.e. clear values from both square and cube st.cache_data.clear() st.dataframe(team_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True) st.download_button( label="Export Projections", data=convert_df_to_csv(team_frame), file_name='NBA_DFS_team_frame.csv', mime='text/csv', key='team_frame', ) with tab2: if st.button("Reset Data/Load Data", key='reset2'): # Clear values from *all* all in-memory and on-disk data caches: # i.e. clear values from both square and cube st.cache_data.clear() team_var1 = st.multiselect('View specific team?', options = props_frame['Team'].unique(), key = 'prop_teamvar') if team_var1: props_frame = props_frame[props_frame['Team'].isin(team_var1)] props_frame = props_frame.set_index('player') st.dataframe(props_frame.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True) st.download_button( label="Export Projections", data=convert_df_to_csv(props_frame), file_name='NBA_DFS_props_frame.csv', mime='text/csv', key='props_frame', ) with tab3: st.write("The Stat specific models are currently not accurate due to an API issue. Apoligies!") 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.') if st.button("Reset Data/Load Data", key='reset3'): # Clear values from *all* all in-memory and on-disk data caches: # i.e. clear values from both square and cube st.cache_data.clear() col1, col2 = st.columns([1, 5]) with col2: df_hold_container = st.empty() info_hold_container = st.empty() plot_hold_container = st.empty() export_container = st.empty() with col1: prop_type_var = st.selectbox('Select prop category', options = ['Points', 'Assists', 'Rebounds', 'Points + Assists', 'Points + Rebounds', 'Points + Rebounds + Assists']) if st.button('Simulate Prop Category'): with col2: with st.spinner('Wait for it...'): with df_hold_container.container(): if prop_type_var == "Points": player_df = load_stat_specific(points_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'points', 'over_points_line', 'under_points_line']] prop_df = prop_df.loc[prop_df['points'] > 0] prop_df['Over'] = np.where(prop_df['over_points_line'] < 0, (-(prop_df['over_points_line'])/((-(prop_df['over_points_line']))+100)), 100/(prop_df['over_points_line']+100)) prop_df['Under'] = np.where(prop_df['under_points_line'] < 0, (-(prop_df['under_points_line'])/((-(prop_df['under_points_line']))+100)), 100/(prop_df['under_points_line']+100)) prop_df.rename(columns={"points": "Prop"}, inplace = True) df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) elif prop_type_var == "Assists": player_df = load_stat_specific(assists_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'assists', 'over_assists_line', 'under_assists_line']] prop_df = prop_df.loc[prop_df['assists'] > 0] prop_df['Over'] = np.where(prop_df['over_assists_line'] < 0, (-(prop_df['over_assists_line'])/((-(prop_df['over_assists_line']))+100)), 100/(prop_df['over_assists_line']+100)) prop_df['Under'] = np.where(prop_df['under_assists_line'] < 0, (-(prop_df['under_assists_line'])/((-(prop_df['under_assists_line']))+100)), 100/(prop_df['under_assists_line']+100)) prop_df.rename(columns={"assists": "Prop"}, inplace = True) df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) elif prop_type_var == "Rebounds": player_df = load_stat_specific(rebounds_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'rebounds', 'over_rebounds_line', 'under_rebounds_line']] prop_df = prop_df.loc[prop_df['rebounds'] > 0] prop_df['Over'] = np.where(prop_df['over_rebounds_line'] < 0, (-(prop_df['over_rebounds_line'])/((-(prop_df['over_rebounds_line']))+100)), 100/(prop_df['over_rebounds_line']+100)) prop_df['Under'] = np.where(prop_df['under_rebounds_line'] < 0, (-(prop_df['under_rebounds_line'])/((-(prop_df['under_rebounds_line']))+100)), 100/(prop_df['under_rebounds_line']+100)) prop_df.rename(columns={"rebounds": "Prop"}, inplace = True) df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) elif prop_type_var == "Points + Assists": player_df = load_stat_specific(pa_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'points_assists', 'over_points_assists_line', 'under_points_assists_line']] prop_df = prop_df.loc[prop_df['points_assists'] > 0] prop_df['Over'] = np.where(prop_df['over_points_assists_line'] < 0, (-(prop_df['over_points_assists_line'])/((-(prop_df['over_points_assists_line']))+100)), 100/(prop_df['over_points_assists_line']+100)) prop_df['Under'] = np.where(prop_df['under_points_assists_line'] < 0, (-(prop_df['under_points_assists_line'])/((-(prop_df['under_points_assists_line']))+100)), 100/(prop_df['under_points_assists_line']+100)) prop_df.rename(columns={"points_assists": "Prop"}, inplace = True) df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) elif prop_type_var == "Points + Rebounds": player_df = load_stat_specific(pr_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'points_rebounds', 'over_points_rebounds_line', 'under_points_rebounds_line']] prop_df = prop_df.loc[prop_df['points_rebounds'] > 0] prop_df['Over'] = np.where(prop_df['over_points_rebounds_line'] < 0, (-(prop_df['over_points_rebounds_line'])/((-(prop_df['over_points_rebounds_line']))+100)), 100/(prop_df['over_points_rebounds_line']+100)) prop_df['Under'] = np.where(prop_df['under_points_rebounds_line'] < 0, (-(prop_df['under_points_rebounds_line'])/((-(prop_df['under_points_rebounds_line']))+100)), 100/(prop_df['under_points_rebounds_line']+100)) prop_df.rename(columns={"points_rebounds": "Prop"}, inplace = True) prop_df = prop_df[['Player', 'Prop', 'Over', 'Under']] df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) elif prop_type_var == "Points + Rebounds + Assists": player_df = load_stat_specific(pra_overall) prop_df = load_props(props_overall) prop_df = prop_df[['Player', 'points_rebounds_assists', 'over_points_rebounds_assists_line', 'under_points_rebounds_assists_line']] prop_df = prop_df.loc[prop_df['points_rebounds_assists'] > 0] prop_df['Over'] = np.where(prop_df['over_points_rebounds_assists_line'] < 0, (-(prop_df['over_points_rebounds_assists_line'])/((-(prop_df['over_points_rebounds_assists_line']))+100)), 100/(prop_df['over_points_rebounds_assists_line']+100)) prop_df['Under'] = np.where(prop_df['under_points_rebounds_assists_line'] < 0, (-(prop_df['under_points_rebounds_assists_line'])/((-(prop_df['under_points_rebounds_assists_line']))+100)), 100/(prop_df['under_points_rebounds_assists_line']+100)) prop_df.rename(columns={"points_rebounds_assists": "Prop"}, inplace = True) prop_df = prop_df[['Player', 'Prop', 'Over', 'Under']] df = pd.merge(player_df, prop_df, how='left', left_on=['Player'], right_on = ['Player']) df.rename(columns={"weighted%": "weighted"}, inplace = True) prop_dict = dict(zip(df.Player, df.Prop)) over_dict = dict(zip(df.Player, df.Over)) under_dict = dict(zip(df.Player, df.Under)) weighted_dict = dict(zip(df.Player, df.weighted)) total_sims = 1000 df.replace("", 0, inplace=True) if prop_type_var == "Points": df['Median'] = df['Points'] elif prop_type_var == "Assists": df['Median'] = df['Assists'] elif prop_type_var == "Rebounds": df['Median'] = df['Rebounds'] elif prop_type_var == "Points + Assists": df['Median'] = df['Points + Assists'] elif prop_type_var == "Points + Rebounds": df['Median'] = df['Points + Rebounds'] elif prop_type_var == "Points + Rebounds + Assists": df['Median'] = df['Points + Rebounds + Assists'] flex_file = df flex_file['Floor'] = flex_file['Median'] * .20 flex_file['Ceiling'] = flex_file['Median'] + (flex_file['Median'] * .20) flex_file['STD'] = (flex_file['Median'] / 4) flex_file['Prop'] = flex_file['Player'].map(prop_dict) flex_file = flex_file[['Player', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD']] hold_file = flex_file overall_file = flex_file prop_file = flex_file overall_players = overall_file[['Player']] for x in range(0,total_sims): prop_file[x] = prop_file['Prop'] prop_file = prop_file.drop(['Player', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1) prop_file.astype('int').dtypes for x in range(0,total_sims): overall_file[x] = np.random.normal(overall_file['Median'],overall_file['STD']) overall_file=overall_file.drop(['Player', 'Prop', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1) overall_file.astype('int').dtypes players_only = hold_file[['Player']] player_outcomes = pd.merge(players_only, overall_file, left_index=True, right_index=True) prop_check = (overall_file - prop_file) players_only['Mean_Outcome'] = overall_file.mean(axis=1) players_only['Weighted_over'] = players_only['Player'].map(weighted_dict) players_only['Weighted_under'] = 1 - players_only['Player'].map(weighted_dict) players_only['10%'] = overall_file.quantile(0.1, axis=1) players_only['90%'] = overall_file.quantile(0.9, axis=1) players_only['Over'] = prop_check[prop_check >= 1].count(axis=1)/float(total_sims) players_only['Imp Over'] = players_only['Player'].map(over_dict) players_only['Over%'] = players_only[["Over", "Weighted_over", "Imp Over"]].mean(axis=1) players_only['Under'] = prop_check[prop_check < 1].count(axis=1)/float(total_sims) players_only['Imp Under'] = players_only['Player'].map(under_dict) players_only['Under%'] = players_only[["Under", "Weighted_under", "Imp Under"]].mean(axis=1) players_only['Prop'] = players_only['Player'].map(prop_dict) players_only['Prop_avg'] = players_only['Prop'].mean() / 100 players_only['prop_threshold'] = np.where(.25 - players_only['Prop_avg'] < .10, .10, .25 - players_only['Prop_avg']) players_only = players_only.loc[players_only['Mean_Outcome'] > 0] players_only['Over_diff'] = players_only['Over%'] - players_only['Imp Over'] players_only['Under_diff'] = players_only['Under%'] - players_only['Imp Under'] players_only['Bet_check'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], players_only['Over_diff'] , players_only['Under_diff']) players_only['Bet_suggest'] = np.where(players_only['Over_diff'] > players_only['Under_diff'], "Over" , "Under") players_only['Bet?'] = np.where(players_only['Bet_check'] >= players_only['prop_threshold'], players_only['Bet_suggest'], "No Bet") players_only['Edge'] = players_only['Bet_check'] players_only['Player'] = hold_file[['Player']] final_outcomes = players_only[['Player', 'Prop', 'Mean_Outcome', 'Imp Over', 'Over%', 'Imp Under', 'Under%', 'Bet?', 'Edge']] final_outcomes = final_outcomes.sort_values(by='Edge', ascending=False) final_outcomes = final_outcomes.set_index('Player') with df_hold_container: df_hold_container = st.empty() st.dataframe(final_outcomes.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), use_container_width = True) with export_container: export_container = st.empty() st.download_button( label="Export Projections", data=convert_df_to_csv(final_outcomes), file_name='NBA_DFS_prop_proj.csv', mime='text/csv', key='prop_proj', ) with tab4: st.info('Coming soon!')