We have reached the knock-out phase of Euro 2020 (or 2021) where the final-16 teams and the games can be shown below:
The question is who is going to be the Euro 2020 Winner. Although we cannot predict the Winner, we can estimate the probabilities of each team to win the Euro.
The Methodology
This is a very simple model that is based on FIFA Ranking. Our assumption is that each team follows the Normal Distribution, with mean its corresponding ranking and standard deviation the (max ranking – min ranking)/6. Regarding the standard deviation, we assumed that the UEFA teams are coming from the same distribution where the standard deviation can be estimated from the 6 sigma rule.
Finally, once we define the distribution of each team, we will simulate the results of Round 16 and we will estimate the Winner.
The Code
We will work in R and we will run 10000 simulations.
# get the UEFA Ranking ranking<-list(bel=1783, fra=1757, eng=1687, por=1666, esp=1648, ita=1642, den=1632, ger=1609, sui=1606, cro=1606, net=1598, wal=1570, swe=1570, aus=1523, ukr=1515, cze=1459) # get the range of the max UEFA ranking (Belgium) minus the minimum (San Marino) and devide it by 6 # assuming that 6 sigma is the range stdev<-(1783-805)/6 sim_champion<-c() for (i in 1:10000) { ####################### #### Final 16 ####################### # game bel vs por teams_game1<-c("bel","por") game1<-rnorm(1, ranking[[teams_game1[1]]], stdev)>rnorm(1, ranking[[teams_game1[2]]], stdev) if (game1) { qualified_game1<-teams_game1[1] } else { qualified_game1<-teams_game1[2]} qualified_game1 # game ita vs aus teams_game2<-c("ita","aus") game2<-rnorm(1, ranking[[teams_game2[1]]], stdev)>rnorm(1, ranking[[teams_game2[2]]], stdev) if (game2) { qualified_game2<-teams_game2[1] } else { qualified_game2<-teams_game2[2]} qualified_game2 # game fra vs sui teams_game3<-c("fra","sui") game3<-rnorm(1, ranking[[teams_game3[1]]], stdev)>rnorm(1, ranking[[teams_game3[2]]], stdev) if (game3) { qualified_game3<-teams_game3[1] } else { qualified_game3<-teams_game3[2]} qualified_game3 # game cro vs esp teams_game4<-c("cro","esp") game4<-rnorm(1, ranking[[teams_game4[1]]], stdev)>rnorm(1, ranking[[teams_game4[2]]], stdev) if (game4) { qualified_game4<-teams_game4[1] } else { qualified_game4<-teams_game4[2]} qualified_game4 # game swe vs ukr teams_game5<-c("swe","ukr") game5<-rnorm(1, ranking[[teams_game5[1]]], stdev)>rnorm(1, ranking[[teams_game5[2]]], stdev) if (game5) { qualified_game5<-teams_game5[1] } else { qualified_game5<-teams_game5[2]} qualified_game5 # game eng vs ger teams_game6<-c("eng","ger") game6<-rnorm(1, ranking[[teams_game6[1]]], stdev)>rnorm(1, ranking[[teams_game6[2]]], stdev) if (game6) { qualified_game6<-teams_game6[1] } else { qualified_game6<-teams_game6[2]} qualified_game6 # game net vs cze teams_game7<-c("net","cze") game7<-rnorm(1, ranking[[teams_game7[1]]], stdev)>rnorm(1, ranking[[teams_game7[2]]], stdev) if (game7) { qualified_game7<-teams_game7[1] } else { qualified_game7<-teams_game7[2]} qualified_game7 # game wal vs den teams_game8<-c("wal","den") game8<-rnorm(1, ranking[[teams_game8[1]]], stdev)>rnorm(1, ranking[[teams_game8[2]]], stdev) if (game8) { qualified_game8<-teams_game8[1] } else { qualified_game8<-teams_game8[2]} qualified_game8 ####################### #### Final 8 ####################### teams_f8_1<-c(qualified_game1,qualified_game2) game_f8_1<-rnorm(1, ranking[[teams_f8_1[1]]], stdev)>rnorm(1, ranking[[teams_f8_1[2]]], stdev) if (game_f8_1) { qualified_f8_1<-teams_f8_1[1] } else { qualified_f8_1<-teams_f8_1[2]} qualified_f8_1 teams_f8_2<-c(qualified_game3,qualified_game4) game_f8_2<-rnorm(1, ranking[[teams_f8_2[1]]], stdev)>rnorm(1, ranking[[teams_f8_2[2]]], stdev) if (game_f8_2) { qualified_f8_2<-teams_f8_2[1] } else { qualified_f8_2<-teams_f8_2[2]} qualified_f8_2 teams_f8_3<-c(qualified_game5,qualified_game6) game_f8_3<-rnorm(1, ranking[[teams_f8_3[1]]], stdev)>rnorm(1, ranking[[teams_f8_3[2]]], stdev) if (game_f8_3) { qualified_f8_3<-teams_f8_3[1] } else { qualified_f8_3<-teams_f8_3[2]} qualified_f8_3 teams_f8_4<-c(qualified_game7,qualified_game8) game_f8_4<-rnorm(1, ranking[[teams_f8_4[1]]], stdev)>rnorm(1, ranking[[teams_f8_4[2]]], stdev) if (game_f8_4) { qualified_f8_4<-teams_f8_4[1] } else { qualified_f8_4<-teams_f8_4[2]} qualified_f8_4 ####################### #### Final 4 ####################### teams_f4_1<-c(qualified_f8_1,qualified_f8_2) game_f4_1<-rnorm(1, ranking[[teams_f4_1[1]]], stdev)>rnorm(1, ranking[[teams_f4_1[2]]], stdev) if (game_f4_1) { qualified_f4_1<-teams_f4_1[1] } else { qualified_f4_1<-teams_f4_1[2]} qualified_f4_1 teams_f4_2<-c(qualified_f8_3,qualified_f8_4) game_f4_2<-rnorm(1, ranking[[teams_f4_2[1]]], stdev)>rnorm(1, ranking[[teams_f4_2[2]]], stdev) if (game_f4_2) { qualified_f4_2<-teams_f4_2[1] } else { qualified_f4_2<-teams_f4_2[2]} qualified_f4_2 ####################### #### Final ####################### teams_f<-c(qualified_f4_1,qualified_f4_2) game_f<-rnorm(1, ranking[[teams_f[1]]], stdev)>rnorm(1, ranking[[teams_f[2]]], stdev) if (game_f) { champion<-teams_f[1] } else { champion<-teams_f[2]} sim_champion<-c(sim_champion,champion) } prop.table(table(sim_champion))*100
The Estimated Results
After running 10,000 simulations, we get that Belgium is the most likely team to win the Euro 2020 with a 25.8% probability followed by France (21%) and England (13%)
Team | Probability (%) |
Belgium | 25.82 |
France | 21.17 |
England | 13.18 |
Denmark | 6.61 |
Italy | 5.24 |
Netherlands | 5.18 |
Spain | 4.74 |
Portugal | 4.66 |
Germany | 3.76 |
Sweden | 2.31 |
Croatia | 2.14 |
Wales | 2.1 |
Switzerland | 1.91 |
Ukraine | 0.78 |
Austria | 0.22 |
Czech Republic | 0.18 |
The Estimated Odds from the Market/Bookmakers
According to Bookmaker, the favorite to win the trophy is France, followed by Italy and England.
My Suggestion
Personally, I see mispricing in Bookmakers’ odds for Belgium that pays off 9 times (meaning an estimated probability to win the euro 11.11%). So my suggestion is to go with Belgium!
1 thought on “Who is going to Win the Euro 2020”
Lol, so much fun, I think you can make it more concise by creating some functions. Here is my way of doing the same:
# Get the UEFA Ranking
ranking <- list(
bel = 1783,
fra = 1757,
eng = 1687,
por = 1666,
esp = 1648,
ita = 1642,
den = 1632,
ger = 1609,
sui = 1606,
cro = 1606,
net = 1598,
wal = 1570,
swe = 1570,
aus = 1523,
ukr = 1515,
cze = 1459
)
# Brackets for the round of 16 (the order is important!)
brackets <- list(
game1 = c("bel", "por"),
game2 = c("ita", "aus"),
game3 = c("fra", "sui"),
game4 = c("cro", "esp"),
game5 = c("swe", "ukr"),
game6 = c("eng", "ger"),
game7 = c("net", "cze"),
game8 = c("wal", "den")
)
# Range of the max UEFA ranking (Belgium) minus the minimum (San Marino) and
# divide it by 6 (estimated from the 6 sigma rule)
stdev <- (1783 – 805) / 6
simulate_game <- function(team1, team2) {
get_prob <- function(ranking, sd) {
rnorm(mean = ranking, sd = sd, n = 1)
}
prob1 <- get_prob(ranking[[team1]], sd = stdev)
prob2 prob2) {
return(team1)
} else {
return(team2)
}
}
# Function to create brackets again, used to create brackets again after
# obtaining the results from a simulated round
create_brackets <- function(x) {
brackets <- vector("list", length(x)/2)
i <- 1
for (j in seq_along(brackets)) {
brackets[[j]] <- c(x[[i]], x[[i+1]])
i <- i + 2
}
brackets
}
simulated_champions 1) {
results <- lapply(brackets, function(game) simulate_game(game[[1]], game[[2]]))
brackets <- create_brackets(results)
}
champion <- simulate_game(unlist(results)[[1]], unlist(results)[[2]])
champion
})
sort(prop.table(table(simulated_champions)) * 100, decreasing = TRUE)