James McCool commited on
Commit
42541ad
·
1 Parent(s): a15c0a1

Add lineup optimization feature in app.py: Implemented a function to generate optimal NFL lineups based on median projections, incorporating position and salary constraints. Added UI button to trigger lineup generation and display results, enhancing user experience for DraftKings and FanDuel users.

Browse files
Files changed (1) hide show
  1. app.py +84 -0
app.py CHANGED
@@ -121,6 +121,73 @@ t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
121
 
122
  tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(["Team Stacks Range of Outcomes", "Overall Range of Outcomes", "QB Range of Outcomes", "RB Range of Outcomes", "WR Range of Outcomes", "TE Range of Outcomes", "Optimals"])
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  with st.sidebar:
125
  st.header("Quick Builder")
126
 
@@ -136,6 +203,23 @@ with st.sidebar:
136
  roo_sample = roo_sample[roo_sample['version'] == 'overall']
137
  #roo_sample = roo_sample.sort_values(by='Own', ascending=False)
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  # Create empty lists to store selected players
140
  selected_qbs = []
141
  selected_rb1 = []
 
121
 
122
  tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(["Team Stacks Range of Outcomes", "Overall Range of Outcomes", "QB Range of Outcomes", "RB Range of Outcomes", "WR Range of Outcomes", "TE Range of Outcomes", "Optimals"])
123
 
124
+ # ... existing code ...
125
+
126
+ @st.cache_data
127
+ def optimize_lineup(player_data):
128
+ """
129
+ Creates optimal lineup based on median projections while respecting position and salary constraints
130
+ """
131
+ # Create optimization problem
132
+ prob = pulp.LpProblem("NFL_Lineup_Optimization", pulp.LpMaximize)
133
+
134
+ # Create player dictionary with binary variables
135
+ players = {}
136
+ for idx, row in player_data.iterrows():
137
+ players[row['Player']] = pulp.LpVariable(f"player_{idx}", 0, 1, pulp.LpBinary)
138
+
139
+ # Objective: Maximize total median points
140
+ prob += pulp.lpSum([players[row['Player']] * row['Median'] for idx, row in player_data.iterrows()])
141
+
142
+ # Constraint: Salary cap
143
+ prob += pulp.lpSum([players[row['Player']] * row['Salary'] for idx, row in player_data.iterrows()]) <= 50000
144
+
145
+ # Position constraints
146
+ qbs = player_data[player_data['Position'] == 'QB']['Player'].tolist()
147
+ rbs = player_data[player_data['Position'] == 'RB']['Player'].tolist()
148
+ wrs = player_data[player_data['Position'] == 'WR']['Player'].tolist()
149
+ tes = player_data[player_data['Position'] == 'TE']['Player'].tolist()
150
+ dsts = player_data[player_data['Position'] == 'DST']['Player'].tolist()
151
+
152
+ # QB: exactly 1
153
+ prob += pulp.lpSum([players[p] for p in qbs]) == 1
154
+
155
+ # RB: 2-3
156
+ prob += pulp.lpSum([players[p] for p in rbs]) >= 2
157
+ prob += pulp.lpSum([players[p] for p in rbs]) <= 3
158
+
159
+ # WR: 3-4
160
+ prob += pulp.lpSum([players[p] for p in wrs]) >= 3
161
+ prob += pulp.lpSum([players[p] for p in wrs]) <= 4
162
+
163
+ # TE: 1-2
164
+ prob += pulp.lpSum([players[p] for p in tes]) >= 1
165
+ prob += pulp.lpSum([players[p] for p in tes]) <= 2
166
+
167
+ # DST: exactly 1
168
+ prob += pulp.lpSum([players[p] for p in dsts]) == 1
169
+
170
+ # Solve the problem
171
+ prob.solve()
172
+
173
+ # Get selected players
174
+ selected_players = []
175
+ total_salary = 0
176
+ total_median = 0
177
+
178
+ for idx, row in player_data.iterrows():
179
+ if players[row['Player']].value() == 1:
180
+ selected_players.append({
181
+ 'Player': row['Player'],
182
+ 'Position': row['Position'],
183
+ 'Salary': row['Salary'],
184
+ 'Median': row['Median']
185
+ })
186
+ total_salary += row['Salary']
187
+ total_median += row['Median']
188
+
189
+ return selected_players, total_salary, total_median
190
+
191
  with st.sidebar:
192
  st.header("Quick Builder")
193
 
 
203
  roo_sample = roo_sample[roo_sample['version'] == 'overall']
204
  #roo_sample = roo_sample.sort_values(by='Own', ascending=False)
205
 
206
+ st.write("---")
207
+ if st.button("Generate Optimal Lineup"):
208
+ if sidebar_site == 'Draftkings':
209
+ roo_data = dk_roo_raw[dk_roo_raw['slate'] == str(sidebar_slate)]
210
+ roo_data = roo_data[roo_data['version'] == 'overall']
211
+ else:
212
+ roo_data = fd_roo_raw[fd_roo_raw['slate'] == str(sidebar_slate)]
213
+ roo_data = roo_data[roo_data['version'] == 'overall']
214
+
215
+ optimal_players, total_salary, total_median = optimize_lineup(roo_data)
216
+
217
+ st.write("Optimal Lineup:")
218
+ for player in optimal_players:
219
+ st.write(f"{player['Position']}: {player['Player']} (${player['Salary']:,})")
220
+ st.write(f"Total Salary: ${total_salary:,}")
221
+ st.write(f"Projected Points: {total_median:.2f}")
222
+
223
  # Create empty lists to store selected players
224
  selected_qbs = []
225
  selected_rb1 = []