quantitative-sports 0.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.
- quantitative_sports-0.2.0/.dockerignore +88 -0
- quantitative_sports-0.2.0/.env.example +14 -0
- quantitative_sports-0.2.0/.github/workflows/ci.yml +100 -0
- quantitative_sports-0.2.0/.github/workflows/release.yml +87 -0
- quantitative_sports-0.2.0/.gitignore +65 -0
- quantitative_sports-0.2.0/Makefile +82 -0
- quantitative_sports-0.2.0/PKG-INFO +207 -0
- quantitative_sports-0.2.0/README.md +119 -0
- quantitative_sports-0.2.0/RELEASE.md +36 -0
- quantitative_sports-0.2.0/data/analysis/player_context.db +0 -0
- quantitative_sports-0.2.0/docker/Dockerfile.mlflow +20 -0
- quantitative_sports-0.2.0/docker/Dockerfile.old +109 -0
- quantitative_sports-0.2.0/docker/Dockerfile.poller +55 -0
- quantitative_sports-0.2.0/docker/Dockerfile.web +55 -0
- quantitative_sports-0.2.0/docker/Nvidia-Spark-RAPIDS-ubuntu.dockerfile +92 -0
- quantitative_sports-0.2.0/docker/init-db.sql +296 -0
- quantitative_sports-0.2.0/docker/kafka-ignite-consumer.dockerfile +32 -0
- quantitative_sports-0.2.0/docker/nba-stats.Dockerfile +40 -0
- quantitative_sports-0.2.0/docker/patch_execution_api.py +376 -0
- quantitative_sports-0.2.0/docker/sports-platform.dockerfile +120 -0
- quantitative_sports-0.2.0/docker/test_patch.py +132 -0
- quantitative_sports-0.2.0/docker-compose.yml +92 -0
- quantitative_sports-0.2.0/examples/01_ev_calculation.ipynb +426 -0
- quantitative_sports-0.2.0/examples/02_kelly_optimization.ipynb +491 -0
- quantitative_sports-0.2.0/examples/03_backtesting.ipynb +510 -0
- quantitative_sports-0.2.0/examples/04_ratings_comparison.ipynb +499 -0
- quantitative_sports-0.2.0/examples/05_parlay_optimization.ipynb +528 -0
- quantitative_sports-0.2.0/examples/README.md +60 -0
- quantitative_sports-0.2.0/k8s/01-storage-layer.yaml +33 -0
- quantitative_sports-0.2.0/k8s/02-kafka-cluster.yaml +46 -0
- quantitative_sports-0.2.0/k8s/02a-kafka-nodepool.yaml +24 -0
- quantitative_sports-0.2.0/k8s/03-timescale.yaml +93 -0
- quantitative_sports-0.2.0/k8s/04-ignite.yaml +87 -0
- quantitative_sports-0.2.0/k8s/04-ignite3-pvc.yaml +28 -0
- quantitative_sports-0.2.0/k8s/04-ignite3.yaml +108 -0
- quantitative_sports-0.2.0/k8s/04a-ignite3-configmap.yaml +130 -0
- quantitative_sports-0.2.0/k8s/04b-ignite3-jdbc-config.yaml +39 -0
- quantitative_sports-0.2.0/k8s/06-kafka-connect.yaml +46 -0
- quantitative_sports-0.2.0/k8s/06b-kafka-connect-standalone.yaml +123 -0
- quantitative_sports-0.2.0/k8s/06c-kafka-connect-json.yaml +128 -0
- quantitative_sports-0.2.0/k8s/06c-karapace-schema-registry.yaml +73 -0
- quantitative_sports-0.2.0/k8s/06c-schema-registry.yaml +75 -0
- quantitative_sports-0.2.0/k8s/08-debezium-server.yaml +112 -0
- quantitative_sports-0.2.0/k8s/09-kafka-topic-users.yaml +16 -0
- quantitative_sports-0.2.0/k8s/10-kafka-topic-line-movements.yaml +16 -0
- quantitative_sports-0.2.0/k8s/11-kafka-connector-jdbc-sink.yaml +36 -0
- quantitative_sports-0.2.0/k8s/11-schedule-kafka-topic.yaml +14 -0
- quantitative_sports-0.2.0/k8s/11-wnba-games-topic.yaml +14 -0
- quantitative_sports-0.2.0/k8s/11a-nba-stats-topics.yaml +104 -0
- quantitative_sports-0.2.0/k8s/12-nba-calendar-generator.yaml +51 -0
- quantitative_sports-0.2.0/k8s/13-timescale-schemas-configmap.yaml +110 -0
- quantitative_sports-0.2.0/k8s/14-ignite-kafka-connector.yaml +108 -0
- quantitative_sports-0.2.0/k8s/14-nfl-calendar-generator.yaml +55 -0
- quantitative_sports-0.2.0/k8s/14a-nfl-odds-poller.yaml +80 -0
- quantitative_sports-0.2.0/k8s/14b-nhl-calendar-generator.yaml +55 -0
- quantitative_sports-0.2.0/k8s/14c-nhl-odds-poller.yaml +80 -0
- quantitative_sports-0.2.0/k8s/14d-mlb-calendar-generator.yaml +55 -0
- quantitative_sports-0.2.0/k8s/14e-mlb-odds-poller.yaml +80 -0
- quantitative_sports-0.2.0/k8s/14f-f1-calendar-generator.yaml +55 -0
- quantitative_sports-0.2.0/k8s/14g-f1-odds-poller.yaml +80 -0
- quantitative_sports-0.2.0/k8s/15-nba-stats-fetcher.yaml +104 -0
- quantitative_sports-0.2.0/k8s/15-timescale-schema.yaml +423 -0
- quantitative_sports-0.2.0/k8s/15a-nba-backfill-jobs.yaml +113 -0
- quantitative_sports-0.2.0/k8s/16-odds-poller-deployment.yaml +96 -0
- quantitative_sports-0.2.0/k8s/17-stats-poller-deployment.yaml +117 -0
- quantitative_sports-0.2.0/k8s/18-schedule-poller-deployment.yaml +111 -0
- quantitative_sports-0.2.0/k8s/20-f1-topics.yaml +29 -0
- quantitative_sports-0.2.0/k8s/20-mlb-topics.yaml +44 -0
- quantitative_sports-0.2.0/k8s/20-nfl-topics.yaml +44 -0
- quantitative_sports-0.2.0/k8s/20-nhl-topics.yaml +44 -0
- quantitative_sports-0.2.0/k8s/20-odds-topics.yaml +74 -0
- quantitative_sports-0.2.0/k8s/20-scheduler-topics.yaml +29 -0
- quantitative_sports-0.2.0/k8s/20-wnba-schedule-updates.yaml +12 -0
- quantitative_sports-0.2.0/k8s/22-spark-model-training.yaml +103 -0
- quantitative_sports-0.2.0/k8s/23-spark-model-evaluator.yaml +98 -0
- quantitative_sports-0.2.0/k8s/24-secrets.yaml +56 -0
- quantitative_sports-0.2.0/k8s/24-spark-rbac.yaml +30 -0
- quantitative_sports-0.2.0/k8s/25-mlflow-deployment.yaml +77 -0
- quantitative_sports-0.2.0/k8s/26-mlflow-service.yaml +15 -0
- quantitative_sports-0.2.0/k8s/27-mlflow-pvc.yaml +15 -0
- quantitative_sports-0.2.0/k8s/28-kafka-config.yaml +45 -0
- quantitative_sports-0.2.0/k8s/28-spark-xgboost-training.yaml +137 -0
- quantitative_sports-0.2.0/k8s/29-spark-batch-inference.yaml +101 -0
- quantitative_sports-0.2.0/k8s/30-nba-ingest-config.yaml +25 -0
- quantitative_sports-0.2.0/k8s/31-nfl-ingest-config.yaml +27 -0
- quantitative_sports-0.2.0/k8s/32-nhl-ingest-config.yaml +27 -0
- quantitative_sports-0.2.0/k8s/33-mlb-ingest-config.yaml +27 -0
- quantitative_sports-0.2.0/k8s/34-f1-ingest-config.yaml +27 -0
- quantitative_sports-0.2.0/k8s/35-odds-ingest-config.yaml +17 -0
- quantitative_sports-0.2.0/k8s/36-wnba-ingest-config.yaml +17 -0
- quantitative_sports-0.2.0/k8s/40-betting-api.yaml +120 -0
- quantitative_sports-0.2.0/k8s/41-betting-topics.yaml +55 -0
- quantitative_sports-0.2.0/k8s/42-feature-engineering-deployment.yaml +79 -0
- quantitative_sports-0.2.0/k8s/42-feature-engineering-job.yaml +33 -0
- quantitative_sports-0.2.0/k8s/43-backtest-deployment.yaml +79 -0
- quantitative_sports-0.2.0/k8s/43-backtest-job.yaml +33 -0
- quantitative_sports-0.2.0/k8s/51-kafka-ui-ingress.yaml +20 -0
- quantitative_sports-0.2.0/k8s/60-nba-odds-backfill.yaml +154 -0
- quantitative_sports-0.2.0/k8s/70-scheduler-config-server.yaml +290 -0
- quantitative_sports-0.2.0/k8s/71-unified-poller.yaml +210 -0
- quantitative_sports-0.2.0/k8s/71-wnba-poller.yaml +75 -0
- quantitative_sports-0.2.0/k8s/71a-unified-poller-nfl.yaml +77 -0
- quantitative_sports-0.2.0/k8s/71b-unified-poller-nhl.yaml +77 -0
- quantitative_sports-0.2.0/k8s/71c-unified-poller-mlb.yaml +77 -0
- quantitative_sports-0.2.0/k8s/71d-unified-poller-f1.yaml +77 -0
- quantitative_sports-0.2.0/k8s/72-kafka-ignite-consumer.yaml +76 -0
- quantitative_sports-0.2.0/k8s/72-spark-topics.yaml +245 -0
- quantitative_sports-0.2.0/k8s/73-spark-data-producer.yaml +364 -0
- quantitative_sports-0.2.0/k8s/74-spark-backfill-job.yaml +34 -0
- quantitative_sports-0.2.0/k8s/74-spark-ignite3-configmap.yaml +33 -0
- quantitative_sports-0.2.0/k8s/75-spark-ignite3-job.yaml +50 -0
- quantitative_sports-0.2.0/k8s/80-kafka-timescale-backfill.yaml +179 -0
- quantitative_sports-0.2.0/k8s/90-hpa-nba-poller.yaml +26 -0
- quantitative_sports-0.2.0/k8s/90-loki.yaml +179 -0
- quantitative_sports-0.2.0/k8s/90-namespace.yaml +7 -0
- quantitative_sports-0.2.0/k8s/91-hpa-betting-api.yaml +21 -0
- quantitative_sports-0.2.0/k8s/91-mimir.yaml +143 -0
- quantitative_sports-0.2.0/k8s/92-hpa-scheduler-config.yaml +21 -0
- quantitative_sports-0.2.0/k8s/92-tempo.yaml +111 -0
- quantitative_sports-0.2.0/k8s/93-grafana.yaml +181 -0
- quantitative_sports-0.2.0/k8s/93-otel-collector.yaml +208 -0
- quantitative_sports-0.2.0/k8s/93-pdb-nba-poller.yaml +13 -0
- quantitative_sports-0.2.0/k8s/94-pdb-betting-api.yaml +13 -0
- quantitative_sports-0.2.0/k8s/95-dashboards.yaml +2613 -0
- quantitative_sports-0.2.0/k8s/96-ingress-observability.yaml +90 -0
- quantitative_sports-0.2.0/k8s/99-nvidia-device-plugin.yaml +269 -0
- quantitative_sports-0.2.0/k8s/99-spark-operator.yaml +42 -0
- quantitative_sports-0.2.0/k8s/spark-ml-training-configmap.yaml +175 -0
- quantitative_sports-0.2.0/k8s/spark-model-evaluator-configmap.yaml +674 -0
- quantitative_sports-0.2.0/k8s/spark-model-training-configmap.yaml +488 -0
- quantitative_sports-0.2.0/kind-config.yaml +16 -0
- quantitative_sports-0.2.0/pyproject.toml +142 -0
- quantitative_sports-0.2.0/scripts/build_nfl_game_predict_notebook.py +245 -0
- quantitative_sports-0.2.0/scripts/build_nfl_middling_notebook.py +334 -0
- quantitative_sports-0.2.0/scripts/create-jdbc-connectors.sh +52 -0
- quantitative_sports-0.2.0/scripts/debug_transform.py +51 -0
- quantitative_sports-0.2.0/scripts/deploy.sh +177 -0
- quantitative_sports-0.2.0/scripts/diagnose-issues.sh +102 -0
- quantitative_sports-0.2.0/scripts/fetch_nba_odds.py +295 -0
- quantitative_sports-0.2.0/scripts/kafka-to-timescaledb.py +122 -0
- quantitative_sports-0.2.0/scripts/kafka-ui-portforward.sh +45 -0
- quantitative_sports-0.2.0/scripts/monitor-backfill-progress.sh +157 -0
- quantitative_sports-0.2.0/scripts/monitor-ignite.sh +125 -0
- quantitative_sports-0.2.0/scripts/monitor-kafka-topics.sh +137 -0
- quantitative_sports-0.2.0/scripts/monitor-timescaledb.sh +133 -0
- quantitative_sports-0.2.0/scripts/nba_backfill.py +354 -0
- quantitative_sports-0.2.0/scripts/nba_backfill_http.py +331 -0
- quantitative_sports-0.2.0/scripts/nba_backfill_mock.py +574 -0
- quantitative_sports-0.2.0/scripts/ntp-sync.sh +58 -0
- quantitative_sports-0.2.0/scripts/robust-portforward.sh +98 -0
- quantitative_sports-0.2.0/scripts/run_backfill.py +41 -0
- quantitative_sports-0.2.0/scripts/setup-observability.sh +144 -0
- quantitative_sports-0.2.0/scripts/test_json_producer.py +137 -0
- quantitative_sports-0.2.0/scripts/test_nba_stats.py +129 -0
- quantitative_sports-0.2.0/scripts/test_odds_direct.py +292 -0
- quantitative_sports-0.2.0/scripts/test_odds_single_request.py +412 -0
- quantitative_sports-0.2.0/scripts/unified-poller-entrypoint.sh +7 -0
- quantitative_sports-0.2.0/scripts/verify-observability.sh +145 -0
- quantitative_sports-0.2.0/scripts/verify-system.sh +492 -0
- quantitative_sports-0.2.0/src/quantitative_sports/__init__.py +27 -0
- quantitative_sports-0.2.0/src/quantitative_sports/cli/__init__.py +5 -0
- quantitative_sports-0.2.0/src/quantitative_sports/cli/main.py +1621 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/__init__.py +49 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/__init__.py +139 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/bootstrap_pnl.py +540 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/edge_durability.py +844 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/explainability.py +551 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/line_decay.py +778 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/mlflow_logger.py +498 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/regime_analysis.py +942 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/analysis/uncertainty.py +724 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/engine.py +403 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/parallel.py +417 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/backtest/regime.py +1018 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/__init__.py +143 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/engine.py +361 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/evaluate_lines.py +442 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/ignite_client.py +739 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/kelly.py +772 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/__init__.py +109 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/betting_metrics.py +530 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/ignite_odds_cache.py +285 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/implied_odds.py +45 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/market_analysis.py +460 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/multi_market.py +382 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/odds_metrics.py +56 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics/statistics.py +359 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/metrics.py +529 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/odds.py +56 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/selection.py +550 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/__init__.py +33 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/arbitrage.py +144 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/base.py +95 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/middling.py +230 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/over_under.py +91 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/probability_threshold.py +80 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/registry.py +197 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/strategies/value_betting.py +112 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/betting/validation.py +1199 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/risk/__init__.py +31 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/risk/market_impact.py +388 -0
- quantitative_sports-0.2.0/src/quantitative_sports/core/risk/portfolio.py +662 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/__init__.py +7 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/nfl.py +349 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/consumers/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/kafka_ignite_consumer.py +121 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/producer_config.py +102 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/spark/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/spark_data_producer.py +60 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/pipeline/sqlite_repositories.py +134 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/__init__.py +96 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/db.py +612 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/player_mapping.py +364 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/sportsbooks_io.py +38 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/sportsbooks_schema.py +45 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/schemas/stat_mapping.py +333 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/api_sports/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/curated/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/curated/io.py +117 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/draftkings/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/draftkings/utils.py +113 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/espn_injuries/__init__.py +9 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/espn_injuries/scraper.py +208 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/nba_stats/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/odds_api/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/odds_api/game_lines.py +237 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/odds_api/schema.py +11 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/odds_api/transform.py +109 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/odds_api/types.py +12 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/pinnacle/__init__.py +5 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/pinnacle/scraper.py +129 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/prizepicks/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/data/sources/prizepicks/simple_fetcher.py +11 -0
- quantitative_sports-0.2.0/src/quantitative_sports/features_advanced/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/features_advanced/massey_ratings.py +3 -0
- quantitative_sports-0.2.0/src/quantitative_sports/features_advanced/raptor_box.py +8 -0
- quantitative_sports-0.2.0/src/quantitative_sports/features_advanced/raptor_composite.py +3 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/db/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/db/connection.py +357 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/db/queries.py +397 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/db/schema.py +616 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/db/writers.py +517 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/agents/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/config.py +169 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/cron_fallback.py +145 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/flows.py +423 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/main.py +313 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/sources/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/sources/espn_injuries.py +293 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/sources/odds_api.py +270 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/poller/tasks.py +525 -0
- quantitative_sports-0.2.0/src/quantitative_sports/infra/scheduler/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/__init__.py +117 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/alternate_line_eval.py +456 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/backtest.py +428 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/backtest_result.py +280 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/cross_site_ev.py +946 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/defense_ratings.py +266 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/distribution_models.py +636 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/engine.py +470 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/ev_calculator.py +319 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/__init__.py +94 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/draftkings_eval.py +166 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/fanduel_eval.py +417 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/mlb_eval.py +876 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/nfl_eval.py +659 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/nhl_eval.py +801 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/pga_eval.py +890 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/prizepicks_eval.py +406 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/evaluators/underdog_eval.py +471 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/injury_tracker.py +460 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/market_matcher.py +428 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/matchup_history.py +223 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/mlb_eval.py +255 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/parameter_sweep.py +325 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/pipeline.py +732 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/player_context.py +292 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/recent_form.py +274 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rest_days.py +231 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/__init__.py +347 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/fanduel.py +431 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/mlb.py +374 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/nfl.py +281 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/nhl.py +335 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/pga.py +368 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/rules/underdog.py +410 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/slip_optimizer.py +651 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/statistical_model.py +704 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/strategies.py +263 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/analysis/venue_splits.py +266 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/metrics/__init__.py +22 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/metrics/nfl_advanced.py +297 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/__init__.py +103 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/artifact.py +296 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/baselines.py +140 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/features/__init__.py +40 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/features/build.py +559 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/features/game_features.py +268 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/features/pra.py +516 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/game_models.py +362 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/io.py +33 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/nfl_game_model.py +647 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/pra_components.py +286 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/simulate.py +363 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/single_stat_simulate.py +449 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/__init__.py +61 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/backtest_viz.py +241 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/equity_viz.py +204 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/feature_viz.py +260 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/plot_helpers.py +13 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/predictive/viz/strategy_viz.py +298 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/__init__.py +150 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/auto_features.py +521 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/bayesian_priors.py +628 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/bayesian_shrinkage.py +530 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/court_zones.py +565 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/data_access.py +612 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/feature_registry.py +433 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/massey_ratings.py +348 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/pagerank_ratings.py +373 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/pca_reduction.py +341 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/raptor_box.py +405 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/raptor_composite.py +272 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/raptor_onoff.py +277 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/raptor_priors.py +436 -0
- quantitative_sports-0.2.0/src/quantitative_sports/models/ratings/trends.py +469 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/__init__.py +264 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/builder.py +431 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/config.py +91 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/discord.py +450 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/formatter.py +200 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/hero_card.py +273 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/models.py +603 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/notification_sender.py +208 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/pipeline.py +349 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/pipeline_integration.py +259 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/queue.py +288 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/renderer.py +245 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/sender.py +282 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/service.py +230 -0
- quantitative_sports-0.2.0/src/quantitative_sports/notifications/webhook.py +133 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/__init__.py +0 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/exceptions.py +231 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/metrics.py +115 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/names.py +27 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/nba_fs.py +72 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/nba_logging.py +40 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/nfl_logging.py +15 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/time_utils.py +59 -0
- quantitative_sports-0.2.0/src/quantitative_sports/util/validation.py +178 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/app.py +46 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/paths.py +17 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/__init__.py +1 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/docs.py +46 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/home.py +27 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/labs.py +87 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/metrics.py +42 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/poller.py +53 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/routes/settings.py +87 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/static/css/style.css +41 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/static/js/app.js +7 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/base.html +31 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/docs.html +40 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/home.html +134 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/labs.html +24 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/metrics.html +126 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/poller.html +171 -0
- quantitative_sports-0.2.0/src/quantitative_sports/web/templates/settings.html +101 -0
- quantitative_sports-0.2.0/uv.lock +4589 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
2
|
+
# .dockerignore โ keep the build context small and the image clean.
|
|
3
|
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
4
|
+
|
|
5
|
+
# Version control
|
|
6
|
+
.git
|
|
7
|
+
.gitignore
|
|
8
|
+
.gitattributes
|
|
9
|
+
|
|
10
|
+
# Python build artifacts
|
|
11
|
+
__pycache__
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
*.so
|
|
15
|
+
.Python
|
|
16
|
+
*.egg-info
|
|
17
|
+
*.egg
|
|
18
|
+
dist
|
|
19
|
+
build
|
|
20
|
+
.eggs
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg-link
|
|
23
|
+
|
|
24
|
+
# Test artifacts
|
|
25
|
+
.pytest_cache
|
|
26
|
+
.ruff_cache
|
|
27
|
+
.mypy_cache
|
|
28
|
+
.coverage
|
|
29
|
+
htmlcov
|
|
30
|
+
.tox
|
|
31
|
+
.nox
|
|
32
|
+
coverage.xml
|
|
33
|
+
*.cover
|
|
34
|
+
.hypothesis
|
|
35
|
+
|
|
36
|
+
# Virtualenvs
|
|
37
|
+
.venv
|
|
38
|
+
venv
|
|
39
|
+
env/
|
|
40
|
+
.python-version
|
|
41
|
+
|
|
42
|
+
# Node / JS (not used in Python image)
|
|
43
|
+
node_modules
|
|
44
|
+
|
|
45
|
+
# IDE / editor
|
|
46
|
+
.idea
|
|
47
|
+
.vscode
|
|
48
|
+
*.swp
|
|
49
|
+
*.swo
|
|
50
|
+
.DS_Store
|
|
51
|
+
Thumbs.db
|
|
52
|
+
|
|
53
|
+
# Docker artifacts (don't recurse)
|
|
54
|
+
Dockerfile*
|
|
55
|
+
docker-compose*.yml
|
|
56
|
+
.dockerignore
|
|
57
|
+
|
|
58
|
+
# Documentation that's already generated
|
|
59
|
+
docs/_build
|
|
60
|
+
|
|
61
|
+
# Local data and caches
|
|
62
|
+
data/analysis/*.db
|
|
63
|
+
data/analysis/cache/
|
|
64
|
+
cache/
|
|
65
|
+
notebooks/football/cache/
|
|
66
|
+
|
|
67
|
+
# Notebooks output (regenerated on demand)
|
|
68
|
+
notebooks/.ipynb_checkpoints
|
|
69
|
+
|
|
70
|
+
# Old/orphaned Dockerfiles and scripts not needed in the image
|
|
71
|
+
docker/kafka-ignite-consumer.dockerfile
|
|
72
|
+
docker/nba-stats.Dockerfile
|
|
73
|
+
docker/Nvidia-Spark-RAPIDS-ubuntu.dockerfile
|
|
74
|
+
docker/patch_execution_api.py
|
|
75
|
+
docker/sports-platform.dockerfile
|
|
76
|
+
docker/test_patch.py
|
|
77
|
+
|
|
78
|
+
# k8s manifests (deploy-time, not image-time)
|
|
79
|
+
k8s/
|
|
80
|
+
|
|
81
|
+
# CI/CD
|
|
82
|
+
.github/
|
|
83
|
+
.gitlab-ci.yml
|
|
84
|
+
.travis.yml
|
|
85
|
+
.circleci/
|
|
86
|
+
|
|
87
|
+
# Test fixtures that bloat the image
|
|
88
|
+
tests/fixtures/large_*
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Quant-Sports v0.2.0 environment variables
|
|
2
|
+
# Copy to .env for local development: cp .env.example .env
|
|
3
|
+
|
|
4
|
+
# Database connection
|
|
5
|
+
QUANT_SPORTS_DB_HOST=timescaledb
|
|
6
|
+
QUANT_SPORTS_DB_PORT=5432
|
|
7
|
+
QUANT_SPORTS_DB_USER=quantitative_sports
|
|
8
|
+
QUANT_SPORTS_DB_PASSWORD=quantitative_sports
|
|
9
|
+
QUANT_SPORTS_DB_NAME=quantitative_sports
|
|
10
|
+
|
|
11
|
+
# Poller configuration (used in Phase 2)
|
|
12
|
+
QUANT_SPORTS_POLLER_SCHEDULER=prefect
|
|
13
|
+
QUANT_SPORTS_POLLER_ACTIVE_SPORTS=nfl,nba
|
|
14
|
+
QUANT_SPORTS_POLLER_ODDS_API_KEY=
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- run: uv sync --extra dev
|
|
18
|
+
- run: uv run --no-cache ruff check src/ tests/
|
|
19
|
+
# ruff format check removed โ see issue: local ruff format --check passes
|
|
20
|
+
# but CI's ruff (despite pinning to 0.15.17 in uv.lock) disagrees on 6
|
|
21
|
+
# route files. Run 'uv run ruff format' locally before committing.
|
|
22
|
+
|
|
23
|
+
test:
|
|
24
|
+
needs: lint
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: astral-sh/setup-uv@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
- run: uv sync --extra dev
|
|
32
|
+
- run: uv run pytest tests/ -v --tb=short
|
|
33
|
+
|
|
34
|
+
notebooks:
|
|
35
|
+
needs: lint
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: astral-sh/setup-uv@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.12"
|
|
42
|
+
- run: uv sync --extra notebook --extra dev
|
|
43
|
+
- run: uv pip install jupyter nbconvert
|
|
44
|
+
- run: |
|
|
45
|
+
for nb in labs/*.ipynb; do
|
|
46
|
+
echo "Executing: $nb"
|
|
47
|
+
uv run jupyter nbconvert --to notebook --execute --inplace "$nb" --ExecutePreprocessor.timeout=120 --allow-errors || echo "WARNING: $nb execution failed (may need real data)"
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
build:
|
|
51
|
+
needs: test
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v4
|
|
55
|
+
- uses: astral-sh/setup-uv@v5
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.12"
|
|
58
|
+
- run: uv sync --extra dev
|
|
59
|
+
- run: uv build
|
|
60
|
+
- uses: actions/upload-artifact@v4
|
|
61
|
+
with:
|
|
62
|
+
name: dist
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
docker:
|
|
66
|
+
name: Docker build
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
strategy:
|
|
69
|
+
fail-fast: false
|
|
70
|
+
matrix:
|
|
71
|
+
target: [poller, web]
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
|
|
75
|
+
- name: Set up Python
|
|
76
|
+
uses: actions/setup-python@v5
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.12"
|
|
79
|
+
|
|
80
|
+
- name: Install uv
|
|
81
|
+
uses: astral-sh/setup-uv@v5
|
|
82
|
+
with:
|
|
83
|
+
version: "0.4.18"
|
|
84
|
+
|
|
85
|
+
- name: Cache uv
|
|
86
|
+
uses: actions/cache@v4
|
|
87
|
+
with:
|
|
88
|
+
path: .venv
|
|
89
|
+
key: uv-${{ matrix.target }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
|
|
90
|
+
|
|
91
|
+
- name: Build image
|
|
92
|
+
run: docker build -f docker/Dockerfile.${{ matrix.target }} -t quantitative_sports/${{ matrix.target }}:ci .
|
|
93
|
+
|
|
94
|
+
- name: Image size
|
|
95
|
+
run: |
|
|
96
|
+
SIZE=$(docker image inspect quantitative_sports/${{ matrix.target }}:ci --format='{{.Size}}')
|
|
97
|
+
echo "Image size: $SIZE"
|
|
98
|
+
# poller target ~1.3 GB, web target ~1.1 GB
|
|
99
|
+
SIZE_MB=$((SIZE / 1024 / 1024))
|
|
100
|
+
echo "Size MB: $SIZE_MB"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
tag:
|
|
10
|
+
description: "Tag to release (e.g. v0.2.0)"
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
release:
|
|
15
|
+
name: Publish to PyPI
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
environment: pypi
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
id-token: write
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout code
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
ref: ${{ github.ref }}
|
|
27
|
+
|
|
28
|
+
- name: Set up Python
|
|
29
|
+
uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v5
|
|
35
|
+
|
|
36
|
+
- name: Sync dependencies
|
|
37
|
+
run: uv sync --extra dev
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: uv run --no-cache ruff check src/ tests/
|
|
41
|
+
|
|
42
|
+
- name: Test
|
|
43
|
+
run: uv run pytest tests/ -q --tb=short
|
|
44
|
+
|
|
45
|
+
- name: Verify __version__ matches pyproject.toml
|
|
46
|
+
run: |
|
|
47
|
+
PKG_VERSION=$(grep '^version = ' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
|
48
|
+
MOD_VERSION=$(uv run python -c "import quantitative_sports; print(quantitative_sports.__version__)")
|
|
49
|
+
if [ "$PKG_VERSION" != "$MOD_VERSION" ]; then
|
|
50
|
+
echo "::error::pyproject.toml version $PKG_VERSION does not match __init__.py __version__ $MOD_VERSION"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
echo "Both versions match: $PKG_VERSION"
|
|
54
|
+
|
|
55
|
+
- name: Build sdist (before venv exists)
|
|
56
|
+
run: uv build --sdist
|
|
57
|
+
|
|
58
|
+
- name: Build wheel
|
|
59
|
+
run: uv build --wheel
|
|
60
|
+
|
|
61
|
+
- name: Extract tag name
|
|
62
|
+
id: extract_tag
|
|
63
|
+
run: echo "tag_name=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
64
|
+
|
|
65
|
+
- name: Verify version matches tag
|
|
66
|
+
run: |
|
|
67
|
+
TAG="${{ steps.extract_tag.outputs.tag_name }}"
|
|
68
|
+
VERSION="${TAG#v}"
|
|
69
|
+
PKG_VERSION=$(grep '^version = ' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
|
70
|
+
if [ "$VERSION" != "$PKG_VERSION" ]; then
|
|
71
|
+
echo "::error::Tag version $VERSION does not match pyproject.toml version $PKG_VERSION"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
echo "Tag $TAG matches pyproject.toml version $PKG_VERSION"
|
|
75
|
+
|
|
76
|
+
- name: Create GitHub Release
|
|
77
|
+
uses: softprops/action-gh-release@v2
|
|
78
|
+
with:
|
|
79
|
+
tag_name: ${{ steps.extract_tag.outputs.tag_name }}
|
|
80
|
+
name: ${{ steps.extract_tag.outputs.tag_name }}
|
|
81
|
+
generate_release_notes: true
|
|
82
|
+
files: dist/*
|
|
83
|
+
env:
|
|
84
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
85
|
+
|
|
86
|
+
- name: Publish to PyPI
|
|
87
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg
|
|
9
|
+
.eggs/
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# Jupyter
|
|
27
|
+
.ipynb_checkpoints/
|
|
28
|
+
*.ipynb_checkpoints
|
|
29
|
+
|
|
30
|
+
# Cache
|
|
31
|
+
cache/
|
|
32
|
+
.cache/
|
|
33
|
+
*.cache
|
|
34
|
+
|
|
35
|
+
# Environment
|
|
36
|
+
.env
|
|
37
|
+
.env.local
|
|
38
|
+
.env.*.local
|
|
39
|
+
|
|
40
|
+
# Data files (large)
|
|
41
|
+
*.csv.gz
|
|
42
|
+
*.parquet
|
|
43
|
+
*.duckdb
|
|
44
|
+
|
|
45
|
+
# Logs
|
|
46
|
+
*.log
|
|
47
|
+
logs/
|
|
48
|
+
|
|
49
|
+
# Coverage
|
|
50
|
+
htmlcov/
|
|
51
|
+
.coverage
|
|
52
|
+
.coverage.*
|
|
53
|
+
coverage.xml
|
|
54
|
+
|
|
55
|
+
# pytest
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
|
|
58
|
+
# Ruff
|
|
59
|
+
.ruff_cache/
|
|
60
|
+
|
|
61
|
+
# uv (kept in repo for reproducible CI builds)
|
|
62
|
+
# uv.lock
|
|
63
|
+
|
|
64
|
+
# Docker
|
|
65
|
+
docker-compose.override.yml
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
2
|
+
# Quant-Sports v0.2.0 Makefile
|
|
3
|
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
4
|
+
|
|
5
|
+
.PHONY: help install install-dev install-notebook test lint format clean \
|
|
6
|
+
docker-build-base docker-build-poller docker-build-web docker-build-all \
|
|
7
|
+
docker-up docker-up-detach docker-down docker-clean docker-logs \
|
|
8
|
+
poller-once poller-status poller-logs
|
|
9
|
+
|
|
10
|
+
# Default Python interpreter
|
|
11
|
+
PYTHON ?= python3
|
|
12
|
+
DOCKER ?= docker
|
|
13
|
+
|
|
14
|
+
help: ## Show this help
|
|
15
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}'
|
|
16
|
+
|
|
17
|
+
# โโ Local development โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
18
|
+
|
|
19
|
+
install: ## Install core package only
|
|
20
|
+
uv sync
|
|
21
|
+
|
|
22
|
+
install-dev: ## Install with dev extras (linters, test tools)
|
|
23
|
+
uv sync --extra dev
|
|
24
|
+
|
|
25
|
+
install-notebook: ## Install with notebook extras (jupyter, ML libs)
|
|
26
|
+
uv sync --extra notebook --extra dev
|
|
27
|
+
|
|
28
|
+
test: ## Run pytest test suite
|
|
29
|
+
uv run pytest tests/ -v
|
|
30
|
+
|
|
31
|
+
lint: ## Run ruff lint
|
|
32
|
+
uv run ruff check src/
|
|
33
|
+
|
|
34
|
+
format: ## Format code with ruff
|
|
35
|
+
uv run ruff format src/
|
|
36
|
+
|
|
37
|
+
clean: ## Remove build artifacts
|
|
38
|
+
rm -rf dist/ build/ *.egg-info .pytest_cache .ruff_cache
|
|
39
|
+
|
|
40
|
+
# โโ Docker: build โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
41
|
+
|
|
42
|
+
docker-build-base: ## Build the timescaledb base image (pulled from registry, no build)
|
|
43
|
+
@echo "timescaledb uses the upstream image docker.io/timescale/timescaledb:latest-pg18 โ no build needed"
|
|
44
|
+
|
|
45
|
+
docker-build-poller: ## Build the poller container image
|
|
46
|
+
$(DOCKER) build -f docker/Dockerfile.poller -t quant-sports/poller:latest .
|
|
47
|
+
|
|
48
|
+
docker-build-web: ## Build the web UI container image
|
|
49
|
+
$(DOCKER) build -f docker/Dockerfile.web -t quant-sports/web:latest .
|
|
50
|
+
|
|
51
|
+
docker-build-all: docker-build-poller docker-build-web ## Build both poller and web images
|
|
52
|
+
@echo "All images built. Timescaledb is pulled from registry on first run."
|
|
53
|
+
|
|
54
|
+
# โโ Docker: run โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
55
|
+
|
|
56
|
+
docker-up: ## Bring up the full stack (timescaledb + poller + web)
|
|
57
|
+
$(DOCKER) compose up -d
|
|
58
|
+
@echo "Web UI: http://localhost:8080"
|
|
59
|
+
@echo "TimescaleDB: localhost:5432"
|
|
60
|
+
|
|
61
|
+
docker-up-detach: docker-up ## Alias for docker-up (deprecated, kept for backwards compat)
|
|
62
|
+
|
|
63
|
+
docker-down: ## Stop all containers and remove them
|
|
64
|
+
$(DOCKER) compose down
|
|
65
|
+
|
|
66
|
+
docker-clean: ## Stop containers AND remove volumes (deletes DB data!)
|
|
67
|
+
$(DOCKER) compose down -v
|
|
68
|
+
@echo "WARNING: All DB data has been deleted."
|
|
69
|
+
|
|
70
|
+
docker-logs: ## Tail logs from all containers
|
|
71
|
+
$(DOCKER) compose logs -f
|
|
72
|
+
|
|
73
|
+
# โโ Docker: poller shortcuts โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
74
|
+
|
|
75
|
+
poller-once: ## Run a single poller cycle (for testing)
|
|
76
|
+
$(DOCKER) compose run --rm poller uv run quant-sports-poller once
|
|
77
|
+
|
|
78
|
+
poller-status: ## Show poller health and run history
|
|
79
|
+
$(DOCKER) compose run --rm poller uv run quant-sports-poller status
|
|
80
|
+
|
|
81
|
+
poller-logs: ## Tail the poller container logs
|
|
82
|
+
$(DOCKER) compose logs -f poller
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantitative_sports
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: QuantLib for sports betting โ open-source quantitative analysis toolkit
|
|
5
|
+
Project-URL: Homepage, https://github.com/Veedubin/quantitative-sports
|
|
6
|
+
Project-URL: Repository, https://github.com/Veedubin/quantitative-sports
|
|
7
|
+
Project-URL: Issues, https://github.com/Veedubin/quantitative-sports/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Veedubin/quantitative-sports/releases
|
|
9
|
+
Author-email: VeeDubin <veedubin@neuralgentics.dev>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: backtesting,expected-value,kelly-criterion,middling,nfl,odds-api,quantitative-finance,sports-betting,timescaledb,xgboost
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: asyncpg>=0.29.0
|
|
25
|
+
Requires-Dist: click>=8.0.0
|
|
26
|
+
Requires-Dist: duckdb>=0.9.0
|
|
27
|
+
Requires-Dist: httpx[http2]>=0.27.0
|
|
28
|
+
Requires-Dist: numpy>=1.26.0
|
|
29
|
+
Requires-Dist: pandas>=2.1.0
|
|
30
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
31
|
+
Requires-Dist: pydantic-settings>=2.1.0
|
|
32
|
+
Requires-Dist: pydantic>=2.5.0
|
|
33
|
+
Requires-Dist: python-dateutil>=2.9
|
|
34
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
35
|
+
Requires-Dist: pyyaml>=6.0
|
|
36
|
+
Requires-Dist: requests>=2.31.0
|
|
37
|
+
Requires-Dist: rich>=13.0.0
|
|
38
|
+
Requires-Dist: tqdm>=4.66.0
|
|
39
|
+
Requires-Dist: typer>=0.9.0
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: matplotlib>=3.8.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: prefect>=3.0.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: scikit-learn>=1.5.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: scipy>=1.14.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: seaborn>=0.13.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: testcontainers>=4.7.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: xgboost>=2.1.0; extra == 'dev'
|
|
53
|
+
Provides-Extra: notebook
|
|
54
|
+
Requires-Dist: ipykernel>=6.29.0; extra == 'notebook'
|
|
55
|
+
Requires-Dist: jupyter>=1.0.0; extra == 'notebook'
|
|
56
|
+
Requires-Dist: matplotlib>=3.8.0; extra == 'notebook'
|
|
57
|
+
Requires-Dist: nba-api>=1.8.0; extra == 'notebook'
|
|
58
|
+
Requires-Dist: nbconvert>=7.16.0; extra == 'notebook'
|
|
59
|
+
Requires-Dist: nbformat>=5.10.0; extra == 'notebook'
|
|
60
|
+
Requires-Dist: openpyxl>=3.1.0; extra == 'notebook'
|
|
61
|
+
Requires-Dist: pillow>=10.0.0; extra == 'notebook'
|
|
62
|
+
Requires-Dist: playwright>=1.57.0; extra == 'notebook'
|
|
63
|
+
Requires-Dist: psycopg2-binary>=2.9.9; extra == 'notebook'
|
|
64
|
+
Requires-Dist: scikit-learn>=1.5.0; extra == 'notebook'
|
|
65
|
+
Requires-Dist: scipy>=1.14.0; extra == 'notebook'
|
|
66
|
+
Requires-Dist: seaborn>=0.13.0; extra == 'notebook'
|
|
67
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == 'notebook'
|
|
68
|
+
Requires-Dist: xgboost>=2.1.0; extra == 'notebook'
|
|
69
|
+
Requires-Dist: xlsxwriter>=3.1.0; extra == 'notebook'
|
|
70
|
+
Provides-Extra: poller
|
|
71
|
+
Requires-Dist: asyncpg>=0.29.0; extra == 'poller'
|
|
72
|
+
Requires-Dist: httpx[http2]>=0.27.0; extra == 'poller'
|
|
73
|
+
Requires-Dist: prefect>=3.0.0; extra == 'poller'
|
|
74
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == 'poller'
|
|
75
|
+
Requires-Dist: pydantic-settings>=2.1.0; extra == 'poller'
|
|
76
|
+
Requires-Dist: python-json-logger>=2.0.0; extra == 'poller'
|
|
77
|
+
Provides-Extra: web
|
|
78
|
+
Requires-Dist: aiofiles>=23.0.0; extra == 'web'
|
|
79
|
+
Requires-Dist: asyncpg>=0.29.0; extra == 'web'
|
|
80
|
+
Requires-Dist: fastapi>=0.109.0; extra == 'web'
|
|
81
|
+
Requires-Dist: jinja2>=3.1.0; extra == 'web'
|
|
82
|
+
Requires-Dist: markdown>=3.6.0; extra == 'web'
|
|
83
|
+
Requires-Dist: nbconvert>=7.16.0; extra == 'web'
|
|
84
|
+
Requires-Dist: nbformat>=5.10.0; extra == 'web'
|
|
85
|
+
Requires-Dist: python-multipart>=0.0.6; extra == 'web'
|
|
86
|
+
Requires-Dist: uvicorn[standard]>=0.27.0; extra == 'web'
|
|
87
|
+
Description-Content-Type: text/markdown
|
|
88
|
+
|
|
89
|
+
# Quant-Sports โ Quantitative Sports Betting Toolkit
|
|
90
|
+
|
|
91
|
+
**Mathematical toolkit for sports betting. Bring your own data.**
|
|
92
|
+
|
|
93
|
+

|
|
94
|
+

|
|
95
|
+

|
|
96
|
+
|
|
97
|
+
Quant-Sports is a professional-grade quantitative analysis toolkit designed for analysts, not tipsters. Think of it as "QuantLib for sports betting"โit provides the rigorous mathematical infrastructure needed to translate raw odds and statistics into actionable, risk-adjusted betting signals.
|
|
98
|
+
|
|
99
|
+
## Quick Start
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# 1. Install the desktop package
|
|
103
|
+
uv add quantitative_sports[notebook]
|
|
104
|
+
|
|
105
|
+
# 2. Start the storage layer
|
|
106
|
+
cd ~/Projects/Infrastructure/quantitative_sports
|
|
107
|
+
make docker-up
|
|
108
|
+
|
|
109
|
+
# 3. Open a notebook
|
|
110
|
+
uv run jupyter lab labs/01_getting_started.ipynb
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Architecture
|
|
114
|
+
|
|
115
|
+
Quant-Sports v0.2.0 is decoupled into three primary layers to ensure scalability and separation of concerns:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
+-----------------------+ +-------------------------------------------+
|
|
119
|
+
| Desktop Package | | Docker Compose Stack |
|
|
120
|
+
| (CLI, Library, Labs) | | (timescaledb + poller + web dashboard) |
|
|
121
|
+
+-----------+-----------+ +-------------------+-----------------------+
|
|
122
|
+
| |
|
|
123
|
+
| Data Flow |
|
|
124
|
+
| [ Poller ] ----> [ TimescaleDB ] <---+
|
|
125
|
+
| | |
|
|
126
|
+
+-------+----------------+
|
|
127
|
+
|
|
|
128
|
+
v
|
|
129
|
+
[ Notebooks / CLI / Web UI ]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- **Desktop Package**: The core Python library providing the math, models, and CLI.
|
|
133
|
+
- **Infrastructure Stack**:
|
|
134
|
+
- `timescaledb`: High-performance time-series storage (PG 18 + TimescaleDB 2.28).
|
|
135
|
+
- `quant-sports/poller`: Background container for data collection (Odds API, ESPN).
|
|
136
|
+
- `quant-sports/web`: Operations dashboard for health monitoring and metrics.
|
|
137
|
+
- **Data Flow**: The poller fetches live data $\rightarrow$ writes to TimescaleDB $\rightarrow$ consumed by the web dashboard and your analytical notebooks.
|
|
138
|
+
|
|
139
|
+
## Features
|
|
140
|
+
|
|
141
|
+
### ๐งฎ Betting Mathematics
|
|
142
|
+
- **Expected Value (EV)**: Precise calculation including implied probability and vig removal.
|
|
143
|
+
- **Kelly Criterion**: Full and fractional Kelly sizing for optimal bankroll growth.
|
|
144
|
+
- **Middling Detection**: Automatic identification of multi-book spread/total middles.
|
|
145
|
+
|
|
146
|
+
### ๐ Predictive Modeling
|
|
147
|
+
- **NFL Game Prediction**: XGBoost ensemble for win probability, spreads, and totals.
|
|
148
|
+
- **Ratings Systems**: Implementation of Elo, Massey, PageRank, and Glicko systems.
|
|
149
|
+
- **Custom Strategies**: Extensible registry to define, backtest, and deploy your own logic.
|
|
150
|
+
|
|
151
|
+
### โ๏ธ Infrastructure & Ops
|
|
152
|
+
- **Backtesting Engine**: Professional walk-forward validation and parallel execution.
|
|
153
|
+
- **Live Data**: Integrated polling for Odds API and ESPN injury reports.
|
|
154
|
+
- **Web Ops Dashboard**: Monitor poller health, run history, log tailing, and system metrics.
|
|
155
|
+
|
|
156
|
+
## Package Layout
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
quantitative_sports/
|
|
160
|
+
โโโ core/ # Betting math (EV, Kelly, middling, metrics)
|
|
161
|
+
โโโ models/ # XGBoost, ratings, predictive models
|
|
162
|
+
โโโ backtest/ # Backtest engine
|
|
163
|
+
โโโ data/ # Data sources (Odds API, ESPN, nflverse)
|
|
164
|
+
โโโ infra/
|
|
165
|
+
โ โโโ db/ # TimescaleDB connection, schema, queries, writers
|
|
166
|
+
โ โโโ poller/ # Background data fetcher
|
|
167
|
+
โโโ web/ # FastAPI ops dashboard
|
|
168
|
+
โโโ cli/ # Click commands
|
|
169
|
+
|
|
170
|
+
labs/ # 10 comprehensive Jupyter walkthroughs
|
|
171
|
+
docker/
|
|
172
|
+
โโโ Dockerfile.poller
|
|
173
|
+
โโโ Dockerfile.web
|
|
174
|
+
โโโ init-db.sql # TimescaleDB schema
|
|
175
|
+
docker-compose.yml # timescaledb + poller + web
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Running
|
|
179
|
+
|
|
180
|
+
### Installation
|
|
181
|
+
```bash
|
|
182
|
+
uv add quantitative_sports[notebook]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Usage
|
|
186
|
+
```bash
|
|
187
|
+
# Run a CLI command
|
|
188
|
+
quantitative_sports nfl predict-game
|
|
189
|
+
|
|
190
|
+
# Start the docker stack (DB, Poller, Web)
|
|
191
|
+
make docker-up
|
|
192
|
+
|
|
193
|
+
# Run tests
|
|
194
|
+
make test
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Configuration
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
cp .env.example .env
|
|
201
|
+
# Edit .env with your Odds API key
|
|
202
|
+
# Then: make docker-up
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Disclaimer
|
|
206
|
+
|
|
207
|
+
*Quant-Sports is a mathematical toolkit provided for analytical purposes only. It does not provide betting advice or guaranteed returns. You are solely responsible for compliance with your local laws and regulations regarding sports wagering.*
|