fantasyfb 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. fantasyfb/__init__.py +41 -0
  2. fantasyfb/analysis/__init__.py +1 -0
  3. fantasyfb/analysis/move_analyzer.py +639 -0
  4. fantasyfb/analysis/war_calculator.py +165 -0
  5. fantasyfb/cli.py +202 -0
  6. fantasyfb/configs.py +166 -0
  7. fantasyfb/data/__init__.py +1 -0
  8. fantasyfb/data/nfl_provider.py +102 -0
  9. fantasyfb/data/nflreadpy_provider.py +367 -0
  10. fantasyfb/data/player_data_manager.py +440 -0
  11. fantasyfb/data/yahoo_client.py +466 -0
  12. fantasyfb/drafts/__init__.py +1 -0
  13. fantasyfb/drafts/prep.py +314 -0
  14. fantasyfb/drafts/salary_cap.py +388 -0
  15. fantasyfb/drafts/snake.py +554 -0
  16. fantasyfb/drafts/snake_cockpit.py +288 -0
  17. fantasyfb/drafts/tools.py +638 -0
  18. fantasyfb/io/__init__.py +1 -0
  19. fantasyfb/io/excel_exporter.py +252 -0
  20. fantasyfb/league.py +830 -0
  21. fantasyfb/projections/__init__.py +1 -0
  22. fantasyfb/projections/engine.py +312 -0
  23. fantasyfb/projections/engine_v2.py +321 -0
  24. fantasyfb/projections/model_fitter.py +227 -0
  25. fantasyfb/scoring/__init__.py +1 -0
  26. fantasyfb/scoring/fantasy_scoring.py +202 -0
  27. fantasyfb/scoring/lineup_optimizer.py +197 -0
  28. fantasyfb/scoring/matchup_model.py +275 -0
  29. fantasyfb/sim/__init__.py +1 -0
  30. fantasyfb/sim/backtest.py +267 -0
  31. fantasyfb/sim/schedule_manager.py +142 -0
  32. fantasyfb/sim/season_simulator.py +489 -0
  33. fantasyfb-0.3.0.dist-info/METADATA +150 -0
  34. fantasyfb-0.3.0.dist-info/RECORD +38 -0
  35. fantasyfb-0.3.0.dist-info/WHEEL +5 -0
  36. fantasyfb-0.3.0.dist-info/entry_points.txt +5 -0
  37. fantasyfb-0.3.0.dist-info/licenses/LICENSE +21 -0
  38. fantasyfb-0.3.0.dist-info/top_level.txt +1 -0
fantasyfb/__init__.py ADDED
@@ -0,0 +1,41 @@
1
+ """fantasyfb: fantasy football league simulation and optimization toolkit.
2
+
3
+ Public API
4
+ ----------
5
+ The most commonly used entry points are re-exported here so that
6
+ ``import fantasyfb as fb`` continues to work after the package
7
+ restructuring (League, FantasyScorer, projection / matchup models, etc.).
8
+ For lower-level helpers, import directly from the relevant submodule:
9
+
10
+ from fantasyfb.drafts.tools import compute_vorp, assign_tiers
11
+ from fantasyfb.sim.backtest import run_backtest
12
+ from fantasyfb.io.excel_exporter import FantasyExcelExporter
13
+ """
14
+
15
+ from .league import League
16
+ from .scoring.fantasy_scoring import FantasyScorer
17
+ from .scoring.matchup_model import MatchupModel
18
+ from .scoring.lineup_optimizer import LineupOptimizer
19
+ from .projections.engine_v2 import ProjectionEngineV2
20
+ from .sim.season_simulator import SeasonSimulator
21
+ from .sim.schedule_manager import ScheduleManager
22
+ from .data.nfl_provider import NFLDataProvider
23
+ from .data.nflreadpy_provider import NflreadpyProvider
24
+ from .configs import get_league_config, apply_default_scoring_categories
25
+
26
+ __version__ = "0.3.0"
27
+
28
+ __all__ = [
29
+ "League",
30
+ "FantasyScorer",
31
+ "MatchupModel",
32
+ "LineupOptimizer",
33
+ "ProjectionEngineV2",
34
+ "SeasonSimulator",
35
+ "ScheduleManager",
36
+ "NFLDataProvider",
37
+ "NflreadpyProvider",
38
+ "get_league_config",
39
+ "apply_default_scoring_categories",
40
+ "__version__",
41
+ ]
@@ -0,0 +1 @@
1
+ """WAR calculation, move analysis, and post-hoc evaluators."""
@@ -0,0 +1,639 @@
1
+ """
2
+ Fantasy football roster move analysis.
3
+
4
+ This module analyzes the impact of potential roster moves including
5
+ pickups, adds, drops, and trades on team performance.
6
+ """
7
+
8
+ import datetime
9
+ import pandas as pd
10
+
11
+
12
+ class MoveAnalyzer:
13
+ """
14
+ Analyzes the impact of potential roster moves on team performance.
15
+ """
16
+
17
+ def __init__(self, league):
18
+ """
19
+ Initialize the move analyzer.
20
+
21
+ Args:
22
+ league: League instance with all the data and simulation methods
23
+ """
24
+ self.league = league
25
+
26
+ def possible_pickups(
27
+ self,
28
+ focus_on: list = [],
29
+ exclude: list = [],
30
+ limit_per: int = 10,
31
+ team_name: str = None,
32
+ postseason: bool = True,
33
+ verbose: bool = True,
34
+ payouts: list = [800, 300, 100],
35
+ bestball: bool = False,
36
+ min_rostership: float = 0.05,
37
+ ):
38
+ """
39
+ Simulates the remainder of the season with the current roster and compares it to
40
+ a simulation of the roster after a series of potential add & drop transactions.
41
+
42
+ Args:
43
+ focus_on (list, optional): list of players to include in every potential trade, defaults to [].
44
+ exclude (list, optional): list of players to exclude from every potential trade, defaults to [].
45
+ limit_per (int, optional): number of players per position to analyze, defaults to 10.
46
+ team_name (str, optional): name of team to analyze trades for, defaults to None (and therefore team of interest).
47
+ postseason (bool, optional): whether to analyze postseason gains or just regular season, defaults to True.
48
+ verbose (bool, optional): whether to print out a status report as the code runs, defaults to True.
49
+ payouts (list, optional): list of payout amounts for top three finishers, defaults to [800, 300, 100].
50
+ bestball (bool, optional): whether to use best ball settings during simulation, defaults to False.
51
+
52
+ Returns:
53
+ pd.DataFrame: dataframe containing the impact and value of every add & drop combination analyzed.
54
+ """
55
+ as_of = self.league.season * 100 + self.league.week
56
+ self.league.yahoo_client.refresh_oauth()
57
+ if bestball:
58
+ orig_standings = self.league.bestball_sims(payouts)
59
+ else:
60
+ orig_standings = self.league.season_sims(postseason, payouts)[1]
61
+ added_value = pd.DataFrame(
62
+ columns=[
63
+ "player_to_drop",
64
+ "player_to_add",
65
+ "wins_avg",
66
+ "wins_stdev",
67
+ "points_avg",
68
+ "points_stdev",
69
+ "per_game_avg",
70
+ "per_game_stdev",
71
+ "per_game_fano",
72
+ "playoffs",
73
+ "playoff_bye",
74
+ ]
75
+ + (
76
+ ["winner", "runner_up", "third", "earnings"]
77
+ if postseason
78
+ else []
79
+ )
80
+ )
81
+ if not team_name:
82
+ team_name = [
83
+ team["name"]
84
+ for team in self.league.teams
85
+ if team["team_key"] == self.league.lg.team_key()
86
+ ][0]
87
+ players_to_drop = self.league.players.loc[self.league.players.fantasy_team == team_name]
88
+ if players_to_drop.name.isin(focus_on).sum() > 0:
89
+ players_to_drop = players_to_drop.loc[players_to_drop.name.isin(focus_on)]
90
+ if players_to_drop.name.isin(exclude).sum() > 0:
91
+ players_to_drop = players_to_drop.loc[~players_to_drop.name.isin(exclude)]
92
+ available = self.league.players.loc[self.league.players.fantasy_team.isnull() \
93
+ & (self.league.players.until.isnull() | (self.league.players.until < 17)) \
94
+ & (self.league.players.pct_rostered >= min_rostership)].reset_index(drop=True)
95
+ for my_player in players_to_drop.name:
96
+ self.league.yahoo_client.refresh_oauth(55)
97
+ if (
98
+ players_to_drop.loc[players_to_drop.name == my_player, "until"].values[
99
+ 0
100
+ ]
101
+ >= as_of % 100
102
+ ):
103
+ possible = available.loc[~available.name.str.contains("Average_")]
104
+ else:
105
+ possible = available.loc[
106
+ ~available.name.str.contains("Average_")
107
+ & (
108
+ available.WAR
109
+ >= self.league.players.loc[
110
+ self.league.players.name == my_player, "WAR"
111
+ ].values[0]
112
+ - 0.5
113
+ )
114
+ ]
115
+ if available.name.isin(focus_on).sum() > 0:
116
+ possible = possible.loc[possible.name.isin(focus_on)]
117
+ if possible.name.isin(exclude).sum() > 0:
118
+ possible = possible.loc[~possible.name.isin(exclude)]
119
+ if verbose:
120
+ print(my_player + ": " + str(possible.shape[0]) + " better players")
121
+ print(datetime.datetime.now())
122
+ possible = possible.groupby("position").head(limit_per)
123
+ for free_agent in possible.name:
124
+ self.league.players.loc[self.league.players.name == my_player, "fantasy_team"] = None
125
+ self.league.players.loc[
126
+ self.league.players.name == free_agent, "fantasy_team"
127
+ ] = team_name
128
+ if bestball:
129
+ new_standings = self.league.bestball_sims(payouts)
130
+ else:
131
+ new_standings = self.league.season_sims(postseason, payouts)[1]
132
+ added_value = pd.concat([added_value,
133
+ new_standings.loc[new_standings.team == team_name]],
134
+ ignore_index=True,
135
+ sort=False,
136
+ )
137
+ added_value.loc[added_value.shape[0] - 1, "player_to_drop"] = my_player
138
+ added_value.loc[added_value.shape[0] - 1, "player_to_add"] = free_agent
139
+ self.league.players.loc[
140
+ self.league.players.name == my_player, "fantasy_team"
141
+ ] = team_name
142
+ self.league.players.loc[self.league.players.name == free_agent, "fantasy_team"] = None
143
+ if verbose:
144
+ temp = added_value.iloc[-1 * possible.shape[0] :][
145
+ ["player_to_drop", "player_to_add", "earnings"]
146
+ ]
147
+ temp["earnings"] -= orig_standings.loc[
148
+ orig_standings.team == team_name, "earnings"
149
+ ].values[0]
150
+ if temp.shape[0] > 0:
151
+ print(
152
+ temp.sort_values(by="earnings", ascending=False).to_string(
153
+ index=False
154
+ )
155
+ )
156
+ del temp
157
+ if added_value.shape[0] > 0:
158
+ for col in [
159
+ "wins_avg",
160
+ "wins_stdev",
161
+ "points_avg",
162
+ "points_stdev",
163
+ "playoffs",
164
+ "playoff_bye",
165
+ ] + (
166
+ ["winner", "runner_up", "third", "earnings"]
167
+ if postseason
168
+ else []
169
+ ):
170
+ added_value[col] -= orig_standings.loc[
171
+ orig_standings.team == team_name, col
172
+ ].values[0]
173
+ added_value[col] = round(added_value[col], 4)
174
+ added_value = added_value.sort_values(
175
+ by="winner" if postseason else "playoffs", ascending=False
176
+ )
177
+ return added_value
178
+
179
+ def possible_adds(
180
+ self,
181
+ focus_on: list = [],
182
+ exclude: list = [],
183
+ limit_per: int = 10,
184
+ team_name: str = None,
185
+ postseason: bool = True,
186
+ verbose: bool = True,
187
+ payouts: list = [800, 300, 100],
188
+ bestball: bool = False,
189
+ min_rostership: float = 0.05,
190
+ ):
191
+ """
192
+ Simulates the remainder of the season with the current roster and compares it to
193
+ a simulation of the roster after a series of potential add transactions.
194
+
195
+ Args:
196
+ focus_on (list, optional): list of players to include in every potential trade, defaults to [].
197
+ exclude (list, optional): list of players to exclude from every potential trade, defaults to [].
198
+ limit_per (int, optional): number of players per position to analyze, defaults to 10.
199
+ team_name (str, optional): name of team to analyze trades for, defaults to None (and therefore team of interest).
200
+ postseason (bool, optional): whether to analyze postseason gains or just regular season, defaults to True.
201
+ verbose (bool, optional): whether to print out a status report as the code runs, defaults to True.
202
+ payouts (list, optional): list of payout amounts for top three finishers, defaults to [800, 300, 100].
203
+ bestball (bool, optional): whether to use best ball settings during simulation, defaults to False.
204
+
205
+ Returns:
206
+ pd.DataFrame: dataframe containing the impact and value of every possible add analyzed.
207
+ """
208
+ as_of = self.league.season * 100 + self.league.week
209
+ self.league.yahoo_client.refresh_oauth()
210
+ if bestball:
211
+ orig_standings = self.league.bestball_sims(payouts)
212
+ else:
213
+ orig_standings = self.league.season_sims(postseason, payouts)[1]
214
+ added_value = pd.DataFrame(
215
+ columns=[
216
+ "player_to_add",
217
+ "wins_avg",
218
+ "wins_stdev",
219
+ "points_avg",
220
+ "points_stdev",
221
+ "per_game_avg",
222
+ "per_game_stdev",
223
+ "per_game_fano",
224
+ "playoffs",
225
+ "playoff_bye",
226
+ ]
227
+ + (
228
+ ["winner", "runner_up", "third", "earnings"]
229
+ if postseason
230
+ else []
231
+ )
232
+ )
233
+ if not team_name:
234
+ team_name = [
235
+ team["name"]
236
+ for team in self.league.teams
237
+ if team["team_key"] == self.league.lg.team_key()
238
+ ][0]
239
+ available = self.league.players.loc[self.league.players.fantasy_team.isnull() \
240
+ & (self.league.players.until.isnull() | (self.league.players.until < 17)) \
241
+ & (self.league.players.pct_rostered >= min_rostership)].reset_index(drop=True)
242
+ possible = available.loc[~available.name.str.contains("Average_")]
243
+ if possible.name.isin(focus_on).sum() > 0:
244
+ possible = possible.loc[possible.name.isin(focus_on)]
245
+ if possible.name.isin(exclude).sum() > 0:
246
+ possible = possible.loc[~possible.name.isin(exclude)]
247
+ possible = possible.groupby("position").head(limit_per)
248
+ for free_agent in possible.name:
249
+ if verbose:
250
+ print("{}, {}".format(free_agent, datetime.datetime.now()))
251
+ self.league.players.loc[
252
+ self.league.players.name == free_agent, "fantasy_team"
253
+ ] = team_name
254
+ if bestball:
255
+ new_standings = self.league.bestball_sims(payouts)
256
+ else:
257
+ new_standings = self.league.season_sims(postseason, payouts)[1]
258
+ added_value = pd.concat([added_value,
259
+ new_standings.loc[new_standings.team == team_name]],
260
+ ignore_index=True,
261
+ sort=False,
262
+ )
263
+ added_value.loc[added_value.shape[0] - 1, "player_to_add"] = free_agent
264
+ added_value.loc[added_value.shape[0] - 1, "position"] = possible.loc[
265
+ possible.name == free_agent, "position"
266
+ ].values[0]
267
+ added_value.loc[added_value.shape[0] - 1, "current_team"] = possible.loc[
268
+ possible.name == free_agent, "current_team"
269
+ ].values[0]
270
+ self.league.players.loc[self.league.players.name == free_agent, "fantasy_team"] = None
271
+ if added_value.shape[0] > 0:
272
+ for col in [
273
+ "wins_avg",
274
+ "wins_stdev",
275
+ "points_avg",
276
+ "points_stdev",
277
+ "playoffs",
278
+ "playoff_bye",
279
+ ] + (
280
+ ["winner", "runner_up", "third", "earnings"]
281
+ if postseason
282
+ else []
283
+ ):
284
+ added_value[col] -= orig_standings.loc[
285
+ orig_standings.team == team_name, col
286
+ ].values[0]
287
+ added_value[col] = round(added_value[col], 4)
288
+ added_value = added_value.sort_values(
289
+ by="winner" if postseason else "playoffs", ascending=False
290
+ )
291
+ if verbose:
292
+ print(
293
+ added_value[["player_to_add", "earnings"]]
294
+ .sort_values(by="earnings", ascending=False)
295
+ .to_string(index=False)
296
+ )
297
+ return added_value
298
+
299
+ def possible_drops(
300
+ self,
301
+ focus_on: list = [],
302
+ exclude: list = [],
303
+ team_name: str = None,
304
+ postseason: bool = True,
305
+ verbose: bool = True,
306
+ payouts: list = [800, 300, 100],
307
+ bestball: bool = False,
308
+ ):
309
+ """
310
+ Simulates the remainder of the season with the current roster and compares it to
311
+ a simulation of the roster after a series of potential drop transactions.
312
+
313
+ Args:
314
+ focus_on (list, optional): list of players to include in every potential trade, defaults to [].
315
+ exclude (list, optional): list of players to exclude from every potential trade, defaults to [].
316
+ team_name (str, optional): name of team to analyze trades for, defaults to None (and therefore team of interest).
317
+ postseason (bool, optional): whether to analyze postseason gains or just regular season, defaults to True.
318
+ verbose (bool, optional): whether to print out a status report as the code runs, defaults to True.
319
+ payouts (list, optional): list of payout amounts for top three finishers, defaults to [800, 300, 100].
320
+ bestball (bool, optional): whether to use best ball settings during simulation, defaults to False.
321
+
322
+ Returns:
323
+ pd.DataFrame: dataframe containing the impact and value of every possible drop analyzed.
324
+ """
325
+ self.league.yahoo_client.refresh_oauth()
326
+ if bestball:
327
+ orig_standings = self.league.bestball_sims(payouts)
328
+ else:
329
+ orig_standings = self.league.season_sims(postseason, payouts)[1]
330
+ reduced_value = pd.DataFrame(
331
+ columns=[
332
+ "player_to_drop",
333
+ "wins_avg",
334
+ "wins_stdev",
335
+ "points_avg",
336
+ "points_stdev",
337
+ "per_game_avg",
338
+ "per_game_stdev",
339
+ "per_game_fano",
340
+ "playoffs",
341
+ "playoff_bye",
342
+ ]
343
+ + (
344
+ ["winner", "runner_up", "third", "earnings"]
345
+ if postseason
346
+ else []
347
+ )
348
+ )
349
+ if not team_name:
350
+ team_name = [
351
+ team["name"]
352
+ for team in self.league.teams
353
+ if team["team_key"] == self.league.lg.team_key()
354
+ ][0]
355
+ players_to_drop = self.league.players.loc[self.league.players.fantasy_team == team_name]
356
+ if players_to_drop.name.isin(focus_on).sum() > 0:
357
+ players_to_drop = players_to_drop.loc[players_to_drop.name.isin(focus_on)]
358
+ if players_to_drop.name.isin(exclude).sum() > 0:
359
+ players_to_drop = players_to_drop.loc[~players_to_drop.name.isin(exclude)]
360
+ for my_player in players_to_drop.name:
361
+ self.league.players.loc[self.league.players.name == my_player, "fantasy_team"] = None
362
+ if bestball:
363
+ new_standings = self.league.bestball_sims(payouts)
364
+ else:
365
+ new_standings = self.league.season_sims(postseason, payouts)[1]
366
+ reduced_value = pd.concat([reduced_value,
367
+ new_standings.loc[new_standings.team == team_name]],
368
+ ignore_index=True,
369
+ sort=False,
370
+ )
371
+ reduced_value.loc[reduced_value.shape[0] - 1, "player_to_drop"] = my_player
372
+ self.league.players.loc[self.league.players.name == my_player, "fantasy_team"] = team_name
373
+ if reduced_value.shape[0] > 0:
374
+ for col in [
375
+ "wins_avg",
376
+ "wins_stdev",
377
+ "points_avg",
378
+ "points_stdev",
379
+ "playoffs",
380
+ "playoff_bye",
381
+ ] + (
382
+ ["winner", "runner_up", "third", "earnings"]
383
+ if postseason
384
+ else []
385
+ ):
386
+ reduced_value[col] -= orig_standings.loc[
387
+ orig_standings.team == team_name, col
388
+ ].values[0]
389
+ reduced_value[col] = round(reduced_value[col], 4)
390
+ reduced_value = reduced_value.sort_values(
391
+ by="winner" if postseason else "playoffs", ascending=False
392
+ )
393
+ if verbose:
394
+ print(
395
+ reduced_value[["player_to_drop", "earnings"]]
396
+ .sort_values(by="earnings", ascending=False)
397
+ .to_string(index=False)
398
+ )
399
+ return reduced_value
400
+
401
+ def possible_trades(
402
+ self,
403
+ focus_on: list = [],
404
+ exclude: list = [],
405
+ given: list = [],
406
+ limit_per: int = 10,
407
+ team_name: str = None,
408
+ postseason: bool = True,
409
+ verbose: bool = True,
410
+ payouts: list = [800, 300, 100],
411
+ bestball: bool = False,
412
+ ):
413
+ """
414
+ Simulates the remainder of the season with the current roster and compares it to
415
+ a simulation of the roster after a series of potential trade transactions.
416
+
417
+ Args:
418
+ focus_on (list, optional): list of players to include in every potential trade, defaults to [].
419
+ exclude (list, optional): list of players to exclude from every potential trade, defaults to [].
420
+ given (list, optional): list of players to include in the trade in the background, defaults to [].
421
+ limit_per (int, optional): number of players per position to analyze, defaults to 10.
422
+ team_name (str, optional): name of team to analyze trades for, defaults to None (and therefore team of interest).
423
+ postseason (bool, optional): whether to analyze postseason gains or just regular season, defaults to True.
424
+ verbose (bool, optional): whether to print out a status report as the code runs, defaults to True.
425
+ payouts (list, optional): list of payout amounts for top three finishers, defaults to [800, 300, 100].
426
+ bestball (bool, optional): whether to use best ball settings during simulation, defaults to False.
427
+
428
+ Returns:
429
+ pd.DataFrame: dataframe containing the impact and value of every possible trade analyzed.
430
+ """
431
+ self.league.yahoo_client.refresh_oauth()
432
+ if not team_name:
433
+ team_name = [
434
+ team["name"]
435
+ for team in self.league.teams
436
+ if team["team_key"] == self.league.lg.team_key()
437
+ ][0]
438
+ my_players = self.league.players.loc[
439
+ (self.league.players.fantasy_team == team_name)
440
+ & ~self.league.players.position.isin(["K", "DEF"])
441
+ ]
442
+ if my_players.name.isin(focus_on).sum() > 0:
443
+ my_players = my_players.loc[my_players.name.isin(focus_on)]
444
+ if my_players.name.isin(exclude).sum() > 0:
445
+ my_players = my_players.loc[~my_players.name.isin(exclude)]
446
+ their_players = self.league.players.loc[
447
+ (self.league.players.fantasy_team != team_name)
448
+ & ~self.league.players.position.isin(["K", "DEF"])
449
+ ]
450
+ if their_players.name.isin(focus_on).sum() > 0:
451
+ their_players = their_players.loc[their_players.name.isin(focus_on)]
452
+ if their_players.name.isin(exclude).sum() > 0:
453
+ their_players = their_players.loc[~their_players.name.isin(exclude)]
454
+ if bestball:
455
+ orig_standings = self.league.bestball_sims(payouts)
456
+ else:
457
+ orig_standings = self.league.season_sims(postseason, payouts)[1]
458
+
459
+ # Make sure there are two teams and narrow down to that team!!!
460
+ given_check = (
461
+ type(given) == list
462
+ and my_players.name.isin(given).any()
463
+ and their_players.loc[their_players.name.isin(given), "fantasy_team"]
464
+ .unique()
465
+ .shape[0]
466
+ == 1
467
+ )
468
+ if given_check:
469
+ mine = [player for player in given if my_players.name.isin([player]).any()]
470
+ theirs = [
471
+ player for player in given if their_players.name.isin([player]).any()
472
+ ]
473
+ their_team = self.league.players.loc[
474
+ self.league.players.name.isin(theirs), "fantasy_team"
475
+ ].values[0]
476
+ self.league.players.loc[self.league.players.name.isin(mine), "fantasy_team"] = their_team
477
+ self.league.players.loc[self.league.players.name.isin(theirs), "fantasy_team"] = team_name
478
+ my_players = my_players.loc[~my_players.name.isin(given)]
479
+ their_players = their_players.loc[
480
+ (their_players.fantasy_team == their_team)
481
+ & ~their_players.name.isin(given)
482
+ ]
483
+ my_players["WAR"] = 0.0
484
+ their_players["WAR"] = 0.0
485
+ # Make sure there are two teams and narrow down to that teams!!!
486
+
487
+ my_added_value = pd.DataFrame()
488
+ their_added_value = pd.DataFrame()
489
+ for my_player in my_players.name:
490
+ self.league.yahoo_client.refresh_oauth(55)
491
+ if their_players.name.isin(focus_on).any():
492
+ possible = their_players.copy()
493
+ else:
494
+ possible = their_players.loc[
495
+ abs(
496
+ their_players.WAR
497
+ - my_players.loc[my_players.name == my_player, "WAR"].values[0]
498
+ )
499
+ <= 0.5
500
+ ]
501
+ # possible = their_players.loc[their_players.WAR - my_players.loc[my_players.name == my_player,'WAR'].values[0] > -1.0]
502
+ if verbose:
503
+ print(my_player + ": " + str(possible.shape[0]) + " comparable players")
504
+ print(datetime.datetime.now())
505
+ possible = possible.groupby("position").head(limit_per)
506
+ for their_player in possible.name:
507
+ their_team = self.league.players.loc[
508
+ self.league.players.name == their_player, "fantasy_team"
509
+ ].values[0]
510
+ self.league.players.loc[
511
+ self.league.players.name == my_player, "fantasy_team"
512
+ ] = their_team
513
+ self.league.players.loc[
514
+ self.league.players.name == their_player, "fantasy_team"
515
+ ] = team_name
516
+ if bestball:
517
+ new_standings = self.league.bestball_sims(payouts)
518
+ else:
519
+ new_standings = self.league.season_sims(postseason, payouts)[1]
520
+ self.league.players.loc[
521
+ self.league.players.name == my_player, "fantasy_team"
522
+ ] = team_name
523
+ self.league.players.loc[
524
+ self.league.players.name == their_player, "fantasy_team"
525
+ ] = their_team
526
+ new_standings["player_to_trade_away"] = my_player
527
+ new_standings["player_to_trade_for"] = their_player
528
+ my_added_value = pd.concat([my_added_value,
529
+ new_standings.loc[new_standings.team == team_name]],
530
+ ignore_index=True,
531
+ )
532
+ their_added_value = pd.concat([their_added_value,
533
+ new_standings.loc[new_standings.team == their_team]],
534
+ ignore_index=True,
535
+ )
536
+ if verbose and possible.shape[0] > 0:
537
+ me = my_added_value.iloc[-1 * possible.shape[0] :][
538
+ ["player_to_trade_away", "player_to_trade_for", "earnings"]
539
+ ].rename(columns={"earnings": "my_earnings"})
540
+ them = their_added_value.iloc[-1 * possible.shape[0] :][
541
+ ["player_to_trade_away", "player_to_trade_for", "team", "earnings"]
542
+ ].rename(columns={"earnings": "their_earnings"})
543
+ me["my_earnings"] -= orig_standings.loc[
544
+ orig_standings.team == team_name, "earnings"
545
+ ].values[0]
546
+ for their_team in them.team.unique():
547
+ them.loc[
548
+ them.team == their_team, "their_earnings"
549
+ ] -= orig_standings.loc[
550
+ orig_standings.team == their_team, "earnings"
551
+ ].values[
552
+ 0
553
+ ]
554
+ temp = pd.merge(
555
+ left=me,
556
+ right=them,
557
+ how="inner",
558
+ on=["player_to_trade_away", "player_to_trade_for"],
559
+ )
560
+ if temp.shape[0] > 0:
561
+ print(
562
+ temp.sort_values(by="my_earnings", ascending=False).to_string(
563
+ index=False
564
+ )
565
+ )
566
+ del me, them, temp, their_team
567
+
568
+ if given_check:
569
+ mine = [player for player in given if my_players.name.isin([player]).any()]
570
+ theirs = [
571
+ player for player in given if their_players.name.isin([player]).any()
572
+ ]
573
+ their_team = self.league.players.loc[
574
+ self.league.players.name.isin(theirs), "fantasy_team"
575
+ ].values[0]
576
+ self.league.players.loc[self.league.players.name.isin(mine), "fantasy_team"] = their_team
577
+ self.league.players.loc[self.league.players.name.isin(theirs), "fantasy_team"] = team_name
578
+
579
+ for col in [
580
+ "wins_avg",
581
+ "wins_stdev",
582
+ "points_avg",
583
+ "points_stdev",
584
+ "per_game_avg",
585
+ "per_game_stdev",
586
+ "per_game_fano",
587
+ "playoffs",
588
+ "playoff_bye",
589
+ ] + (["winner", "runner_up", "third", "earnings"] if postseason else []):
590
+ my_added_value[col] -= orig_standings.loc[
591
+ orig_standings.team == team_name, col
592
+ ].values[0]
593
+ my_added_value[col] = round(my_added_value[col], 4)
594
+ for their_team in their_added_value.team.unique():
595
+ for col in [
596
+ "wins_avg",
597
+ "wins_stdev",
598
+ "points_avg",
599
+ "points_stdev",
600
+ "per_game_avg",
601
+ "per_game_stdev",
602
+ "per_game_fano",
603
+ "playoffs",
604
+ "playoff_bye",
605
+ ] + (["winner", "runner_up", "third", "earnings"] if postseason else []):
606
+ their_added_value.loc[
607
+ their_added_value.team == their_team, col
608
+ ] -= orig_standings.loc[orig_standings.team == their_team, col].values[
609
+ 0
610
+ ]
611
+ their_added_value[col] = round(their_added_value[col], 4)
612
+ for col in [
613
+ "team",
614
+ "wins_avg",
615
+ "wins_stdev",
616
+ "points_avg",
617
+ "points_stdev",
618
+ "per_game_avg",
619
+ "per_game_stdev",
620
+ "per_game_fano",
621
+ "playoffs",
622
+ "playoff_bye",
623
+ ] + (["winner", "runner_up", "third", "earnings"] if postseason else []):
624
+ my_added_value = my_added_value.rename(
625
+ index=str, columns={col: "my_" + col}
626
+ )
627
+ their_added_value = their_added_value.rename(
628
+ index=str, columns={col: "their_" + col}
629
+ )
630
+ added_value = pd.merge(
631
+ left=my_added_value,
632
+ right=their_added_value,
633
+ how="inner",
634
+ on=["player_to_trade_away", "player_to_trade_for"],
635
+ )
636
+ added_value = added_value.sort_values(
637
+ by="my_winner" if postseason else "playoffs", ascending=False
638
+ )
639
+ return added_value