fplkit 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/TOOLS.md +5 -5
- {fplkit-1.1.0 → fplkit-1.2.0}/.gitignore +1 -0
- fplkit-1.2.0/CHANGELOG.md +33 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/PKG-INFO +1 -1
- fplkit-1.2.0/config/fixture_predictions.yaml +41 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/docs/architecture.md +5 -5
- {fplkit-1.1.0 → fplkit-1.2.0}/docs/command-reference.md +7 -5
- {fplkit-1.1.0 → fplkit-1.2.0}/docs/custom-analysis.md +32 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/_version.py +2 -2
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/action/waiver.py +29 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/bench_order.py +16 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/captain.py +14 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/starting_xi.py +16 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/stats.py +46 -3
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/transfer_eval.py +39 -7
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/core_insights.py +155 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/historical_types.py +60 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/vaastav.py +2 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/differentials.py +8 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/player.py +97 -16
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/stats.py +42 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/targets.py +9 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/transfer_eval.py +7 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/waivers.py +9 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/player.py +2 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/types.py +8 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/player_prior.py +6 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/player_scoring.py +882 -50
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/squad_allocator.py +2 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_action.py +29 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_core_insights.py +305 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_historical.py +86 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_vaastav.py +42 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_differentials.py +14 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_player.py +192 -4
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_stats.py +2 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_targets.py +14 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_transfer_eval.py +31 -2
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_waivers.py +18 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_common_scoring.py +95 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_data_prep_harmonisation.py +11 -11
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_player_prior.py +72 -1
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_player_scoring.py +1603 -122
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_stats.py +75 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_transfer_eval_agent.py +48 -0
- fplkit-1.1.0/CHANGELOG.md +0 -10
- fplkit-1.1.0/config/fixture_predictions.yaml +0 -67
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/README.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/SKILL.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/references/output-template.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/references/rules.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/scripts/bench_order.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/scripts/starting_xi.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/gw-prep/scripts/transfer_eval.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/squad-builder/SKILL.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/squad-builder/references/output-template.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/squad-builder/references/rules.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.agents/skills/update-gw-prep/SKILL.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.claude/output-styles/fpl-mate.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.claude/settings.json +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.env.example +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.github/workflows/ci.yml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/.github/workflows/release.yml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/AGENTS.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/CLAUDE.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/LICENSE +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/README.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/cliff.toml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/config/team_managers.yaml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/config/team_ratings.yaml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/config/team_ratings_overrides.yaml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/docs/fpl-rules.md +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/docs/images/fpl-player-demo-v1-0.png +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/action/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/analysis/squad_analyzer.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/base.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/common.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/data/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/data/fixture.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/data/price.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/data/scout.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/orchestration/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/agents/orchestration/report.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/dataset_fetcher.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/football_data.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/fpl.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/fpl_draft.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/historical.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/providers/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/providers/_models.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/providers/anthropic.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/providers/openai_compat.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/providers/perplexity.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/api/understat.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_banner.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_context.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_fines.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_fines_config.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_helpers.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_json.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_league_recap_data.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_league_recap_types.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_plan_grid.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_review_analysis.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_review_classic.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_review_draft.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/_review_summarisation.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/allocate.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/captain.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/chips.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/credentials.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/fdr.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/fixtures.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/history.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/init.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/league.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/league_recap.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/preview.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/price_changes.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/price_history.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/ratings.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/review.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/sell_prices.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/squad.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/status.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/cli/xg.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/config/defaults.yaml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/constants.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/chip_plan.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/fixture.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/models/team.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/parsers/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/parsers/recommendations.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/paths.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/prompts/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/prompts/league_recap.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/prompts/review.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/prompts/scout.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/scraper/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/scraper/fpl_prices.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/season.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/fixture_predictions.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/matchup.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/team_form.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/team_ratings.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/services/team_ratings_prior.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/templates/gw_league_recap.md.j2 +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/templates/gw_preview.md.j2 +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/templates/gw_review.md.j2 +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/utils/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/fpl_cli/utils/text.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/pyproject.toml +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/pyrightconfig.json +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/requirements.lock +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/__init__.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/conftest.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_analysis.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_analysis_squad.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_base.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_data.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_data_price.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_agents_orchestration.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_allocate.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_client.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_dataset_fetcher.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_draft.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_football_data.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_api_understat.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_bench_order_script.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_allocate.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_banner.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_captain.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_chips.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_credentials.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_custom_analysis.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_fdr.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_fdr_blanks.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_fixtures.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_format.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_format_suppression.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_history.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_init.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_json.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_plan_grid.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_preview.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_price_history.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_review.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_sell_prices.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_squad.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_status.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_warnings_stderr.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_cli_xg.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_common.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_fines.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_fines_config.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_fixture_predictions.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_fpl_prices.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_init.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_init_fines.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_league_recap.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_matchup_service.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_models.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_paths.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_player_resolution.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_providers.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_recommendations_parser.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_report_agent.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_review.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_rolling_pts_per_m.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_scraper.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_season.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_sell_prices.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_squad_allocator.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_starting_xi_agent.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_team_ratings.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_team_ratings_prior.py +0 -0
- {fplkit-1.1.0 → fplkit-1.2.0}/tests/test_utils_text.py +0 -0
|
@@ -27,7 +27,7 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
27
27
|
| `init` | Set up fpl-cli with your FPL IDs and optional features | No | General | No | direct-api |
|
|
28
28
|
| `status` | Show FPL gameweek status and upcoming deadlines | Yes | General | No | direct-api |
|
|
29
29
|
| `fixtures` | Show fixtures for a gameweek | Yes | General | No | direct-api |
|
|
30
|
-
| `player` | Look up a player's stats, xG, ownership and fixture run. JSON includes `ep_next` (predicted pts next GW) and `ep_this` (current GW); panel shows `xPts` (ep_next) | Yes | General | No | direct-api |
|
|
30
|
+
| `player` | Look up a player's stats, xG, ownership and fixture run. JSON includes `ep_next` (predicted pts next GW) and `ep_this` (current GW); panel shows `xPts` (ep_next). `--detail` (`-d`) shows GW-by-GW performance and, for FWD/MID with sufficient history, xGI sustainability (per-match GI-xGI divergence -> form modifier). With `custom_analysis`: JSON adds `info.adjusted_npxg_per_90` (fixture-adjusted) and `info.raw_npxg_per_90` (Understat season avg); panel shows `adj. npxG/90: X.XXX (raw: Y.YYY)` | Yes | General | No | direct-api |
|
|
31
31
|
| `stats` | List players with filtering and sorting. `--value` adds quality/value per £m columns; `--window N` sets rolling lookback (3-10) for `rolling_pts_per_m`. Sortable by `ep_next`/`ep_this` (FPL predicted points); both in JSON output | Yes | General | No | direct-api |
|
|
32
32
|
| `history` | Show historical player performance across seasons | Yes | General | No | direct-api |
|
|
33
33
|
| `league` | Show live league standings for Classic and Draft leagues | No | General | No | direct-api |
|
|
@@ -38,10 +38,10 @@ Errors use `emit_json_error()` with `{"command", "error"}` shape.
|
|
|
38
38
|
| `preview` | Run full pre-gameweek analysis and generate report | No | General | No | via-agent |
|
|
39
39
|
| `review` | Review a completed gameweek - squad performance and standings | No | General | No | via-agent |
|
|
40
40
|
| `league-recap` | Recap a completed gameweek - awards, standings, and banter | No | General | No | via-agent |
|
|
41
|
-
| `captain` | Analyse and rank captain options for next gameweek | Yes | Classic | Yes | via-agent |
|
|
42
|
-
| `differentials` | Find differential picks - high potential, low ownership | Yes | Classic | Yes | via-agent |
|
|
43
|
-
| `targets` | Find transfer targets - high performers across all ownership | Yes | Classic | Yes | via-agent |
|
|
44
|
-
| `transfer-eval` | Compare transfer OUT player against IN candidates | Yes | General | Yes | via-agent |
|
|
41
|
+
| `captain` | Analyse and rank captain options for next gameweek. JSON candidates include `adjusted_npxg_per_90` and `raw_npxg_per_90` when fixture adjustment is active. Consistency tiebreaker (CV-xGI percentile) phased in GW6-10 | Yes | Classic | Yes | via-agent |
|
|
42
|
+
| `differentials` | Find differential picks - high potential, low ownership. Inverted consistency bonus (volatile players score higher, phased in GW6-10) | Yes | Classic | Yes | via-agent |
|
|
43
|
+
| `targets` | Find transfer targets - high performers across all ownership. Consistency bonus (CV-xGI percentile, phased in GW6-10) | Yes | Classic | Yes | via-agent |
|
|
44
|
+
| `transfer-eval` | Compare transfer OUT player against IN candidates. JSON includes `adjusted_npxg_per_90` and `raw_npxg_per_90` per player when fixture adjustment is active | Yes | General | Yes | via-agent |
|
|
45
45
|
| `allocate` | Select mathematically optimal 15-player squad within budget | Yes | Classic | Yes | direct-api |
|
|
46
46
|
| `waivers` | Show waiver recommendations for your draft league | Yes | Draft | Yes | via-agent |
|
|
47
47
|
| `squad` | Analyse your FPL squad health and fixtures | Yes | General | No | via-agent |
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
## [1.1.0] - 2026-04-07
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- wrap migration in try/except, update architecture.md paths
|
|
9
|
+
- CLI hygiene batch (P10-P13) (#4)
|
|
10
|
+
- Understat name matching for abbreviated/punctuated web_names
|
|
11
|
+
- PR #4 code review follow-ups (#5)
|
|
12
|
+
- review fixes - gather resilience, CSV field protection, test gaps
|
|
13
|
+
- correct README data source count and Core-Insights repo URL
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- add cached dataset fetcher with ETag conditional requests (#6)
|
|
18
|
+
- add rolling pts/£m metric and rename value fields (#7)
|
|
19
|
+
- expose ep_next/ep_this (FPL predicted points) (#8)
|
|
20
|
+
- implement CoreInsightsClient for 2025-26 season data
|
|
21
|
+
- add HistoricalDataProvider composition layer, update call sites
|
|
22
|
+
|
|
23
|
+
### Refactoring
|
|
24
|
+
|
|
25
|
+
- extract shared types, rename season helpers, separate cache dirs
|
|
26
|
+
|
|
27
|
+
## [1.0.0] - 2026-04-03
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
- CI pipeline and PyPI publish for v1.0.0 (#1)
|
|
32
|
+
|
|
33
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fplkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: CLI tool for Fantasy Premier League analysis - classic and draft formats
|
|
5
5
|
Project-URL: Homepage, https://github.com/rossgroomio/fpl-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/rossgroomio/fpl-cli
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
metadata:
|
|
2
|
+
last_updated: '2026-04-07'
|
|
3
|
+
notes: Updated from Ben Crellin BGW/DGW schedule (GW33-GW38)
|
|
4
|
+
predicted_blanks:
|
|
5
|
+
- gameweek: 34
|
|
6
|
+
teams:
|
|
7
|
+
- BHA
|
|
8
|
+
- BOU
|
|
9
|
+
- BUR
|
|
10
|
+
- CHE
|
|
11
|
+
- LEE
|
|
12
|
+
- MCI
|
|
13
|
+
confidence: confirmed
|
|
14
|
+
- gameweek: 37
|
|
15
|
+
teams:
|
|
16
|
+
- BOU
|
|
17
|
+
confidence: low
|
|
18
|
+
predicted_doubles:
|
|
19
|
+
- gameweek: 33
|
|
20
|
+
teams:
|
|
21
|
+
- BHA
|
|
22
|
+
- BOU
|
|
23
|
+
- BUR
|
|
24
|
+
- CHE
|
|
25
|
+
- LEE
|
|
26
|
+
- MCI
|
|
27
|
+
confidence: confirmed
|
|
28
|
+
- gameweek: 36
|
|
29
|
+
teams:
|
|
30
|
+
- CRY
|
|
31
|
+
- MCI
|
|
32
|
+
confidence: high
|
|
33
|
+
- gameweek: 36
|
|
34
|
+
teams:
|
|
35
|
+
- BOU
|
|
36
|
+
confidence: low
|
|
37
|
+
- gameweek: 37
|
|
38
|
+
teams:
|
|
39
|
+
- CRY
|
|
40
|
+
- MCI
|
|
41
|
+
confidence: low
|
|
@@ -361,8 +361,8 @@ Services live in `fpl_cli/services/` and provide the computation layer between a
|
|
|
361
361
|
|
|
362
362
|
| Service | Purpose |
|
|
363
363
|
|---|---|
|
|
364
|
-
| `player_scoring` | Central scoring engine: `prepare_scoring_data()`, all score functions, `shrink_scores()` |
|
|
365
|
-
| `player_prior` | Bayesian early-season confidence (GW1-10 shrinkage) |
|
|
364
|
+
| `player_scoring` | Central scoring engine: `prepare_scoring_data()`, all score functions, `shrink_scores()`. Form modifiers: `compute_form_trajectory()` (direction over recent GWs) and `compute_xgi_sustainability()` (ATK-only rolling xGI divergence -> [0.85, 1.15] multiplier). Fixture-adjusted npxG: `compute_adjusted_npxg()` / `build_adjusted_npxg_lookup()` normalise historical xG by opponent Elo; `apply_adjusted_npxg()` overwrites `npxG_per_90` in agent enrichment when data is available. Consistency signals: `build_consistency_lookup()` computes 5 per-player signals (CV-xGI percentile, blank rate, floor percentile, involvement rate, GK consistency) with GW6-10 phase-in; additive bonuses per scoring family (target/waiver cv*1.5, differential inverted cv*0.75, lineup cv*0.75, bench floor*1.5+inv*0.75) |
|
|
365
|
+
| `player_prior` | Bayesian early-season confidence (GW1-10 shrinkage); threads `PlayerProfile.reliability` (historical availability rate) through `PlayerPrior` to agents |
|
|
366
366
|
| `team_ratings` | TeamRatingsService + Calculator (1-7 scale, 4 axes) |
|
|
367
367
|
| `matchup` | Fixture matchup scoring (0-10), 3-GW recency-weighted |
|
|
368
368
|
| `fixture_predictions` | BGW/DGW predictions from YAML + live detection |
|
|
@@ -416,7 +416,7 @@ All providers share the `LLMResponse` contract. `OpenAICompatProvider` supports
|
|
|
416
416
|
| `FPLDraftClient` | FPL Draft API | Draft leagues, waivers, squad data |
|
|
417
417
|
| `UnderstatClient` | understat.com | npxG, xA, xGChain, xGBuildup per-90 stats |
|
|
418
418
|
| `VaastavClient` | vaastav/FPL GitHub | Historical CSV data (3 seasons: 2022-25), price trends, GW-level profiles |
|
|
419
|
-
| `CoreInsightsClient` | Core-Insights/FPL GitHub | Current-season CSV data (2025-26+), season aggregates, GW trends |
|
|
419
|
+
| `CoreInsightsClient` | Core-Insights/FPL GitHub | Current-season CSV data (2025-26+), season aggregates, GW trends, per-GW match-level xG + Elo (from `By Tournament/Premier League/GW{n}/`) |
|
|
420
420
|
| `HistoricalDataProvider` | Composition layer | Unifies vaastav + Core-Insights via `make_historical_provider()` |
|
|
421
421
|
| `FootballDataClient` | football-data.org | League standings, match results |
|
|
422
422
|
| `FPLPriceScraper` | FPL website | Price change scraping (needs credentials) |
|
|
@@ -516,7 +516,7 @@ fpl_cli/
|
|
|
516
516
|
│ ├── fpl.py # FPLClient (main API, caches bootstrap-static)
|
|
517
517
|
│ ├── fpl_draft.py # FPLDraftClient
|
|
518
518
|
│ ├── understat.py # UnderstatClient + match_fpl_to_understat()
|
|
519
|
-
│ ├── historical_types.py # Shared dataclasses (SeasonHistory, PlayerProfile, GwTrendProfile) + compute_trend/compute_acceleration
|
|
519
|
+
│ ├── historical_types.py # Shared dataclasses (SeasonHistory, PlayerProfile, GwTrendProfile) + compute_trend/compute_acceleration/compute_reliability
|
|
520
520
|
│ ├── vaastav.py # VaastavClient (historical seasons 2022-25 via DatasetFetcher)
|
|
521
521
|
│ ├── core_insights.py # CoreInsightsClient (current season 2025-26+ via DatasetFetcher)
|
|
522
522
|
│ ├── historical.py # HistoricalDataProvider (composition: vaastav + Core-Insights)
|
|
@@ -527,7 +527,7 @@ fpl_cli/
|
|
|
527
527
|
│ ├── openai_compat.py # OpenAICompatProvider (OpenAI, Groq, Together, Ollama)
|
|
528
528
|
│ └── perplexity.py # PerplexityProvider (extends OpenAICompat)
|
|
529
529
|
├── services/
|
|
530
|
-
│ ├── player_scoring.py # Scoring engines + prepare_scoring_data() + shrink_scores()
|
|
530
|
+
│ ├── player_scoring.py # Scoring engines + prepare_scoring_data() + shrink_scores() + consistency signals
|
|
531
531
|
│ ├── player_prior.py # Player prior (Bayesian early-season confidence)
|
|
532
532
|
│ ├── team_ratings.py # TeamRatingsService + Calculator (1-7 scale)
|
|
533
533
|
│ ├── team_ratings_prior.py # Pre-GW5 prior ratings for blending
|
|
@@ -57,7 +57,7 @@ fpl captain --format json
|
|
|
57
57
|
|
|
58
58
|
Output columns: Score, Atk, Def, Form±, Pos±.
|
|
59
59
|
|
|
60
|
-
Score combines position-weighted matchup quality, recent form (with trajectory adjustment), and xGI
|
|
60
|
+
Score combines position-weighted matchup quality, recent form (with trajectory adjustment), xGI per 90, and a consistency tiebreaker (CV-xGI percentile, phased in GW6-10). DGW players are scored across both fixtures. See [Captain Score](custom-analysis.md#captain-score) for the full formula, [Matchup Scoring](custom-analysis.md#matchup-scoring) for column definitions.
|
|
61
61
|
|
|
62
62
|
### Transfer Targets
|
|
63
63
|
|
|
@@ -75,7 +75,7 @@ Groups players into tiers:
|
|
|
75
75
|
- **Popular** (15-30% owned): Emerging picks
|
|
76
76
|
- **Differential** (<15% owned): Low-ownership value
|
|
77
77
|
|
|
78
|
-
Target score combines xG metrics, form, PPG,
|
|
78
|
+
Target score combines xG metrics, form, PPG, 3-GW matchup quality, and consistency (CV-xGI percentile bonus, phased in GW6-10), normalised to 0-100. Subject to [early-season shrinkage](custom-analysis.md#early-season-confidence-gw1-10). See [Target Score](custom-analysis.md#target-score) for the full formula.
|
|
79
79
|
|
|
80
80
|
### Transfer Evaluation
|
|
81
81
|
|
|
@@ -98,6 +98,7 @@ Output columns:
|
|
|
98
98
|
- **Fixtures** - next 3 opponents with FDR
|
|
99
99
|
- **Form** - FPL form (last 30 days PPG)
|
|
100
100
|
- **Status** - availability indicator
|
|
101
|
+
- **Avail** - historical availability rate (recency-weighted starts across previous seasons). Null when no historical data.
|
|
101
102
|
- **Quality** - price-independent player quality (0-100). Uses `VALUE_QUALITY_WEIGHTS`. Null when no Understat match.
|
|
102
103
|
- **Value** - quality per GBP million (`quality_score / price`). Higher = more output per pound. Null when no Understat match or price is 0. *(classic only)*
|
|
103
104
|
- **Price** - current price *(classic only)*
|
|
@@ -120,7 +121,7 @@ fpl differentials -m 200 # Require 200+ minutes played
|
|
|
120
121
|
fpl differentials --format json # JSON envelope (metadata: {gameweek})
|
|
121
122
|
```
|
|
122
123
|
|
|
123
|
-
Differential score combines xG metrics, form, ownership bonus,
|
|
124
|
+
Differential score combines xG metrics, form, ownership bonus, 3-GW matchup quality, and consistency (inverted CV-xGI bonus - volatile players score higher, phased in GW6-10), normalised to 0-100. Subject to [early-season shrinkage](custom-analysis.md#early-season-confidence-gw1-10). See [Differential Score](custom-analysis.md#differential-score) for the full formula.
|
|
124
125
|
|
|
125
126
|
### Waiver Recommendations
|
|
126
127
|
|
|
@@ -133,7 +134,7 @@ fpl waivers --format json
|
|
|
133
134
|
|
|
134
135
|
Identifies squad weaknesses by position, ranks available free agents by waiver score, suggests who to drop for each pickup. This covers the waiver wire (unclaimed players) only - trade recommendations between managers are not in scope.
|
|
135
136
|
|
|
136
|
-
Waiver score combines xGI, form, PPG,
|
|
137
|
+
Waiver score combines xGI, form, PPG, 3-GW matchup quality, and consistency (CV-xGI percentile bonus, phased in GW6-10), normalised to 0-100. Uses a stricter minutes factor than target/differential because draft waivers are a season commitment. Subject to [early-season shrinkage](custom-analysis.md#early-season-confidence-gw1-10). See [Waiver Score](custom-analysis.md#waiver-score) for the full formula.
|
|
137
138
|
|
|
138
139
|
## Fixture & Strategic Planning
|
|
139
140
|
|
|
@@ -357,6 +358,7 @@ When a player has an Understat match, `fpl player` computes and displays two add
|
|
|
357
358
|
| **pts_per_m** | `total_points / price` (per £m). Raw season points efficiency. |
|
|
358
359
|
| **form_per_m** | `form / price` (per £m). Recent form efficiency. |
|
|
359
360
|
| **rolling_pts_per_m** | Points per £m over the last N qualifying fixtures (configurable via `--window`). Captures recent form-adjusted value. |
|
|
361
|
+
| **adj. npxG/90** | Fixture-adjusted non-penalty xG per 90: npxG normalised by opponent Elo over a rolling window. Shown alongside raw when the adjustment changes the value. See [Fixture-Adjusted npxG](custom-analysis.md#fixture-adjusted-npxg). |
|
|
360
362
|
|
|
361
363
|
`quality_score` and `quality_per_m` are `null` when no Understat match exists. In JSON output (`--format json`), they appear under `info.quality_score` and `info.quality_per_m`. In the Rich panel, they appear as `Quality: 85 | Value: 11.3/£m`.
|
|
362
364
|
|
|
@@ -373,7 +375,7 @@ Both fields default to `0.0` for unavailable/injured players. In JSON output, th
|
|
|
373
375
|
|
|
374
376
|
#### Player Detail Flags
|
|
375
377
|
|
|
376
|
-
**`--detail` (`-d`)**: GW-by-GW match performance from the FPL API. Shows gameweek, opponent, minutes, goals, xG, assists, xA, and points for the last 10 matches.
|
|
378
|
+
**`--detail` (`-d`)**: GW-by-GW match performance from the FPL API. Shows gameweek, opponent, minutes, goals, xG, assists, xA, and points for the last 10 matches. For FWD/MID players with sufficient recent history, also shows xGI sustainability: the per-match GI-xGI divergence and the resulting form modifier (e.g. `xGI Sustainability: +0.18/match -> 0.94x form`).
|
|
377
379
|
|
|
378
380
|
**`--understat` (`-u`)**: Combined Understat analysis: shot analysis (total shots, shots on target, average xG per shot, body part split, situation breakdown) and situation profile. Includes a data-through date caveat since Understat data can lag behind the live season.
|
|
379
381
|
|
|
@@ -301,11 +301,42 @@ Current-season data is blended with a prior from the previous season's Understat
|
|
|
301
301
|
|
|
302
302
|
Stored in `config/team_ratings.yaml`.
|
|
303
303
|
|
|
304
|
+
## Fixture-Adjusted npxG
|
|
305
|
+
|
|
306
|
+
The scoring pipeline normally consumes npxG/90 as a flat season average from Understat. A player with a soft early schedule accumulates a high rate and also earns a strong matchup score — double-counting fixture difficulty.
|
|
307
|
+
|
|
308
|
+
Fixture-adjusted npxG normalises historical xG by the Elo strength of each opponent before computing the per-90 rate. This isolates fixture difficulty to the matchup component alone.
|
|
309
|
+
|
|
310
|
+
### How it works
|
|
311
|
+
|
|
312
|
+
For each completed match within a 7-match / 12-GW rolling window:
|
|
313
|
+
|
|
314
|
+
1. **npxG approximation**: `xg - (penalties_scored + penalties_missed) × 0.76` (removes penalty xG from the Core-Insights match total)
|
|
315
|
+
2. **Adjustment factor**: `median_elo / opponent_elo`, capped at [0.80, 1.25] — a match against a 1900-Elo side is scaled up; a match against an 800-Elo side is scaled down
|
|
316
|
+
3. **Adjusted per-90**: `(npxg × factor) / minutes × 90` per match, averaged over qualifying matches
|
|
317
|
+
4. Players with fewer than 4 qualifying matches fall back to raw Understat npxG/90
|
|
318
|
+
|
|
319
|
+
The median Elo is computed fresh from the season's match data each run. Elo ratings come from per-gameweek `matches.csv` files in the Core-Insights dataset (`By Tournament/Premier League/GW{n}/`).
|
|
320
|
+
|
|
321
|
+
### Display
|
|
322
|
+
|
|
323
|
+
When `custom_analysis: true` and match data is available, `fpl player` shows:
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
adj. npxG/90: 0.312 (raw: 0.385)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The existing `npxG` panel line continues to show the raw Understat season total. In JSON output, `info.adjusted_npxg_per_90` and `info.raw_npxg_per_90` are added when custom analysis is enabled. Captain and transfer evaluation agent outputs include both fields when an adjustment is active.
|
|
330
|
+
|
|
331
|
+
If Core-Insights data is unavailable, the pipeline falls back to raw npxG/90 with no error surfaced to users.
|
|
332
|
+
|
|
304
333
|
## Quality & Value Scores
|
|
305
334
|
|
|
306
335
|
Available via `fpl stats --value` and `fpl player` when Understat data exists.
|
|
307
336
|
|
|
308
|
-
**quality_score** (0-100): Normalised player output quality using `VALUE_QUALITY_WEIGHTS`. Weights form and PPG heavily to capture current FPL points production rate.
|
|
337
|
+
**quality_score** (0-100): Normalised player output quality using `VALUE_QUALITY_WEIGHTS`. Weights form and PPG heavily to capture current FPL points production rate. Position-specific scoring paths diverge for defensive players:
|
|
338
|
+
- **GK**: dedicated signals via `for_gk()` weights — saves per 90, defensive quality (inverted xGC/90, range 0-2), and clean sheet rate. Normalised against `GK_VALUE_CEILING` (28.2).
|
|
339
|
+
- **DEF**: `dc_per_90` (defensive contribution rate) replaces attacking xG stats via `without_xgi()`. Normalised against `VALUE_CEILING` (24.3).
|
|
309
340
|
|
|
310
341
|
**quality_per_m**: `quality_score / price` (per £m). Within-position budget efficiency - higher means more output per pound. Not meaningful for cross-position comparison. Null when price is 0.
|
|
311
342
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 2, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -14,11 +14,15 @@ from fpl_cli.api.fpl import FPLClient
|
|
|
14
14
|
from fpl_cli.api.fpl_draft import FPLDraftClient
|
|
15
15
|
from fpl_cli.models.types import EnrichedPlayer, WaiverTarget
|
|
16
16
|
from fpl_cli.services.player_scoring import (
|
|
17
|
+
ConsistencySignals,
|
|
18
|
+
apply_adjusted_npxg,
|
|
19
|
+
apply_consistency,
|
|
17
20
|
apply_shrinkage,
|
|
18
21
|
build_player_evaluation,
|
|
19
22
|
calculate_waiver_score,
|
|
20
23
|
compute_aggregate_matchup,
|
|
21
24
|
compute_form_trajectory,
|
|
25
|
+
compute_xgi_sustainability,
|
|
22
26
|
prepare_scoring_data,
|
|
23
27
|
)
|
|
24
28
|
|
|
@@ -40,6 +44,8 @@ class WaiverAgent(Agent):
|
|
|
40
44
|
super().__init__(config)
|
|
41
45
|
self.client = FPLDraftClient()
|
|
42
46
|
self.fpl_client = FPLClient()
|
|
47
|
+
self._adjusted_npxg_lookup: dict[int, float] | None = None
|
|
48
|
+
self._consistency_lookup: dict[int, ConsistencySignals] | None = None
|
|
43
49
|
self.league_id = config.get("draft_league_id") if config else None
|
|
44
50
|
self.entry_id = config.get("draft_entry_id") if config else None
|
|
45
51
|
|
|
@@ -170,10 +176,13 @@ class WaiverAgent(Agent):
|
|
|
170
176
|
data = await prepare_scoring_data(
|
|
171
177
|
self.fpl_client, include_players=True,
|
|
172
178
|
include_history=True, include_prior=True,
|
|
179
|
+
include_match_data=True,
|
|
173
180
|
)
|
|
174
181
|
next_gw_id = data.next_gw_id
|
|
175
182
|
self._player_histories = data.player_histories or {}
|
|
176
183
|
self._player_priors = data.player_priors
|
|
184
|
+
self._adjusted_npxg_lookup = data.adjusted_npxg_lookup
|
|
185
|
+
self._consistency_lookup = data.consistency_lookup
|
|
177
186
|
scoring_ctx = data.scoring_ctx
|
|
178
187
|
|
|
179
188
|
# Enrich available players with matchup and FDR
|
|
@@ -322,11 +331,21 @@ class WaiverAgent(Agent):
|
|
|
322
331
|
history = histories.get(player.get("id", 0), [])
|
|
323
332
|
if history:
|
|
324
333
|
enrichment["form_trajectory"] = compute_form_trajectory(history, next_gw_id)
|
|
334
|
+
sustainability, divergence = compute_xgi_sustainability(history, next_gw_id, player.get("position", "MID"))
|
|
335
|
+
enrichment["xgi_sustainability"] = sustainability
|
|
336
|
+
enrichment["xgi_divergence"] = divergence
|
|
325
337
|
priors = getattr(self, "_player_priors", None)
|
|
326
338
|
if priors:
|
|
327
339
|
prior = priors.get(player.get("id", 0))
|
|
328
340
|
if prior:
|
|
329
341
|
enrichment["prior_confidence"] = prior.confidence
|
|
342
|
+
if "npxG_per_90" not in enrichment:
|
|
343
|
+
npxg = player.get("npxG_per_90")
|
|
344
|
+
if npxg is not None:
|
|
345
|
+
enrichment["npxG_per_90"] = npxg
|
|
346
|
+
pid = int(player.get("id") or 0)
|
|
347
|
+
apply_adjusted_npxg(enrichment, pid, self._adjusted_npxg_lookup)
|
|
348
|
+
apply_consistency(enrichment, pid, self._consistency_lookup)
|
|
330
349
|
evaluation, _ = build_player_evaluation(
|
|
331
350
|
player,
|
|
332
351
|
enrichment=enrichment,
|
|
@@ -497,6 +516,14 @@ class WaiverAgent(Agent):
|
|
|
497
516
|
target, drop_candidate, team_counts
|
|
498
517
|
)
|
|
499
518
|
|
|
519
|
+
priors = getattr(self, "_player_priors", None)
|
|
520
|
+
target_prior = priors.get(target.get("id", 0)) if priors else None
|
|
521
|
+
reliability = target_prior.reliability if target_prior is not None else None
|
|
522
|
+
|
|
523
|
+
con_lookup = getattr(self, "_consistency_lookup", None)
|
|
524
|
+
con_signals = con_lookup.get(target.get("id", 0)) if con_lookup else None
|
|
525
|
+
con_pct = con_signals.cv_xgi_percentile if con_signals is not None else None
|
|
526
|
+
|
|
500
527
|
rec: dict[str, Any] = {
|
|
501
528
|
"priority": len(recommendations) + 1,
|
|
502
529
|
"target": {
|
|
@@ -505,6 +532,8 @@ class WaiverAgent(Agent):
|
|
|
505
532
|
"position": pos,
|
|
506
533
|
"form": target.get("form"),
|
|
507
534
|
"waiver_score": target.get("waiver_score"),
|
|
535
|
+
"reliability": reliability,
|
|
536
|
+
"cv_xgi_percentile": con_pct,
|
|
508
537
|
},
|
|
509
538
|
"drop": {
|
|
510
539
|
"name": drop_candidate.get("player_name") if drop_candidate else None,
|
|
@@ -8,12 +8,16 @@ from fpl_cli.agents.base import Agent, AgentResult, AgentStatus
|
|
|
8
8
|
from fpl_cli.api.fpl import FPLClient
|
|
9
9
|
from fpl_cli.models.player import FORMATION_LIMITS, PlayerStatus
|
|
10
10
|
from fpl_cli.services.player_scoring import (
|
|
11
|
+
ConsistencySignals,
|
|
11
12
|
ScoringContext,
|
|
13
|
+
apply_adjusted_npxg,
|
|
14
|
+
apply_consistency,
|
|
12
15
|
apply_shrinkage,
|
|
13
16
|
build_fixture_matchups,
|
|
14
17
|
build_player_evaluation,
|
|
15
18
|
calculate_bench_score,
|
|
16
19
|
compute_form_trajectory,
|
|
20
|
+
compute_xgi_sustainability,
|
|
17
21
|
prepare_scoring_data,
|
|
18
22
|
)
|
|
19
23
|
|
|
@@ -67,6 +71,7 @@ class BenchOrderAgent(Agent):
|
|
|
67
71
|
self.client,
|
|
68
72
|
include_players=True, include_understat=True,
|
|
69
73
|
include_history=True, include_prior=True,
|
|
74
|
+
include_match_data=True,
|
|
70
75
|
)
|
|
71
76
|
all_players = data.players or []
|
|
72
77
|
player_map = {p.id: p for p in all_players}
|
|
@@ -74,6 +79,8 @@ class BenchOrderAgent(Agent):
|
|
|
74
79
|
scoring_context = data.scoring_ctx
|
|
75
80
|
team_map = data.team_map
|
|
76
81
|
understat_by_id = data.understat_lookup
|
|
82
|
+
adjusted_npxg_lookup = data.adjusted_npxg_lookup
|
|
83
|
+
consistency_lookup = data.consistency_lookup
|
|
77
84
|
|
|
78
85
|
bench_ids = context["bench"]
|
|
79
86
|
bench_players = [player_map[pid] for pid in bench_ids if pid in player_map]
|
|
@@ -94,6 +101,8 @@ class BenchOrderAgent(Agent):
|
|
|
94
101
|
understat_by_id=understat_by_id,
|
|
95
102
|
player_histories=player_histories,
|
|
96
103
|
player_priors=data.player_priors,
|
|
104
|
+
adjusted_npxg_lookup=adjusted_npxg_lookup,
|
|
105
|
+
consistency_lookup=consistency_lookup,
|
|
97
106
|
)
|
|
98
107
|
scored_bench.append(score_data)
|
|
99
108
|
|
|
@@ -239,6 +248,8 @@ class BenchOrderAgent(Agent):
|
|
|
239
248
|
understat_by_id: dict[int, dict[str, float]] | None = None,
|
|
240
249
|
player_histories: dict[int, list[dict[str, Any]]] | None = None,
|
|
241
250
|
player_priors: dict[int, Any] | None = None,
|
|
251
|
+
adjusted_npxg_lookup: dict[int, float] | None = None,
|
|
252
|
+
consistency_lookup: dict[int, ConsistencySignals] | None = None,
|
|
242
253
|
) -> dict[str, Any]:
|
|
243
254
|
"""Score a bench player via the scoring engine."""
|
|
244
255
|
team = context.team_map.get(player.team_id)
|
|
@@ -251,11 +262,16 @@ class BenchOrderAgent(Agent):
|
|
|
251
262
|
us_data = understat_by_id.get(player.id)
|
|
252
263
|
if us_data:
|
|
253
264
|
enrichment.update(us_data)
|
|
265
|
+
apply_adjusted_npxg(enrichment, player.id, adjusted_npxg_lookup)
|
|
266
|
+
apply_consistency(enrichment, player.id, consistency_lookup)
|
|
254
267
|
|
|
255
268
|
if player_histories:
|
|
256
269
|
history = player_histories.get(player.id, [])
|
|
257
270
|
if history:
|
|
258
271
|
enrichment["form_trajectory"] = compute_form_trajectory(history, next_gw_id)
|
|
272
|
+
sustainability, divergence = compute_xgi_sustainability(history, next_gw_id, player.position_name)
|
|
273
|
+
enrichment["xgi_sustainability"] = sustainability
|
|
274
|
+
enrichment["xgi_divergence"] = divergence
|
|
259
275
|
|
|
260
276
|
if player_priors:
|
|
261
277
|
prior = player_priors.get(player.id)
|
|
@@ -12,12 +12,16 @@ from fpl_cli.api.fpl import FPLClient
|
|
|
12
12
|
from fpl_cli.models.player import Player
|
|
13
13
|
from fpl_cli.models.types import CaptainCandidate
|
|
14
14
|
from fpl_cli.services.player_scoring import (
|
|
15
|
+
ConsistencySignals,
|
|
15
16
|
ScoringContext,
|
|
17
|
+
apply_adjusted_npxg,
|
|
18
|
+
apply_consistency,
|
|
16
19
|
apply_shrinkage,
|
|
17
20
|
build_fixture_matchups,
|
|
18
21
|
build_player_evaluation,
|
|
19
22
|
calculate_captain_score,
|
|
20
23
|
compute_form_trajectory,
|
|
24
|
+
compute_xgi_sustainability,
|
|
21
25
|
prepare_scoring_data,
|
|
22
26
|
)
|
|
23
27
|
|
|
@@ -43,6 +47,8 @@ class CaptainAgent(Agent):
|
|
|
43
47
|
def __init__(self, config: dict[str, Any] | None = None):
|
|
44
48
|
super().__init__(config)
|
|
45
49
|
self.client = FPLClient()
|
|
50
|
+
self._adjusted_npxg_lookup: dict[int, float] | None = None
|
|
51
|
+
self._consistency_lookup: dict[int, ConsistencySignals] | None = None
|
|
46
52
|
|
|
47
53
|
# Differential thresholds
|
|
48
54
|
self.differential_threshold = config.get("differential_threshold", 10.0) if config else 10.0
|
|
@@ -63,11 +69,14 @@ class CaptainAgent(Agent):
|
|
|
63
69
|
self.client,
|
|
64
70
|
include_players=True, include_understat=True,
|
|
65
71
|
include_history=True, include_prior=True,
|
|
72
|
+
include_match_data=True,
|
|
66
73
|
)
|
|
67
74
|
all_players = data.players or []
|
|
68
75
|
player_map = {p.id: p for p in all_players}
|
|
69
76
|
self._player_histories = data.player_histories or {}
|
|
70
77
|
self._player_priors = data.player_priors
|
|
78
|
+
self._adjusted_npxg_lookup = data.adjusted_npxg_lookup
|
|
79
|
+
self._consistency_lookup = data.consistency_lookup
|
|
71
80
|
|
|
72
81
|
return (
|
|
73
82
|
player_map,
|
|
@@ -216,6 +225,8 @@ class CaptainAgent(Agent):
|
|
|
216
225
|
us_data = understat_by_id.get(player.id)
|
|
217
226
|
if us_data:
|
|
218
227
|
enrichment.update(us_data)
|
|
228
|
+
apply_adjusted_npxg(enrichment, player.id, self._adjusted_npxg_lookup)
|
|
229
|
+
apply_consistency(enrichment, player.id, self._consistency_lookup)
|
|
219
230
|
|
|
220
231
|
# xGI_per_90 fallback for players without Understat data
|
|
221
232
|
minutes_safe = max(player.minutes, 1)
|
|
@@ -226,6 +237,9 @@ class CaptainAgent(Agent):
|
|
|
226
237
|
history = histories.get(player.id, [])
|
|
227
238
|
if history:
|
|
228
239
|
enrichment["form_trajectory"] = compute_form_trajectory(history, next_gw_id)
|
|
240
|
+
sustainability, divergence = compute_xgi_sustainability(history, next_gw_id, player.position_name)
|
|
241
|
+
enrichment["xgi_sustainability"] = sustainability
|
|
242
|
+
enrichment["xgi_divergence"] = divergence
|
|
229
243
|
|
|
230
244
|
# Bayesian prior confidence
|
|
231
245
|
priors = getattr(self, "_player_priors", None)
|
|
@@ -7,13 +7,17 @@ from typing import TYPE_CHECKING, Any
|
|
|
7
7
|
from fpl_cli.agents.base import Agent, AgentResult, AgentStatus
|
|
8
8
|
from fpl_cli.api.fpl import FPLClient
|
|
9
9
|
from fpl_cli.services.player_scoring import (
|
|
10
|
+
ConsistencySignals,
|
|
10
11
|
ScoringContext,
|
|
12
|
+
apply_adjusted_npxg,
|
|
13
|
+
apply_consistency,
|
|
11
14
|
apply_shrinkage,
|
|
12
15
|
build_fixture_matchups,
|
|
13
16
|
build_player_evaluation,
|
|
14
17
|
calculate_lineup_score,
|
|
15
18
|
compute_aggregate_matchup,
|
|
16
19
|
compute_form_trajectory,
|
|
20
|
+
compute_xgi_sustainability,
|
|
17
21
|
prepare_scoring_data,
|
|
18
22
|
select_starting_xi,
|
|
19
23
|
)
|
|
@@ -67,12 +71,15 @@ class StartingXIAgent(Agent):
|
|
|
67
71
|
self.client,
|
|
68
72
|
include_players=True, include_understat=True,
|
|
69
73
|
include_history=True, include_prior=True,
|
|
74
|
+
include_match_data=True,
|
|
70
75
|
)
|
|
71
76
|
all_players = data.players or []
|
|
72
77
|
player_map = {p.id: p for p in all_players}
|
|
73
78
|
player_histories = data.player_histories or {}
|
|
74
79
|
scoring_context = data.scoring_ctx
|
|
75
80
|
understat_by_id = data.understat_lookup
|
|
81
|
+
adjusted_npxg_lookup = data.adjusted_npxg_lookup
|
|
82
|
+
consistency_lookup = data.consistency_lookup
|
|
76
83
|
|
|
77
84
|
squad_ids = context["squad"]
|
|
78
85
|
squad_players = [player_map[pid] for pid in squad_ids if pid in player_map]
|
|
@@ -95,6 +102,8 @@ class StartingXIAgent(Agent):
|
|
|
95
102
|
understat_by_id=understat_by_id,
|
|
96
103
|
player_histories=player_histories,
|
|
97
104
|
player_priors=data.player_priors,
|
|
105
|
+
adjusted_npxg_lookup=adjusted_npxg_lookup,
|
|
106
|
+
consistency_lookup=consistency_lookup,
|
|
98
107
|
)
|
|
99
108
|
scored.append(score_data)
|
|
100
109
|
|
|
@@ -150,6 +159,8 @@ class StartingXIAgent(Agent):
|
|
|
150
159
|
understat_by_id: dict[int, dict[str, float]] | None = None,
|
|
151
160
|
player_histories: dict[int, list[dict[str, Any]]] | None = None,
|
|
152
161
|
player_priors: dict[int, Any] | None = None,
|
|
162
|
+
adjusted_npxg_lookup: dict[int, float] | None = None,
|
|
163
|
+
consistency_lookup: dict[int, ConsistencySignals] | None = None,
|
|
153
164
|
) -> dict[str, Any]:
|
|
154
165
|
"""Score a squad player via the scoring engine."""
|
|
155
166
|
team = context.team_map.get(player.team_id)
|
|
@@ -167,11 +178,16 @@ class StartingXIAgent(Agent):
|
|
|
167
178
|
us_data = understat_by_id.get(player.id)
|
|
168
179
|
if us_data:
|
|
169
180
|
enrichment.update(us_data)
|
|
181
|
+
apply_adjusted_npxg(enrichment, player.id, adjusted_npxg_lookup)
|
|
182
|
+
apply_consistency(enrichment, player.id, consistency_lookup)
|
|
170
183
|
|
|
171
184
|
if player_histories:
|
|
172
185
|
history = player_histories.get(player.id, [])
|
|
173
186
|
if history:
|
|
174
187
|
enrichment["form_trajectory"] = compute_form_trajectory(history, next_gw_id)
|
|
188
|
+
sustainability, divergence = compute_xgi_sustainability(history, next_gw_id, player.position_name)
|
|
189
|
+
enrichment["xgi_sustainability"] = sustainability
|
|
190
|
+
enrichment["xgi_divergence"] = divergence
|
|
175
191
|
|
|
176
192
|
if player_priors:
|
|
177
193
|
prior = player_priors.get(player.id)
|