Identifying Arbitrage Opportunities#
An arbitrage bet (or “arb”) is a risk-free opportunity. It exists when you can bet on all outcomes of a single event across different bookmakers and guarantee a profit, because their odds are misaligned. These are rare but highly valuable.
The find_arbitrage_opportunities Function#
This function scans lists of odds from multiple bookmakers for the same event to find these risk-free opportunities.
penaltyblog.betting.find_arbitrage_opportunities(
bookmaker_odds_list: List[List[float]],
outcome_labels: List[str] = None
) -> ArbitrageResult
Parameters#
bookmaker_odds_list: A list of lists. Each inner list represents one bookmaker’s odds for all outcomes of an event.outcome_labels: Optional names for the outcomes (e.g., [“Home”, “Away”]).
Returns (ArbitrageResult)#
has_arbitrage(bool):Trueif a risk-free opportunity exists.guaranteed_return(float): The guaranteed profit as a percentage of your total stake.best_odds(List[float]): The best odds found for each outcome across all bookmakers.best_bookmakers(List[int]): The index of the bookmaker offering the best odds for each outcome.stake_percentages(List[float]): The percentage of your total stake to place on each outcome to guarantee the profit.
Usage Example#
import penaltyblog as pb
# Odds for a soccer match (Home Win, Draw, Away Win) from three different bookmakers
# Each inner list represents one bookmaker's odds for [Home, Draw, Away]
odds_data = [
[2.80, 3.50, 3.10], # Bookmaker 1
[3.10, 3.40, 2.90], # Bookmaker 2
[3.00, 3.20, 3.00], # Bookmaker 3
]
# Define the labels for the outcomes
outcome_labels = ["Home Win", "Draw", "Away Win"]
arb_result = pb.betting.find_arbitrage_opportunities(odds_data, outcome_labels)
if arb_result.has_arbitrage:
print("Arbitrage opportunity found!")
print(f"Guaranteed Return on Investment: {arb_result.guaranteed_return:.2%}")
print("-" * 20)
# The function tells you exactly where to bet and how much to stake
for i, label in enumerate(arb_result.outcome_labels):
stake_pct = arb_result.stake_percentages[i]
best_odd = arb_result.best_odds[i]
# Adding 1 to the index to make it human-readable (Bookmaker 1, 2, 3)
bookie_idx = arb_result.best_bookmakers[i] + 1
print(f"Bet {stake_pct:.2%} on {label} at odds {best_odd} with Bookmaker {bookie_idx}")
else:
print("No arbitrage opportunity found.")
Arbitrage opportunity found!
Guaranteed Return on Investment: 7.43%
--------------------
Bet 34.65% on Home Win at odds 3.1 with Bookmaker 2
Bet 30.69% on Draw at odds 3.5 with Bookmaker 1
Bet 34.65% on Away Win at odds 3.1 with Bookmaker 1
Simple Expected Value Calculation#
If you don’t need the full analysis from identify_value_bet and just want a quick Expected Value (EV) calculation, you can use this lightweight utility function.
penaltyblog.betting.calculate_bet_value(
bookmaker_odds: float,
estimated_probability: float
) -> float
import penaltyblog as pb
# 60% chance at odds of 2.0
ev = pb.betting.calculate_bet_value(2.0, 0.6)
print(f"Expected Value (per £1 staked): £{ev:.2f}")
Expected Value (per £1 staked): £0.20