sports-betting Python Toolbox

repository·main·Indexed 20 days ago

https://github.com/georgedouzas/sports-betting

A toolkit for building, testing, and executing sports betting models. It provides dataloaders for gathering statistics and odds, and bettors that wrap scikit-learn estimators to backtest strategies and identify value bets. The library is accessible via a Python API, a CLI (sportsbet command), and an MCP server for AI agents. Key features include the ClassifierBettor, OddsComparisonBettor, and BettorGridSearchCV for hyperparameter tuning.

Tokens
101.4K
Snippets
207
Records
464
Agent score
72%

What's inside sports-betting

  1. Choose an interface for the sports-betting library

    main

    The sports-betting library provides three distinct ways to interact with its data, models, and betting logic. Choose the interface based on your workflow:

    1. Python API: Use this when writing custom Python scripts. It provides direct access to the library's dataloaders and bettors as importable modules.
    2. CLI: Use the sportsbet command for shell-based operations. This allows you to extract data, run backtests, fit models, and place bets without writing any Python code.
    3. Agents (MCP Server): Use this to expose the library's capabilities to an AI assistant. It operates as a Model Context Protocol (MCP) server, allowing an agent to drive the library on your behalf.

    Regardless of the interface chosen, all sports, data sources, and models remain consistent across all three methods.

  2. Use the `sportsbet.datasets` public API

    main

    The sportsbet.datasets module provides the user-facing data interface for loading and extracting sports betting datasets. It follows a scikit-learn-style interface for data loading and feature extraction.

    Public Exports

    The following symbols are exported by sportsbet.datasets.__all__:

    • BaseDataLoader (Base class)
    • SoccerDataLoader (Concrete implementation)
    • DummySoccerDataLoader (Offline implementation using sample data)
    • load_dataloader (Utility to reload a saved loader)
    • BaseStatsSchema, BaseOddsSchema (Data schemas)
    • required_col, optional_col (Column validation utilities)
  3. Evasion and Automation Boundaries (FR-023)

    main

    The system operates under a strict 'no-evasion' policy. It is designed to automate accounts that the user already holds, rather than attempting to defeat venue-level security controls.

    Prohibited Techniques:

    • Stealth browsing
    • Fingerprint spoofing
    • Captcha solving
    • Proxy rotation
    • Geographic circumvention

    Operational Behavior:

    • Reporting vs. Circumvention: If a venue blocks automation (e.g., bet365), the system will report that the site is unreachable rather than attempting to bypass the block.
    • Proxy Rotation Warning: Proxy rotation is explicitly avoided because it can trigger 'account sharing' or 'compromise' flags at the venue, even if the session is already authenticated via cookies.
  4. Reconcile statistics and odds from different providers

    main

    When using different providers for statistics and odds, the library performs a reconciliation (join) process to match matches across sources (e.g., mapping "Man United" to "Manchester United").

    Reconciliation Features:

    • Reporting: The preparation step reports the proportion of matches successfully reconciled versus those that remained unmatched.
    • Tolerance Threshold: You can configure a tolerance level for unmatched matches. If the unmatched rate exceeds this threshold, extraction will fail with an error naming example unmatched matches to prevent producing a dataset with silent holes.
    • Data Integrity: Even when within tolerance, unmatched matches are reported rather than being silently dropped.
  5. Understand the Data Storage Format

    main

    The project uses a hybrid storage strategy to balance performance, schema stability, and data retention:

    • Derived Snapshots: Stored as Parquet files (using pyarrow with zstd compression). These are partitioned by source/sport/league/season to allow efficient slicing. Parquet is used specifically to preserve strict data types (e.g., preventing empty columns from being cast to objects).
    • Raw Payloads: Stored as gzipped files, one per RawItem. Raw data is retained indefinitely to allow for rebuilding derived tables without re-fetching (and re-paying for) the data.
    • Manifest: A JSONL file acting as the index. JSONL is used because it is append-only and human-readable, making it resilient to partial writes.
  6. Implement a custom data source using `BaseSource`

    main

    To create a new data source, you must subclass BaseSource (located in src/sportsbet/datasets/_sources/_base.py) and implement several abstract methods. A source is responsible for defining what parameters it supports and how to transform raw payloads into long-format snapshots.

    Core Requirements

    1. index_items(): Return a list of RawItem objects needed to discover available data. This must be a 'free' operation (no network/file access).
    2. catalogue(payloads): A pure function that parses index payloads into a list of Param objects (combinations of league/division/year).
    3. required_items(params): A pure, deterministic function that returns the RawItem list needed to satisfy a specific list of Param objects.
    4. to_snapshots(payloads): A pure function that transforms RawPayload objects into a pd.DataFrame in a long format (e.g., the stats table or odds table).

    Implementation Rules

    • Purity: Methods index_items, catalogue, required_items, and to_snapshots must be pure. They must not open sockets, access the filesystem, or perform network requests. This ensures that prepare(dry_run=True) remains free and side-effect-free.
    • Instance Methods: available_params is an instance method, not a class method, because availability depends on the specific instance configuration (e.g., API keys or subscription tiers).
    • Security: Never include credentials in a RawItem. Credentials should be stored on the source instance and injected into request headers at fetch time.
    • Marker Subclasses: Use BaseStatsSource or BaseOddsSource as marker subclasses to allow the dataloader to type-check that the correct source type is provided for stats= or odds= arguments.
    class BaseSource(ABC):
        name: ClassVar[str]
        kind: ClassVar[str]          # 'stats' or 'odds'
    
        def available_params(self, store: BaseStore | None = None) -> list[Param]: ...
    
        @abstractmethod
        def index_items(self) -> list[RawItem]: ...
    
        @abstractmethod
        def catalogue(self, payloads: list[RawPayload]) -> list[Param]: ...
    
        @abstractmethod
        def required_items(self, params: list[Param]) -> list[RawItem]: ...
    
        @abstractmethod
        def to_snapshots(self, payloads: list[RawPayload]) -> pd.DataFrame: ...
    
        def estimate(self, items: list[RawItem]) -> int: ...
  7. How documentation examples are verified

    main

    To ensure reliability, all documentation examples are verified through two automated processes during the build:

    1. Gallery Examples: Every example located in docs/examples is executed during the documentation build.
    2. Docstring Examples: Every example within public API docstrings is verified using doctest.

    Constraints for examples:

    • Secrets: Must use placeholders instead of real secrets.
    • Network Dependencies: Must use sample data or fakes to ensure they can run without internet access.
    • Offline Execution: Any example that cannot run entirely offline is removed from the documentation.
  8. Understand the NBA Data Model entities

    main

    The NBA data model follows the same structure as the EuroLeague model, consisting of three primary hierarchical entities: Season, Game, and Snapshot.

    • Season: Represents a competition year, named by the year it ends in (e.g., 2026).
    • Game: A contest between two clubs. A game is either a fixture (not yet played) or played (has a score). Pre-season and exhibition games are excluded from the dataset.
    • Snapshot: The actual data rows held by the library. A single game can result in one or two snapshots depending on its status.
  9. Configuration requirements and breaking changes

    main

    The library uses a Configuration object to define what the user wants to extract.

    • New Contract: A configuration is now a fully configured dataloader. This is the only way to carry a source and its associated credentials.
    • Breaking Change: The previous configuration contract has been removed. If you use an old configuration format, it will fail with an error message indicating what needs to be changed.
    • Discovery: If a configuration has not yet selected specific seasons or data, you can still query the surface to ask what data is available.
  10. Lifecycle and State Transitions of a Single-Event Run

    main

    A single-event execution follows a linear progression for both the event status and the execution unit's internal state.

    Event Status Progression

    The event status advances in a single direction: preplay $\rightarrow$ kickoff $\rightarrow$ inplay $\rightarrow$ final whistle $\rightarrow$ postplay

    Execution Unit Lifecycle

    The unit moves through these stages:

    1. setup: Explore URLs, match the event, pin controls, and ensure login. The run stops here if no match is found or login fails.
    2. monitoring: Poll the source, log status and price, and wait. This repeats until the betting moment is reached or the bound (timeout) is hit.
    3. moment: The bettor is applied to the event's data as of this specific moment.
    4. decided: The bettor either places one bet or declines. Both outcomes are logged.
    5. done: The run concludes.