DFS_NBA_TEST / appBackup.py
acetwo3ee's picture
Upload 8 files
dc03771 verified
Raw History Blame Contribute Delete
15.7 kB
import streamlit as st
import pandas as pd
import numpy as np
import gspread
from google.oauth2.service_account import Credentials
from itertools import combinations
from collections import Counter
from tqdm import tqdm
from data_processor import DataProcessor
# Set page config to wide layout
st.set_page_config(layout="wide")
# Google Sheets Config
SHEET_ID = "1PCugIPyYM1lF6KOCPUY5hL8xbGI5PWd7gVvCyudrB5c"
SHEET_NAME = "Dream11 NBA Data"
OUTPUT_SHEETS = ["Optimized_Lineups", "Boom_Bust", "Ownership%"]
# Service Account Info (unchanged)
SERVICE_ACCOUNT_INFO = {
"type": "service_account",
"project_id": "abiding-state-451516-e6",
"private_key_id": "668f5f97d95b999d0ad706445e1a9a624b6bb263",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCCIBM+5nspxseN\nD/Fls62ZRrCpPhqLX2TS+EDBFwhOtwlLmFVWGUUBBGVXhLRzrLS550p5toyhfOIU\nR2Bd0copSmLkglEwuUyClbswz+MJD4JCVuKbLugm5rMxJSuCwH/vXyvV8LtJESjK\nsxfQZ5uLkXS6i+S+SlNgR5TcdxskUa0/ymCEjiLojrbaZ64eiM75b/gakljI40ee\nZMWV6awSe2+EQnQ2W27H0uk4B2cMjEA0q1SHDRyLhwMNeaW3DIrCmUZntUev/KdD\nsamjgPPA8Z2VMvYh0ONr131EFlXq8atbUwHeefWLsRxeNmpzruJtASv10Y8fa/wl\nzADUaU+9AgMBAAECggEAJxI2dSaDSLH07PgihWUD9Qfyt358Pejwvx/nYZLm3au7\n0MT2qpl/zKRjQXoJLMpooYE/LXiQlVSIFYqw9rqtNuSs+OvgMP8SaAtJX4isJOZR\nOFYaSHo3HqcamuEMRYBDab5rzRsxQOk0FFjeB6d0Xt3X7MNgdXqfukMEAYaJ0cd+\n4LOhlMaDQ8jXdnthXh55Gz7Qyk0W5AjnckWdlaaNZ2y1MVI9zdJ4qDBdreua9p5g\n3mgBe93s2KbCEKBSzCTsoU1GbGw5sBP2w7MyPvXuyIgk2BNdDH5Sz+u/8Wdm7gIh\n8EXwdxWcDJRYxm5tTErqgGDDEvCB/bXaU0Yt2IiKSQKBgQC2mmmUGEUM87b7hOtC\n1+hpEf/iucHaGBrkxW0Fcz2/Gi5mPeb60gbMtbxmFMNPdDaqKQxi5KjhLP/iZgRk\nVWJJsJb/P8xuL64y8sI6NUdTGqTUxqB6CJOriMkLI0b6JVK/vTj3Hd88VthdYwNC\nFWvFi0ckT9Ab3Fp9+309oav6ZwKBgQC2bcTjuwsiXRKzTYoODoF/Z/+NIPCI4BZ5\nwUAwY/otQ0/dQB7B5+DruLnIWCVM4PzXoPvpLgrs/iiyFPDGTlYtUa2rBTfKHr13\nUA0Y97uRvBd6004RAbM/xHoWeX2uSRtD8kzs5TZGlzgPyS0FT7b9nb+1iWPrcMJy\naoQI3hhWOwKBgAlqLrVfBvhi/HU9Zi/dF/OK5QmYkEEhawmOkE9uoKlgvTGyUV1v\nRMB7Qkp1GZjYIhVl5miW54osmsjef614pYetHivs6ypnNPEViSQdKR9EyWjbEN0Z\n4hs/AzaazAcLs5eFNxGurZrb024FM97NYfAefT2HC/DTzKqgM9XgpxBTAoGBAKEW\nQLuO2IAZi3/EwlkzOepU3PWz1nIm3fHZUWLphWoefBE0o3Q52wSK85ITM6y1Js8J\nGiiHPaKTsz+8+fdCCRqvexj+3IqfplIK3+XiK0C+xETy/YZK6Asw5VWfJfmWGHth\nI8gzhj/DG//kdtqvzzgX6cbVcOPfh/QcVka2RustAoGBAJHVNd9QSSd5Z8aFTHmf\nv+oKjnQKzGY7j+K+bU3yxPHxfDrN5ticodaHxbho2VFaI17qcFVHkkX12k2I2O1Z\nV8Nve8i2JkFVEftVpSQ/01AhsbZflDm8ORJet2M8R2o5bcZ4Yc8/shI/j7Sz8BeM\nYMSoAW+xwwELc6a+5m8u60ZU\n-----END PRIVATE KEY-----\n",
"client_email": "dream11-optimizer@abiding-state-451516-e6.iam.gserviceaccount.com",
"client_id": "102537539700971787452",
"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/dream11-optimizer%40abiding-state-451516-e6.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
# Authenticate Google Sheets
@st.cache_resource
def connect_google_sheets():
try:
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
creds = Credentials.from_service_account_info(SERVICE_ACCOUNT_INFO, scopes=scope)
client = gspread.authorize(creds)
return client
except Exception as e:
st.error(f"Failed to connect to Google Sheets: {str(e)}")
return None
# Fetch player data from Google Sheets
def fetch_player_data():
client = connect_google_sheets()
if not client:
return None
try:
sheet = client.open_by_key(SHEET_ID).worksheet(SHEET_NAME)
df = pd.DataFrame(sheet.get_all_records())
if df.empty:
st.error("No data found in Google Sheet")
return None
required_columns = ["Player", "Position", "Credits", "Projected Points", "Team Name", "Lineup Status"]
missing_required = [col for col in required_columns if col not in df.columns]
if missing_required:
raise ValueError(f"Missing required columns: {missing_required}")
df["Credits"] = pd.to_numeric(df["Credits"], errors="coerce").fillna(0)
df["Projected Points"] = pd.to_numeric(df["Projected Points"], errors="coerce").fillna(0)
if "Std Dev" in df.columns:
df["Std Dev"] = pd.to_numeric(df["Std Dev"], errors="coerce").fillna(0)
else:
df["Std Dev"] = 0
return df
except Exception as e:
st.error(f"Error fetching player data: {str(e)}")
return None
# Generate valid lineups with progress bar
def generate_valid_lineups(players, min_stack=0, max_exposure=100, num_lineups=10):
valid_lineups = []
player_combinations = combinations(players, 8)
player_usage = Counter()
progress_bar = st.progress(0)
total_combinations = min(num_lineups, sum(1 for _ in player_combinations))
player_combinations = combinations(players, 8) # Recreate iterator
for i, lineup in enumerate(tqdm(player_combinations, total=total_combinations, desc="Generating Lineups")):
lineup_df = pd.DataFrame(lineup)
lineup_df["Position"] = lineup_df["Position"].str.strip()
total_credits = lineup_df["Credits"].sum()
total_points = lineup_df["Projected Points"].sum()
# Position constraints
position_counts = {"PG": 0, "SG": 0, "SF": 0, "PF": 0, "C": 0}
for pos in lineup_df["Position"]:
if pos in position_counts:
position_counts[pos] += 1
if not all(1 <= position_counts[pos] <= 4 for pos in position_counts):
continue
# Team constraints
team_counts = lineup_df["Team Name"].value_counts()
if min_stack > 0 and team_counts.max() < min_stack:
continue
if team_counts.max() > 5:
continue
# Salary cap
if not (48500 <= total_credits <= 50000):
continue
# Exposure check
if any(player_usage.get(p, 0) / (len(valid_lineups) + 1) * 100 > max_exposure for p in lineup_df["Player"]):
continue
valid_lineups.append((lineup_df, total_credits, total_points, position_counts, team_counts))
for player in lineup_df["Player"]:
player_usage[player] += 1
progress_bar.progress(min((i + 1) / total_combinations, 1.0))
if len(valid_lineups) >= num_lineups:
break
progress_bar.empty()
return sorted(valid_lineups, key=lambda x: x[2], reverse=True), player_usage
# Generate accurate Boom/Bust analysis
def generate_boom_bust_sheet(players_df):
boom_bust_data = []
for _, row in players_df.iterrows():
proj_points = row["Projected Points"]
std_dev = row["Std Dev"]
if std_dev == 0:
pos_factor = {"PG": 0.25, "SG": 0.22, "SF": 0.20, "PF": 0.18, "C": 0.15}.get(row["Position"], 0.20)
std_dev = proj_points * pos_factor
boom_points = proj_points + 1.5 * std_dev
bust_points = max(0, proj_points - 1.5 * std_dev)
boom_prob = 1 - 0.9332
bust_prob = 0.0668
boom_bust_data.append([
row["Player"], row["Team Name"], proj_points,
round(boom_points, 1), round(bust_points, 1),
round(boom_prob * 100, 1), round(bust_prob * 100, 1)
])
return pd.DataFrame(boom_bust_data, columns=[
"Player", "Team", "Projected Points", "Boom Points", "Bust Points",
"Boom Probability %", "Bust Probability %"
])
# Save data to Google Sheets
def save_to_google_sheets(sheet_name, df):
client = connect_google_sheets()
if not client:
return
try:
sheet = client.open_by_key(SHEET_ID).worksheet(sheet_name)
sheet.clear()
sheet.update([df.columns.tolist()] + df.values.tolist())
except Exception as e:
st.error(f"Error saving to {sheet_name}: {str(e)}")
# Generate Ownership % Data with Player Count and Corrected Value Score
def generate_ownership_sheet(players_df, player_usage, total_lineups):
ownership_data = []
for _, row in players_df.iterrows():
player_name = row["Player"]
player_count = player_usage.get(player_name, 0)
ownership_pct = (player_count / total_lineups) * 100 if total_lineups > 0 else 0
value_score = (row["Projected Points"] * 500) / row["Credits"] if row["Credits"] > 0 else 0
ownership_data.append([
player_name, row["Team Name"], row["Credits"], row["Projected Points"],
player_count, round(ownership_pct, 2), round(value_score, 2)
])
return pd.DataFrame(ownership_data, columns=[
"Player", "Team", "Credits", "Projected Points", "Count", "Ownership %", "Value"
])
# Filter lineups based on include or exclude logic
def filter_lineups(df_lineups, players, filter_type):
if not players or len(players) == 0:
return df_lineups
filtered = df_lineups
if filter_type == "Include Players":
# Include only lineups where ALL selected players are present
for player in players:
filtered = filtered[filtered[["Player 1", "Player 2", "Player 3", "Player 4",
"Player 5", "Player 6", "Player 7", "Player 8"]].apply(
lambda row: player in row.values, axis=1)]
elif filter_type == "Exclude Players":
# Exclude lineups where ANY of the selected players are present
for player in players:
filtered = filtered[~filtered[["Player 1", "Player 2", "Player 3", "Player 4",
"Player 5", "Player 6", "Player 7", "Player 8"]].apply(
lambda row: player in row.values, axis=1)]
return filtered
def main():
st.title("NBA DFS Optimizer")
# Initialize components
data_processor = DataProcessor()
optimizer = DFSOptimizer()
# Sidebar controls
st.sidebar.header("Optimization Settings")
num_lineups = st.sidebar.slider("Number of Lineups", 1, 50, 10)
min_stack = st.sidebar.slider("Minimum Stack", 1, 5, 2)
max_stack = st.sidebar.slider("Maximum Stack", min_stack, 5, 4)
max_exposure = st.sidebar.slider("Maximum Exposure %", 20, 100, 60) / 100
# Load data
df = fetch_player_data()
if df is None:
st.error("Failed to load player data")
return
try:
data_processor.load_data(df)
except ValueError as e:
st.error(str(e))
return
# Sidebar controls
with st.sidebar:
st.header("Optimization Settings")
min_stack = st.slider("Min players from same team (stacking)", 0, 5, 0)
max_exposure = st.slider("Max player exposure (%)", 10, 100, 100)
num_lineups = st.number_input("Number of lineups to generate", 1, 10000, 10)
if st.button("Run Optimization"):
with st.spinner("Fetching data..."):
df = fetch_player_data()
if df is None:
return
st.session_state.df = df
players = df.to_dict("records")
st.write(f"Generating {num_lineups} Optimized Lineups...")
# Generate Lineups
valid_lineups, player_usage = generate_valid_lineups(players, min_stack, max_exposure, num_lineups)
if not valid_lineups:
st.error("No valid lineups found with given constraints")
return
# Prepare lineup data
lineup_data = []
team_headers = sorted(set(df["Team Name"]))
for lineup_df, credits, points, pos_counts, team_counts in valid_lineups:
pos_counts_str = f"{pos_counts['PG']}-{pos_counts['SG']}-{pos_counts['SF']}-{pos_counts['PF']}-{pos_counts['C']}"
yes_count = sum(lineup_df["Lineup Status"] == "Yes")
no_count = sum(lineup_df["Lineup Status"] == "No")
team_columns = {team: team_counts.get(team, 0) for team in team_headers}
row = lineup_df["Player"].tolist() + [credits, points, pos_counts_str, yes_count, no_count] + list(team_columns.values())
lineup_data.append(row)
# Store in session state
st.session_state.df_lineups = pd.DataFrame(lineup_data, columns=["Player 1", "Player 2", "Player 3", "Player 4", "Player 5",
"Player 6", "Player 7", "Player 8", "Credits", "FP",
"Pos_Count", "Yes", "No"] + team_headers)
st.session_state.filtered_lineups = st.session_state.df_lineups.copy()
st.session_state.player_usage = player_usage
st.session_state.lineups_generated = True
# Save all lineups to Google Sheets
save_to_google_sheets("Optimized_Lineups", st.session_state.df_lineups)
# Generate and save Boom/Bust Sheet
boom_bust_df = generate_boom_bust_sheet(df)
save_to_google_sheets("Boom_Bust", boom_bust_df)
# Generate and save Ownership Sheet
ownership_df = generate_ownership_sheet(df, player_usage, len(valid_lineups))
save_to_google_sheets("Ownership%", ownership_df)
# Display results if lineups are generated
if st.session_state.lineups_generated and st.session_state.df_lineups is not None:
# Player filter in sidebar with include/exclude options
with st.sidebar:
st.header("Filter Lineups")
total_players = st.session_state.df["Player"].tolist()
filter_option = st.selectbox("Filter by", ["All", "Include Players", "Exclude Players"], key="filter_option")
if filter_option in ["Include Players", "Exclude Players"]:
players_to_filter = st.multiselect(
f"Which players would you like to {'include in' if filter_option == 'Include Players' else 'exclude from'} the lineups?",
options=total_players,
key="player_filter"
)
else:
players_to_filter = []
# Apply filter based on selected option
filtered_lineups = filter_lineups(st.session_state.df_lineups, players_to_filter, filter_option)
st.session_state.filtered_lineups = filtered_lineups
# Display Filtered Optimized Lineups with vertical scroll
st.subheader("Optimized Lineups")
if len(filtered_lineups) > 0:
st.dataframe(
filtered_lineups.style.format({
"Credits": "{:.0f}",
"FP": "{:.1f}",
"Yes": "{:.0f}",
"No": "{:.0f}"
}),
use_container_width=True,
height=400 # Fixed height with vertical scrollbar
)
else:
st.warning(f"No lineups match the selected filter ({filter_option}).")
st.write("Generated Lineups Sample:", st.session_state.df_lineups.head())
st.write(f"{'Players to Include' if filter_option == 'Include Players' else 'Players to Exclude' if filter_option == 'Exclude Players' else 'No Filter'}:", players_to_filter)
st.success(f"✅ Generated {len(st.session_state.df_lineups)} lineups (showing {len(filtered_lineups)} after filtering) and saved to Google Sheets!")
if __name__ == "__main__":
main()