Skip to content

API Reference

core

Core data structures and utilities for rfpy.

GoalGrid

A grid representing all possible goal combinations between home and away teams.

Creates two DataFrames: - home: entry [away_goals, home_goals] = home_goals - away: entry [away_goals, home_goals] = away_goals

Both DataFrames use consistent naming: - index.name = "away" (represents away team goals) - columns.name = "home" (represents home team goals)

Attributes:

Name Type Description
n int

Maximum number of goals for either team (default: 10).

default() -> GoalGrid classmethod

Create a default GoalGrid with n=10.

__attrs_post_init__() -> None

Initialize the home and away goal matrices after object creation.

models

Statistical models for betting analysis.

Distribution

Bases: ABC

Base class for all probability distributions.

pmf(data: GoalGrid | pd.Series) -> pd.Series | pd.DataFrame abstractmethod

Calculate probability mass function.

plot(data: GoalGrid | pd.Series, *args: Any, **kwds: Any) -> Axes abstractmethod

Plot the distribution.

UnivariateDistribution

Bases: Distribution

Base class for univariate distributions.

cdf(data: pd.Series) -> pd.Series abstractmethod

Calculate cumulative distribution function.

BivariateDistribution

Bases: Distribution

Base class for bivariate distributions.

pmf(data: GoalGrid) -> pd.DataFrame abstractmethod

Calculate probability mass function.

plot(data: GoalGrid, *args: Any, **kwds: Any) -> Axes

Plot the distribution as a heatmap.

Poisson

Bases: UnivariateDistribution

Poisson distribution for modeling goal counts.

pmf(data: pd.Series) -> pd.Series

Calculate probability mass function for given values.

cdf(data: pd.Series) -> pd.Series

Calculate cumulative distribution function for given values.

plot(data: pd.Series, *args: Any, **kwds: Any) -> Axes

Plot the distribution.

Skellam

Bases: UnivariateDistribution

Skellam distribution for modeling goal differences.

pmf(data: GoalGrid | pd.Series) -> pd.Series

Calculate probability mass function for goal differences.

cdf(data: GoalGrid | pd.Series) -> pd.Series

Calculate cumulative distribution function for goal differences.

plot(data: GoalGrid | pd.Series, *args: Any, **kwds: Any) -> Axes

Plot the distribution.

BivariatePoisson

Bases: BivariateDistribution

Bivariate Poisson distribution for modeling home and away goals.

pmf(data: GoalGrid) -> pd.DataFrame

Calculate probability mass function for home and away goals.

plot(data: GoalGrid, *args: Any, **kwds: Any) -> Axes

Plot the distribution as a heatmap.

DixonColes

Bases: BivariateDistribution

Dixon-Coles adjusted bivariate Poisson distribution.

This class implements the Dixon-Coles (1997) adjustment to the independent bivariate Poisson distribution. The adjustment modifies the probabilities for low-scoring outcomes (0-0, 1-0, 0-1, 1-1) to account for correlation between home and away goals.

The adjustment factors are
  • P(0,0) *= 1 - λ_home * λ_away * ρ
  • P(1,0) *= 1 + λ_away * ρ
  • P(0,1) *= 1 + λ_home * ρ
  • P(1,1) *= 1 - ρ

Where ρ (rho) is the dependence parameter. Typically ρ < 0 is used to increase the probability of low-scoring draws relative to the independent Poisson model.

Attributes:

Name Type Description
dist BivariatePoisson

The base bivariate distribution to be adjusted.

rho float

Dependence parameter. Must satisfy constraints to ensure valid probabilities.

References

Dixon, M. J., & Coles, S. G. (1997). Modelling Association Football Scores and Inefficiencies in the Football Betting Market. Journal of the Royal Statistical Society: Series C (Applied Statistics), 46(2), 265-280.

pmf(data: GoalGrid) -> pd.DataFrame

Calculate probability mass function with Dixon-Coles adjustment.

This method adjusts the basic bivariate Poisson PMF by modifying the probabilities for the four low-scoring outcomes (0-0, 1-0, 0-1, 1-1).

Parameters:

Name Type Description Default
data GoalGrid

GoalGrid containing home and away goal combinations to calculate probabilities for.

required

Returns:

Type Description
DataFrame

DataFrame with adjusted probabilities for each home-away goal combination.

plot(data: GoalGrid, *args: Any, **kwds: Any) -> Axes

Plot the distribution as a heatmap.

calibrate(tg_quote: Quote, ah_quote: Quote, rho: float = -0.1, xtol: float = 0.001) -> DixonColes classmethod

Calibrate a Dixon-Coles model from Total Goals and Asian Handicap quotes.

Uses a two-step root-finding procedure:

  1. Total lambda: finds the total scoring rate that matches the implied over-probability from the Total Goals quote.
  2. Lambda split: finds the home/away split that matches the implied home-probability from the Asian Handicap quote.

Parameters:

Name Type Description Default
tg_quote Quote

A Total Goals market quote (with margin; will be stripped).

required
ah_quote Quote

An Asian Handicap market quote (with margin; will be stripped).

required
rho float

Dixon-Coles dependence parameter (typically negative).

-0.1
xtol float

Solver tolerance for root-finding.

0.001

Returns:

Type Description
DixonColes

A calibrated DixonColes instance.

Raises:

Type Description
ValueError

If quotes are for the wrong market type.

RingfinityModelZero

Bases: DixonColes

Dixon-Coles model with a fixed dependence parameter (ρ = -0.10).

This is a convenience preset that freezes the Dixon-Coles rho parameter, so only the base BivariatePoisson distribution needs to be supplied.

Attributes:

Name Type Description
dist BivariatePoisson

The base bivariate Poisson distribution.

market

Market classes for betting quotes and odds.

Market

Bases: Enum

Represents the different markets for betting quotes.

BetState

Bases: Enum

Represents the side of a betting quote.

QuotedLine

Represents a betting line value with quarter-point precision.

Betting lines are typically quoted in increments of 0.25 points and can be: - Unit lines: whole numbers (0, 1, 2, etc.) - push possible - Half lines: x.5 values (0.5, 1.5, etc.) - no push possible - Quarter lines: x.25, x.75 values - split between adjacent half-lines

Attributes:

Name Type Description
value float

The line value, must be a multiple of 0.25.

__attrs_post_init__() -> None

Validate that the line value is a multiple of 0.25.

is_unit_line() -> bool

Check if this is a unit line (whole number).

is_half_line() -> bool

Check if this is a half line (x.5).

is_quarter_line() -> bool

Check if this is a quarter line (x.25 or x.75).

neighbours() -> tuple[float, float]

Get the neighbours for quarter line splitting.

Quarter lines are split between two adjacent half-lines. For other lines, both neighbours are the same value.

Returns:

Type Description
tuple[float, float]

Tuple of (lower_bound, upper_bound) for line calculation

QuotedOdds

Bases: ABC

Abstract base class for betting odds.

This class serves as a base for different odds representations (e.g., DecimalOdds). Subclasses must implement the implied_probability method.

Attributes:

Name Type Description
value float

The odds value, typically a float representing the odds ratio.

implied_probability() -> float abstractmethod

Calculate the implied probability from the odds.

Returns:

Type Description
float

The implied probability as a float.

DecimalOdds

Bases: QuotedOdds

Decimal odds representation.

Attributes:

Name Type Description
value float

The odds value, typically a float representing the odds ratio.

implied_probability() -> float

Calculate the implied probability from the decimal odds.

Returns:

Type Description
float

The implied probability as a float.

from_probability(prob: float) -> DecimalOdds classmethod

Create DecimalOdds from an implied probability.

Parameters:

Name Type Description Default
prob float

The implied probability as a float.

required

QuoteLeg

Represents a price quote for a betting outcome.

Attributes:

Name Type Description
market Market

The type of betting market.

line QuotedLine

The betting line for the quote.

side BetState

Which side of the line (Goal Total, Home/Away).

odds QuotedOdds

The odds for the bet.

__attrs_post_init__() -> None

Validate side based on market type.

Quote

Represents a pair of complementary price quotes.

This class is used to group together two price quotes that are mutually exclusive (e.g., Home Win and Away Win).

Attributes:

Name Type Description
side_a QuoteLeg

The first price quote.

side_b QuoteLeg

The second price quote.

market: Market property

Get the market type of the quote pair.

line: QuotedLine property

Get the line value of the quote pair.

__attrs_post_init__() -> None

Validate that the two sides are complementary.

build(market: Market, line: QuotedLine, side_pair: tuple[BetState, BetState], odds_pair: tuple[QuotedOdds, QuotedOdds]) -> Quote classmethod

Build a Quote from market, line, sides, and odds.

Parameters:

Name Type Description Default
market Market

The type of betting market

required
line QuotedLine

The betting line for the quote

required
side_pair tuple[BetState, BetState]

A tuple of two BetState values representing the sides

required
odds_pair tuple[QuotedOdds, QuotedOdds]

A tuple of two QuotedOdds values representing the odds

required

Returns: A Quote instance with the specified parameters.

implied_margin() -> float

Calculate the bookmaker's margin for the quote pair.

Returns:

Type Description
float

The juice as a float.

margin_strip() -> Quote

Return a new Quote with de-margined odds.

Uses the basic proportional method to remove the bookmaker's margin.

Returns:

Type Description
Quote

A new Quote with de-margined odds.

margin_set(target_margin: float) -> Quote

Return a new Quote with specified margin.

Uses the basic proportional method to set the bookmaker's margin.

Parameters:

Name Type Description Default
target_margin float

The desired margin as a float (e.g., 0.05 for 5%)

required

Returns:

Type Description
Quote

A new Quote with the specified margin.

__repr__() -> str

Return string representation of Quote.

payoffs

Payoff calculation classes for different betting markets.

PayoffSurface

Bases: ABC

Abstract base class for betting payoff surfaces.

A payoff surface calculates betting outcomes across all possible goal combinations for a given line. Subclasses implement specific bet types (Asian handicap, over/under).

Attributes:

Name Type Description
line QuotedLine

The betting line (automatically converted to QuotedLine).

market: Market abstractmethod property

The market type for this payoff surface.

Returns:

Type Description
Market

The market type as a Market enum (e.g., Market.TOTAL_GOAL, Market.ASIAN_HCAP)

grid_payoff(grid: GoalGrid | None = None) -> pd.DataFrame abstractmethod

Calculate the payoff surface for this bet type.

Returns:

Type Description
DataFrame

DataFrame with payoff values for all goal combinations

grid_payoff_prob(model: BivariateDistribution, grid: GoalGrid | None = None) -> pd.DataFrame

Convenience method to calculate probability-weighted payoff.

Parameters:

Name Type Description Default
model BivariateDistribution

BivariateDistribution representing the probability distribution

required
grid GoalGrid | None

Optional GoalGrid for calculations (defaults to GoalGrid.default())

None

Returns:

Type Description
DataFrame

DataFrame with expected (model probability weighted) payoff values for

DataFrame

all goal combinations

grid_payoff_ev(model: BivariateDistribution, grid: GoalGrid | None = None) -> pd.DataFrame

Calculate the expected payoff given a probability distribution.

Parameters:

Name Type Description Default
model BivariateDistribution

BivariateDistribution representing the probability distribution

required
grid GoalGrid | None

Optional GoalGrid for calculations (defaults to GoalGrid.default())

None

Returns:

Type Description
DataFrame

DataFrame with expected (model probability weighted) payoff values for

DataFrame

all goal combinations

implied_prob(model: BivariateDistribution, grid: GoalGrid | None = None) -> dict[BetState, Any]

Calculate model-implied probabilities of winning on each side of the bet.

Parameters:

Name Type Description Default
model BivariateDistribution

BivariateDistribution representing the probability distribution

required
grid GoalGrid | None

Optional GoalGrid for calculations (defaults to GoalGrid.default())

None

Returns:

Type Description
dict[BetState, Any]

Tuple with probabilities of (under, over, push)

implied_odds(model: BivariateDistribution, grid: GoalGrid | None = None) -> Quote

Calculate model-implied probabilities of winning on each side of the bet.

Parameters:

Name Type Description Default
model BivariateDistribution

BivariateDistribution representing the probability distribution

required
grid GoalGrid | None

Optional GoalGrid for calculations (defaults to GoalGrid.default())

None

Returns:

Type Description
Quote

Tuple with probabilities of (under, over, push)

AsianHandicap

Bases: PayoffSurface

Asian handicap betting surface.

Asian handicap gives one team a goal advantage/disadvantage. The line represents the handicap applied to the home team: - Positive line: home team gets goals added - Negative line: home team gets goals subtracted

Payoff calculation: (home_goals - away_goals) + line - Win: final result > 0 - Push: final result = 0 (unit lines only) - Loss: final result < 0

market: Market property

The market type for this payoff surface.

Returns:

Type Description
Market

The market type as a Market enum (Market.ASIAN_HCAP)

grid_payoff(grid: GoalGrid | None = None) -> pd.DataFrame

Calculate Asian handicap payoffs.

Returns:

Type Description
DataFrame

DataFrame with payoff values based on goal difference minus handicap line

TotalGoals

Bases: PayoffSurface

Goal Total (totals) betting surface.

Goal Total betting is on the total number of goals scored by both teams. The line represents the threshold for total goals: - Over: bet wins if total goals > line - Under: bet wins if total goals < line - Push: total goals = line (unit lines only)

Payoff calculation: (home_goals + away_goals) - line - Win: final result > 0 (Over wins) - Push: final result = 0 (unit lines only) - Loss: final result < 0 (Under wins)

market: Market property

The market type for this payoff surface.

Returns:

Type Description
Market

The market type as a Market enum (Market.TOTAL_GOAL)

grid_payoff(grid: GoalGrid | None = None) -> pd.DataFrame

Calculate Goal Total payoffs.

Returns:

Type Description
DataFrame

DataFrame with payoff values based on total goals minus line

utilities

Core data structures and utilities for pyrf.

calc_total_mass(df: pd.DataFrame) -> float

Calculate total probability mass.

sum_diagonals(npy: np.ndarray) -> pd.Series

Sum the diagonals of a 2D numpy array representing a PMF, yielding goal differences.

pyrf_data

Client for accessing Ringfinity match data (FootyIQ watcher API + bet3 ratings).

PyRfDataClient dataclass

Client for the FootyIQ watcher data API.

Returns DataFrames with flattened data by default. Use *_raw methods for the original nested JSON response.

__post_init__() -> None

Initialize tokens and paths from environment if not provided.

fetch_scores_raw(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None, scores_agree: bool | None = None) -> list[dict[str, Any]]

Fetch game scores from the API as raw nested JSON.

Parameters:

Name Type Description Default
limit int

Maximum number of records to fetch.

1000
from_date Timestamp | None

Start date as pd.Timestamp (defaults to 2000-07-07).

None
to_date Timestamp | None

End date as pd.Timestamp (defaults to 2029-08-07).

None
scores_agree bool | None

If True, only return games where watcher and NowGoal agree. If False, only return games where they disagree. If None, return all games regardless.

None

Returns:

Type Description
list[dict[str, Any]]

Raw nested JSON response (country -> comps -> seasons -> rounds -> fixtures).

fetch_full_export_raw(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> dict[str, Any]

Fetch full export data as raw JSON.

Returns:

Type Description
dict[str, Any]

Raw JSON with 'fixtures' list and metadata.

fetch_scores(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None, scores_agree: bool | None = None) -> pd.DataFrame

Fetch game scores as a flattened DataFrame.

Parameters:

Name Type Description Default
limit int

Maximum number of records to fetch.

1000
from_date Timestamp | None

Start date as pd.Timestamp (defaults to 2000-07-07).

None
to_date Timestamp | None

End date as pd.Timestamp (defaults to 2029-08-07).

None
scores_agree bool | None

If True, only return games where watcher and NowGoal agree. If False, only return games where they disagree. If None, return all games regardless.

None

Returns:

Type Description
DataFrame

DataFrame with columns: fixture_id, start_time, country, comp_id, comp_name,

DataFrame

comp_short, season, round, team1_id, team1_name, team2_id, team2_name,

DataFrame

score_ft, score_ht, team1_goals, team2_goals, scores_agree, pitch,

DataFrame

weather, temp.

fetch_scores_agreed(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame

Fetch all trusted data where watcher and NowGoal scores agree.

This is the recommended method for fetching reliable historical data.

Returns:

Type Description
DataFrame

DataFrame with one row per fixture.

fetch_scores_disagreed(limit: int = 1000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame

Fetch games where watcher score differs from NowGoal score.

Useful for investigating data quality or discrepancies.

Returns:

Type Description
DataFrame

DataFrame with one row per fixture.

fetch_full_export(limit: int = 500000, from_date: pd.Timestamp | None = None, to_date: pd.Timestamp | None = None) -> pd.DataFrame

Fetch full export with detailed match statistics as DataFrame.

Includes per-team stats: goals, chances, half-chances, deliveries, ooohs, choos - broken down by half.

Returns:

Type Description
DataFrame

DataFrame with detailed match statistics.

fetch_ratings_club_raw() -> list[dict[str, Any]]

Fetch club team ratings as raw JSON.

Returns:

Type Description
list[dict[str, Any]]

List of rating objects, one per home/away club matchup, each

list[dict[str, Any]]

containing alpha/beta/lambda parameters, supremacy (sup), total

list[dict[str, Any]]

goals (tg), and team/league metadata.

fetch_ratings_club() -> pd.DataFrame

Fetch club team ratings as a DataFrame.

Returns:

Type Description
DataFrame

DataFrame with one row per club matchup, including alpha/beta/lambda

DataFrame

parameters, supremacy, total goals, and team/league metadata.

DataFrame

The updated_at column is parsed to datetime.

fetch_ratings_international_raw() -> list[dict[str, Any]]

Fetch international (national-team) ratings as raw JSON.

Returns:

Type Description
list[dict[str, Any]]

List of rating objects, one per nation, each containing supremacy

list[dict[str, Any]]

(sup), total goals (tg, goals), per-side goal expectations,

list[dict[str, Any]]

normalised alpha/beta, and team/NowGoal metadata.

fetch_ratings_international() -> pd.DataFrame

Fetch international (national-team) ratings as a DataFrame.

Returns:

Type Description
DataFrame

DataFrame with one row per nation, including supremacy, total goals,

DataFrame

per-side goal expectations, normalised alpha/beta, and team/NowGoal

DataFrame

metadata. The updated_at column is parsed to datetime.

load_wcplayer_export_raw() -> dict[str, Any]

Load the local wcplayerexport.json file as raw JSON.

Returns:

Type Description
dict[str, Any]

Dict with keys wcteamplayers (list of player records),

dict[str, Any]

exportedAt, and count.

load_wcplayer_export() -> pd.DataFrame

Load the local wcplayerexport.json as a flat DataFrame.

Nested fs / ng / tm sub-objects are flattened with dot-separated column names (e.g. tm.cpm, ng._id). Date columns (createdAt, updatedAt, v1StarRatingChangedAt, v2StarRatingChangedAt) are parsed to datetime.

Returns:

Type Description
DataFrame

DataFrame with one row per player.

fetch_wcplayer_export_raw(limit: int = 5000) -> dict[str, Any]

Fetch the wcteamplayers export from the API as raw JSON.

Returns:

Type Description
dict[str, Any]

Dict with keys wcteamplayers (list of player records),

dict[str, Any]

exportedAt, and count.

fetch_wcplayer_export(limit: int = 5000, *, cache: bool = True) -> pd.DataFrame

Fetch the wcteamplayers export from the API as a flat DataFrame.

When cache is True (default) and rf_local_path is set, the raw JSON response is written to <rf_local_path>/wcplayerexport.json, overwriting any existing file.

Returns:

Type Description
DataFrame

DataFrame with one row per player, matching load_wcplayer_export.

fetch_wc2026_squads(*, update: bool = False, cache_path: Path | str | None = None, url: str = WC2026_SQUADS_URL) -> pd.DataFrame

Return the 2026 World Cup squads as one row per (nation, player).

Reads data/wc2026_players_flashscore.parquet (or rf_local_path equivalent) by default. Pass update=True to re-scrape the FlashScore article at url and overwrite the cache.

See docs/specs/wc2026_v2.md for the column contract.