Skip to content

Payoff Surfaces

Payoff surfaces compute the outcome of a bet for every possible scoreline on the goal grid.

How It Works

  1. A PayoffSurface maps each (home, away) scoreline to a payoff: +1 (win), 0 (push), or -1 (loss)
  2. Combined with a model's PMF, this yields expected value per cell
  3. Summing gives the model-implied win/loss probability and fair odds

Asian Handicap

from pyrf.payoffs import AsianHandicap
from pyrf.market import QuotedLine
from pyrf.models import DixonColes
from pyrf.core import GoalGrid

surface = AsianHandicap(line=QuotedLine(value=-0.5))
model = DixonColes(lambda_home=1.8, lambda_away=1.2, rho=-0.10)
grid = GoalGrid.default()

# Payoff grid: +1 / 0 / -1 for each scoreline
payoffs = surface.grid_payoff(grid)

# Model-implied fair odds
implied = surface.implied_odds(model, grid)

Total Goals (Over/Under)

from pyrf.payoffs import TotalGoals

surface = TotalGoals(line=QuotedLine(value=2.5))
implied = surface.implied_odds(model, grid)

Quarter-Line Blending

Quarter lines (e.g., 2.25, 2.75) are split into two adjacent half lines and averaged. This matches the industry convention where a quarter-line bet is effectively half-stake on each neighbouring half line.

surface = TotalGoals(line=QuotedLine(value=2.75))
# Internally: 50% on 2.5, 50% on 3.0

From Payoffs to Implied Odds

The full pipeline:

# Get probabilities for each side
probs = surface.implied_prob(model, grid)

# Convert to a fair Quote
quote = surface.implied_odds(model, grid)

API Reference

See the full API Reference.