Friday, March 14, 2014

Implementing ELO Ratings

I've been planning for quite some time to implement an ELO Rating system in my World Cup project to predict the strength of foot ball teams. ELO is a method of calculating the relative strength of players. Therefore, you'd expect Brazil and Spain to have high ELO ratings. While I need some tweaking to the formula to make some events more important (For example, a friendly international isn't as important to win as a World Cup Final). You can see these early ratings on the World Cup detail pages, for example, World Cup 2010.

The actual code to calculate the ELO Rating is below. It runs quick, processing all 800 games I have in my database in less than a second. 

  /// <summary>
        ///  Updates the scores in the matchup object. 
        /// </summary>
        /// <param name="matchup">The Matchup to update</param>
        /// <param name="user1WonMatch">Whether User 1 was the winner (if both user 1 and user 2 are false, it's a draw)</param>
        /// <param name="user2WonMatch">Whether User 2 was the winner</param>
        /// <param name="diff">The desired Diff, currently a constant of 400</param>
        /// <param name="kFactor">The K factor. My K factor currently takes into account the type of game & the goals difference</param>
        /// <returns></returns>
        public static Matchup UpdateEloRatingScores(Matchup matchup, bool user1WonMatch, bool user2WonMatch, double diff, double kFactor)
        {
            // player A rating
            double User1ScoreDouble = Convert.ToDouble(matchup.User1Score);
            // player B rating
            double User2ScoreDouble = Convert.ToDouble(matchup.User2Score);

            //expected score for player A = 1 / (1 + 10 ^ ((player A rating - player B rating) / 400 ))
            double player1ExpectedScore = 1d / Convert.ToDouble(1d + Math.Pow(10d, ((User2ScoreDouble - User1ScoreDouble) / diff)));
            //expected score for player B = 1 / (1 + 10 ^ ((player B rating - player A rating) / 400 ))
            double player2ExpectedScore = 1d / Convert.ToDouble(1d + Math.Pow(10d, ((User1ScoreDouble - User2ScoreDouble) / diff)));

            double user1Result = 0;
            double user2Result = 0;
            if (user1WonMatch == true)
            {
                user1Result = 1;
            }
            else if (user2WonMatch == true)
            {
                user2Result = 1;
            }
            else //split the result evenly for a draw
            {
                user1Result = 0.5;
                user2Result = 0.5;
            }

            //player new rating = [player A current rating] + [K Factor] ( userResult - playerExpectedScore )
            matchup.User1Score = Convert.ToInt32(Math.Round(Convert.ToDouble(matchup.User1Score) + kFactor * (user1Result - player1ExpectedScore)));
            matchup.User2Score = Convert.ToInt32(Math.Round(Convert.ToDouble(matchup.User2Score) + kFactor * (user2Result - player2ExpectedScore)));

            return matchup;
        }

No comments:

Post a Comment