├── images ├── rapid.png ├── blitz1.png ├── blitz2.png ├── blitz3.png ├── blitz4.png ├── bullet.png ├── venmoQR.png └── classical.png ├── index.html ├── converter.js ├── about.html └── style.css /images/rapid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/rapid.png -------------------------------------------------------------------------------- /images/blitz1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/blitz1.png -------------------------------------------------------------------------------- /images/blitz2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/blitz2.png -------------------------------------------------------------------------------- /images/blitz3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/blitz3.png -------------------------------------------------------------------------------- /images/blitz4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/blitz4.png -------------------------------------------------------------------------------- /images/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/bullet.png -------------------------------------------------------------------------------- /images/venmoQR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/venmoQR.png -------------------------------------------------------------------------------- /images/classical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLebowitz/RatingConverter/HEAD/images/classical.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | Rating Converter 16 | 17 | 18 | 19 | 20 |
21 |

Lichess to FIDE rating converter

22 |

Enter your username:

23 |
24 |
25 |
26 | 27 | 28 | 29 |
30 |
31 |
32 | calculate 33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 108 | -------------------------------------------------------------------------------- /converter.js: -------------------------------------------------------------------------------- 1 | //by Ethan Lebowitz 2 | 3 | models = { 4 | "ubrc": {"intercept": -334.2451901, "blitz_rating": 0.4530337, "classical_rating": 0.5658316}, 5 | "ubr": {"intercept": -66.8425785, "blitz_rating": 0.508449, "rapid_rating": 0.3563537}, 6 | "ubc": {"intercept": -334.2451901, "blitz_rating": 0.4530337, "classical_rating": 0.5658316}, 7 | "urc": {"intercept": -350.0336331, "rapid_rating": 0.2658311, "classical_rating": 0.6049978, "bullet_rating": 0.156788}, 8 | "brc": {"intercept": -335.6368609, "blitz_rating": 0.4556436, "classical_rating": 0.5634002}, 9 | "bc": {"intercept": -335.6368609, "blitz_rating": 0.4556436, "classical_rating": 0.5634002}, 10 | "br": {"intercept": -66.671154, "blitz_rating": 0.5103704, "rapid_rating": 0.3542381}, 11 | "ub": {"intercept": 79.9044944, "blitz_rating": 0.8033708}, 12 | "rc": {"intercept": -397.009453, "classical_rating": 0.6031852, "rapid_rating": 0.4309982}, 13 | "uc": {"intercept": -268.8950587, "classical_rating": 0.7616303, "bullet_rating": 0.2386308}, 14 | "ur": {"intercept": -54.5835126, "rapid_rating": 0.6575871, "bullet_rating": 0.201434}, 15 | "u": {"intercept": 506.0, "bullet_rating": 0.62}, 16 | "b": {"intercept": 78.6426799, "blitz_rating": 0.8039702}, 17 | "r": {"intercept": -178.7064057, "rapid_rating": 0.8985765}, 18 | "c": {"intercept": -277.3399015, "classical_rating": 0.9852217} 19 | } 20 | 21 | ratingCategories = ["bullet","blitz","rapid","classical"] 22 | 23 | function getLichessData(username){ 24 | var apiURL = "https://lichess.org/api/user/"+username; 25 | try{ 26 | var data = fetch(apiURL); 27 | }catch(e){ 28 | throw(e); 29 | } 30 | return data; 31 | } 32 | 33 | function getReliableRatingsList(userJSON){ 34 | var threshold = 20; //how many games minimum in category 35 | var reliableRatings = []; 36 | for(var i=0; i= threshold){ 38 | reliableRatings.push(ratingCategories[i]); 39 | } 40 | } 41 | return reliableRatings 42 | } 43 | 44 | function getModelID(categories){ 45 | var id = "" 46 | for(var i =0; i { 111 | return data.json() 112 | });//.catch(error=>throw(error)); 113 | if(userJSON.closed){ 114 | throw("account deleted"); 115 | } 116 | var FIDErating = convert(userJSON); 117 | fillRating(FIDErating); 118 | return true; 119 | } 120 | catch(e){ 121 | console.log(e); 122 | if(e == "empty input"){ 123 | fillErrorMessage("Enter a username"); 124 | } 125 | else if(e == "TypeError: userJSON is undefined" || e == "SyntaxError: Unexpected end of JSON input" || e == "SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data"){ 126 | fillErrorMessage("Couldn't find that user, are you sure you spelled it right?"); 127 | } 128 | else if(e == "account deleted"){ 129 | fillErrorMessage("Looks like that account was deleted") 130 | } 131 | else if(e == "not enough games"){ 132 | fillErrorMessage("You haven't played enough games yet. You need at least 20 games in a time control category to get a result. Go play some chess!") 133 | } 134 | else{ 135 | console.log(e); 136 | fillErrorMessage("Something went wrong :("); 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | About 13 | 14 | 15 | 16 | 17 |
18 | 19 |

TLDR

20 |

This site uses your ratings in each time control category that you have at least 20 games as inputs to linear regression models in order to estimate a FIDE rating. The models were generated from a data set of about 28,000 users who have listed their FIDE rating on their lichess profile.

21 |
Also, if you liked this site and would like to see it get more accurate I would be eternally grateful if you considered donating to the project.
22 | 34 |

About the Data

35 |

I started by downloading the public information of a little over a million lichess users (this took a while) and then created a dataset with only the users who listed a FIDE rating in their profile. Only about 3% of users included a FIDE rating in their profile, so the dataset came out to around 28,000 observations. Before fitting a linear model I removed every data point that had fewer than 50 games played in the time controls that I was using for the explanatory variables. Below is a scatterplot of FIDE rating plotted against blitz rating.

36 | blitz1 37 |

Because the FIDE ratings are self reported it’s a little messy. There is clearly a relationship, but there’s a lot of cleanup that has to be done. First, I removed every data point with a FIDE rating above 2900 (the record is 2882) and below 1000 (because that’s the floor in the FIDE rating system). The plot is already looking better:

38 | blitz2 39 |

There’s still a lot of noise in the top left, which makes sense because I would expect people to pretend to be stronger players than they are and not weaker than they are. To clean it some more I fit a linear model to the data and removed every point with a residual greater than two standard deviations from the median (chop at red lines):

40 | blitz3 41 |

The result looks like this:

42 | blitz4 43 |

Beautiful. If I were doing this data analysis for interpretive reasons that last step would likely have been me overstepping as an analyst, but all I’m trying to do is create a model with strong predictive power. The final step is to generate a least absolute distance model using r’s rq() function and call it a day.

44 |

Unfortunately blitz rating isn’t the only explanatory variable I’m working with. I also have bullet, rapid and classical ratings, and every combination of the four (for a total of 15 models). For the other three single regression models I repeated the steps above. All four resulting scatter plots follow: 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
bulletblitz
rapidclassical
54 |

The only thing I did differently for the multiple regression models (the models with more than one explanatory variable) was use the r’s step() function to remove redundant variables each time I fit a model. Unfortunately I can’t show the results in a scatterplot, because scatterplots only have two dimensions. The models have a Pearson correlation coefficient of 0.626 at best to 0.538 at worst.

55 |

If you have any questions, comments, or words of encouragement feel free to shoot an email to duckunreal@gmail.com

56 |
57 | 58 | 68 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .caption { 2 | padding: 8% 0 5px 0; 3 | font-size: 26px; 4 | color: #636161; 5 | font-family: "Verdana", sans-serif; 6 | } 7 | 8 | #back_button { 9 | background-color:#d9d5cc; 10 | display:inline-block; 11 | cursor:pointer; 12 | color:#ffffff; 13 | font-family:Arial; 14 | font-size:13px; 15 | font-weight:bold; 16 | padding:18px 30px; 17 | text-decoration:none; 18 | position: absolute; 19 | /*margin: 40px 0 0 40px;*/ 20 | } 21 | #back_button:hover { 22 | background-color:#aba79d; 23 | } 24 | 25 | .arrow_left { 26 | border: solid white; 27 | border-width: 0 5px 5px 0; 28 | display: inline-block; 29 | padding: 5px; 30 | transform: rotate(135deg); 31 | -webkit-transform: rotate(135deg); 32 | } 33 | 34 | 35 | .error { 36 | color: #a31515; 37 | } 38 | 39 | .center_img { 40 | display: block; 41 | margin-left: auto; 42 | margin-right: auto; 43 | width: 60%; 44 | } 45 | .table_img { 46 | width: 100%; 47 | } 48 | 49 | p, .content { 50 | font-size: 15px; 51 | color: #7c7c7c; 52 | font-family: "Verdana", sans-serif; 53 | } 54 | 55 | .content { 56 | padding: 0 40px; 57 | color: #212121; 58 | } 59 | 60 | #coffee_btn { 61 | color: #0a677a; 62 | cursor: pointer; 63 | } 64 | 65 | #bottom_text { 66 | font-family: "Verdana", sans-serif; 67 | font-size: 17px; 68 | width: 100%; 69 | text-align: center; 70 | position: relative; 71 | top: 18%; 72 | } 73 | 74 | #donate_links { 75 | display: none; 76 | } 77 | 78 | a { 79 | color: #0a677a; 80 | text-decoration: none; 81 | } 82 | 83 | .container, .about_container { 84 | height: 90%; 85 | width: 70%; 86 | margin: auto; 87 | box-shadow: 0px 0px 20px 1px rgba(0,0,0,0.3); 88 | background-color: #ffffff; 89 | padding: 0 0 30px 0; 90 | } 91 | 92 | .about_container { 93 | height: auto; 94 | } 95 | 96 | .input_container, .center { 97 | text-align: center; 98 | } 99 | 100 | input { 101 | font-size: 19px; 102 | text-align: center; 103 | border-width: 0px; 104 | border: none; 105 | padding: 10px; 106 | margin: 10px 0 20px 0; 107 | } 108 | 109 | .username:focus { 110 | outline: none; 111 | box-shadow: 0px 0px 20px 1px #c4c4c4; 112 | } 113 | 114 | body { 115 | background-color: #dbdad9; 116 | } 117 | 118 | /* progress circle from https://www.codingnepalweb.com/2020/07/circular-progress-bar-using-html-css.html */ 119 | 120 | .circular { 121 | height: 200px; 122 | width: 200px; 123 | position: relative; 124 | top: 5%; 125 | margin: auto; 126 | } 127 | 128 | .circular .inner, .circular .outer, .circular .circle { 129 | position: absolute; 130 | z-index: 6; 131 | height: 100%; 132 | width: 100%; 133 | border-radius: 100%; 134 | box-shadow: inset 0 0 3px 2px rgba(0,0,0,0.3); 135 | } 136 | .circular .inner { 137 | z-index:8; 138 | top: 50%; 139 | left: 50%; 140 | height: 180px; 141 | width: 180px; 142 | margin: -90px 0 0 -90px; 143 | background-color: #ffffff; 144 | border-radius: 100%; 145 | box-shadow: 0 0 3px 2px rgba(0,0,0,0.4); 146 | } 147 | 148 | .circular .inner:hover, #FIDE_rating_container:hover { 149 | cursor: pointer; 150 | background-color: #f0f0f0; 151 | } 152 | 153 | .circular .circle { 154 | z-index: 1; 155 | box-shadow: none; 156 | } 157 | 158 | .circular #FIDE_rating_container, .circular #calculate_container { 159 | position: absolute; 160 | top: 50%; 161 | left: 50%; 162 | transform: translate(-50%, -50%); 163 | z-index: 10; 164 | font-size: 25px; 165 | color: 7c7c7c; 166 | font-family: "Verdana", sans-serif 167 | } 168 | 169 | .circular #calculate_container { 170 | font-size: 18px; 171 | } 172 | 173 | .circular .bar { 174 | position: absolute; 175 | height: 100%; 176 | width: 100%; 177 | background: #dbdad9; 178 | -webkit-border-radius: 100%; 179 | clip: rect(0px, 200px, 200px, 100px); 180 | } 181 | .circle .bar .progress { 182 | position: absolute; 183 | height: 100%; 184 | width: 100%; 185 | -webkit-border-radius: 100%; 186 | clip: rect(0px, 100px, 200px, 0px); 187 | } 188 | .circle .bar .progress { 189 | background: #2cdb00; 190 | } 191 | 192 | .circle .left .progress { 193 | z-index: 1; 194 | animation: left 0.5s linear both; 195 | } 196 | @keyframes left { 197 | 100%{ 198 | transform: rotate(360deg); 199 | } 200 | } 201 | .circle .right { 202 | z-index: 3; 203 | transform: rotate(180deg); 204 | } 205 | .circle .right .progress { 206 | animation: right 0.5s linear both; 207 | } 208 | @keyframes right { 209 | 100%{ 210 | transform: rotate(-360deg); 211 | } 212 | } 213 | 214 | @media screen and (max-width: 650px) { 215 | .container, .about_container { 216 | height: 95%; 217 | width: 100%; 218 | margin: none; 219 | box-shadow: none; 220 | } 221 | .about_container{ 222 | height: auto; 223 | } 224 | .circular { 225 | position: relative; 226 | height: 150px; 227 | width: 150px; 228 | margin: auto; 229 | top: 20px; 230 | left: 0%; 231 | transform: translate(0%, 0%); 232 | } 233 | .circular .inner { 234 | height: 135px; 235 | width: 135px; 236 | margin: -67.5px 0 0 -67.5px; 237 | } 238 | .circular .bar{ 239 | clip: rect(0px, 150px, 150px, 75px); 240 | } 241 | .circle .bar .progress { 242 | clip: rect(0px, 75px, 150px, 0px); 243 | } 244 | .caption { 245 | padding: 8% 0 5px 0; /*change or delete*/ 246 | font-size: 18px; 247 | color: #636161; 248 | font-family: "Verdana", sans-serif; 249 | } 250 | body { 251 | background-color: #ffffff; 252 | } 253 | #bottom_text { 254 | top: 20%; 255 | } 256 | } --------------------------------------------------------------------------------