Predictive Hacks

# Who is going to Win the Euro 2020

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%)

## 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”

1. 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)

### Get updates and learn from the best

Python

#### Creating Dynamic Forms with Streamlit: A Step-by-Step Guide

In this blog post, we’ll teach you how to create dynamic forms based on user input using Streamlit’s session state

Python

#### How to Connect Wikipedia with ChatGPT and LangChain

ChatGPT’s knowledge is limited to its training data, which has the cutoff year of 2021. This implies that we cannot