Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

Scientific Calculations with Julia/Basic Model of Sports Betting

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

Scientific Calculations with Julia Basic Model of Sports Betting Portfolio Optimization in Finance Supply and Demand A basic model for soccer betting can be created using the Poisson distribution to estimate goals, the outcome probability matrix for scores 0-0, 1-0, 2-1, etc., and the expected value of the bet (Expected Value). thumb|left|Poisson Distribution - The horizontal axis is the index k or number of occurrences. λ is the expected occurrence rate. The vertical axis is the probability of events k given λ. The function is defined only for integer values ​​of k. The Poisson distribution is often used in sports betting (especially soccer) because it well models the number of rare events that occur in a fixed time interval. In a soccer match, goals are few, occur discretely, and are counted as 0, 1, 2, 3... Typical example: | goals | probability | | --- | ----------- | | 0 | high | | 1 | high | | 2 | medium | | 3 | low | | 4+ | very low | This type of phenomenon is exactly what the Poisson distribution was designed to address. The probability of scoring (k) goals is: for each where (λ) = average goals (k) = number of goals Example: if (λ = 1.5) | goals | probability | | --- | ----------- | | 0 | 22% | | 1 | 33% | | 2 | 25% | | 3 | 12% | A distribution very similar to real soccer data. The outcome probability matrix relating to scores 0-0, 1-0, 2-1, etc. is the most used in soccer models. For example, the element (1,0) of the matrix is ​​given by the probability that the home team scores 1 goal, knowing that its average goal total is λ = 1.6 according to the relevant Poisson probability, multiplied by the probability that the away team scores 0 goals, knowing that its average goal total is λ = 1.1 according to the relevant Poisson probability. So essentially, you need to multiply the values ​​of two separate Poisson probability matrices. In the basic model, it is assumed that the home team's goals and the away team's goals are independent, so the probabilities multiply: P(i,j) = P(i) X P(j). The resulting matrix becomes, for example: | | 0 | 1 | 2 | 3 | | - | ---- | ---- | ---- | --- | | 0 | 0.07 | 0.08 | 0.04 | ... | | 1 | 0.10 | 0.11 | 0.06 | ... | | 2 | 0.08 | 0.09 | 0.05 | ... | so the outcome (0,1) has a probability of 8%, etc. The expected value of the bet is the most important calculation because it indicates how much you win or lose on average with each bet. EV = (P X Odds) - 1 where P = estimated true probability and Odds = bookmaker's odds For example, if the estimated probability is 50% and the odds are 2.50: EV = (0.50 X 2.50) - 1 = + 0.25, it is a theoretically profitable bet, because you theoretically earn €0.25 for every euro wagered on average. So: | EV | meaning | | ------ | ---------------------------- | | EV > 0 | theoretically profitable bet | | EV = 0 | fair game | | EV < 0 | unfavorable bet | Implementation in Julia First, I install the Julia libraries I need: using Pkg Pkg.add("Distributions") Pkg.add("CSV") Pkg.add("DataFrames") First, I plot the results matrix with a maximum of 5 goals per team: max_goals = 5 matrix = [(i,j) for i in 0:max_goals, j in 0:max_goals] matrix 6×6 Matrix{Tuple{Int64, Int64}}: (0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (1, 0) (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (2, 0) (2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (3, 0) (3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (4, 0) (4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (5, 0) (5, 1) (5, 2) (5, 3) (5, 4) (5, 5) Suppose that the home team has an average goal per game λ = 1.6 and the away team has an average goal per game λ = 1.1 and plot the probability matrix of the outcomes: using Distributions using CSV using DataFrames lambda_home = 1.6 lambda_away = 1.1 home_goals = Poisson(lambda_home) away_goals = Poisson(lambda_away) prob_matrix = zeros(max_goals+1, max_goals+1) for i in 0:max_goals for j in 0:max_goals prob_matrix[i+1, j+1] = pdf(home_goals, i) * pdf(away_goals, j) end end prob_matrix 6×6 Matrix{Float64}: 0.0672055 0.0739261 0.0406593 0.0149084 0.00409982 0.00090196 0.107529 0.118282 0.0650549 0.0238535 0.00655971 0.00144314 0.0860231 0.0946254 0.0520439 0.0190828 0.00524776 0.00115451 0.045879 0.0504669 0.0277568 0.0101775 0.00279881 0.000615738 0.0183516 0.0201867 0.0111027 0.00407099 0.00111952 0.000246295 0.00587251 0.00645976 0.00355287 0.00130272 0.000358247 7.88144e-5 Let's calculate the probabilitiesof win, draw, and loss: p_home = 0.0 p_draw = 0.0 p_away = 0.0 for i in 0:max_goals for j in 0:max_goals p = prob_matrix[i+1, j+1] if i > j p_home += p elseif i == j p_draw += p else p_away += p end end end println("Home win probability: ", p_home) println("Draw probability: ", p_draw) println("Away win probability: ", p_away) Home win probability: 0.48353796379688296 Draw probability: 0.24890698520246393 Away win probability: 0.260552747649295 So a home win is more likely at 48.35%. Let's assume the following bookmaker odds and calculate the expected value: Home = 2.30 Draw = 3.40 Away = 3.10 odds_home = 2.30 odds_draw = 3.40 odds_away = 3.10 ev_home = p_home * odds_home - 1 ev_draw = p_draw * odds_draw - 1 ev_away = p_away * odds_away - 1 println("EV home: ", ev_home) println("EV draw: ", ev_draw) println("EV away: ", ev_away) EV home: 0.11213731673283078 EV draw: -0.15371625031162262 EV away: -0.1922864822871856 Then: home → favorable draw → unfavorable away → unfavorable We can also find the most likely exact result: max_prob = 0 score = (0,0) for i in 0:max_goals for j in 0:max_goals p = prob_matrix[i+1,j+1] if p > max_prob max_prob = p score = (i,j) end end end println("Most likely result: ", score) println("Probability: ", max_prob) Most likely outcome: (1, 1) Probability: 0.1182817024219596 Estimate of lambda λ (goal averages) with Serie A data Let's download the data with these columns: | Date | HomeTeam | AwayTeam | FTHG | FTAG | | ---------- | -------- | -------- | ---- | ---- | | 2023-08-19 | Empoli | Verona | 0 | 1 | FTHG = Full Time Home Goals FTAG = Full Time Away Goals from: https://www.football-data.co.uk/mmz4281/2324/I1.csv and create the relevant DataFrame: df = CSV.read("I1.csv", DataFrame) df = select(df, :HomeTeam, :AwayTeam, :FTHG, :FTAG) rename!(df, :FTHG => :HomeGoals, :FTAG => :AwayGoals ) dropmissing!(df) teams = unique(vcat(df.HomeTeam, df.AwayTeam)) # Let's build a dataset with **goals scored and conceded**: stats = DataFrame( team = teams, goals_scored = zeros(length(teams)), goals_granted = zeros(length(teams)), matches = zeros(Int,length(teams)) ) # Calculation of averages: for row in each row(df) home = row.HomeTeam away = row.AwayTeam hg = row.HomeGoals ag = row.AwayGoals stats[stats.team .== home, :goals_scored] .+= hg stats[stats.team .== home, :goals_granted] .+= ag stats[stats.team .== home, :matches] .+= 1 stats[stats.team .== away, :goals_scored] .+= adj stats[stats.team .== away, :goals_allowed] .+= hg stats[stats.team .== away, :matches] .+= 1 end # Average goals (λ): stats.lambda_attack = stats.goals_scored ./ stats.matches stats.lambda_defense = stats.goals_granted ./ stats.matches Example result: | team | attack λ | defense λ | | ----- | -------- | --------- | | Inter | 1.9 | 0.8 | | Milan | 1.6 | 1.0 | | Roma | 1.4 | 1.2 | Now, for example, we can estimate the two λs for Inter vs. Milan: team_home = "Inter" team_away = "Milan" lambda_home = stats[stats.team .== team_home, :lambda_attack][1] lambda_away = stats[stats.team .== team_away, :lambda_attack][1] println("lambda_home =",lambda_home) println("lambda_away =",lambda_away) lambda_home =2.3421052631578947 lambda_away =2.0 Once we have the two lambdas, we can proceed by calculating the outcome probability matrix as before... Conclusions This basic model does not take into account the In fact, there is actually a dependence between the two teams' goals, because if one team scores, it can retreat into defense; if a team loses, it attacks more; red cards change the dynamics; and the strategy changes after a goal. Therefore, the Dixon-Coles model introduces a correction for results like 0-0, 1-0, 1-1, which the independent Poisson model tends to estimate poorly. Furthermore, this new model estimates lambda differently and introduces two new parameters, mu and ro (Dixon-Coles parameter), with: λ = attack_home x defense_away x home_advantage μ = attack_away x defense_home This is because it assumes that the home team has a stronger attack, the away team a stronger defense, and there is a home advantage. However, the model with two independent Poisson models is widely used because it is simple, easy to estimate, works surprisingly well, and requires few parameters, making it the basic model in the literature.Sports betting analysis.