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). |
models
¶
Statistical models for betting analysis.
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.
Poisson
¶
Bases: UnivariateDistribution
Poisson distribution for modeling goal counts.
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.
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:
- Total lambda: finds the total scoring rate that matches the implied over-probability from the Total Goals quote.
- 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
¶
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 |
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 |
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 |
dict[str, Any]
|
|
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 |
dict[str, Any]
|
|
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 |
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.