View the extension on the Chrome Web Store
Background
Betting odds are complicated on purpose. Look no further than "moneyline" (or American) odds, which like Fahrenheit are 1) only used in the US and 2) confuse everyone else. Unlike fractional odds (i.e. 4/1, 1/4) and decimal odds (i.e. 5.0, 1.25), American odds can be positive or negative. I had trouble describing in a single sentence:
If the number is positive it means "How much you'd win on a $100 wager" and if it's negative it means "How much you need to wager to win $100".
Let's use an example from the NFL. Here's an "even" game on Draftkings, where you can bet on the Bills or the Rams to win (moneyline) at -110 odds:
A 50/50 bet like this should be even odds, or 100 moneyline (wager $100 to win $100). Because The House always wins, the odds are set at -110 for either side, or "$110 to win $100".
Let's say I'm going to flip a coin - if it's heads I'll give you $10, if it's tails you owe me $11. If you play this game over and over, it's easy to see why you'd lose money over time. When it's -110, or worse something like +350 to win the Super Bowl, it's difficult to know if you're getting a "fair" price.
I'm sure there's an argument why the moneyline odds betting syntax makes more sense, but in reality it's just another way that casinos try to distract you from the amount of money you're losing.
Implied Probability
Assuming the odds are "fair", the implied probability is the percentage chance that the bet is supposed to win. In the coin example, the probability of heads is 50%. At -110 odds, however, the implied probability of choosing the Rams (or the Bills) isn't 50%, but 52.38%.
-110 odds is like saying the odds are 110 / (100 + 110) = 11/21 = 0.5238095 = 52.38% (I'll explain later)
You can tell how much the vig is by adding up the implied probabilities, which in the NFL example is about 105%. When betting on Futures, like "Super Bowl winner", this can be 140% or higher. This how sportsbooks make money: bets typically cost more than they should.
Using the implied probability is simpler way to look at sports betting: a "good bet" is one where you believe the percentage chance is higher than the implied probability.
Converting odds
There are three main ways to frame odds:
- Fractional: The profit / wager.
- Decimal: The ratio of the payout amount, including the original stake, to the stake itself.
- Moneyline/American:
- Positive: How much money will be won on a $100 wager.
- Negative: How much money must be wagered to win $100.
For example:
4/1 fractional odds implies you could make $400 profit on a $100 wager, which is +400 American
or 5.0 decimal (you get back 5x your original wager).
1/4 odds is "wager $400 to win $100", or 1.25 in decimal. This would be -400 in freedom
units.
Converting from decimal to implied probability is easy - just do 1 divided by the decimal odds. If you need to convert a moneyline to a probability, however, it's kinda messy:
If Odds < 0:
- Probability = (-1*(Odds)) / (-1(Odds) + 100)
If Odds > 0:
- Probability = 100 / (Odds + 100)
Building a Chrome Extension
I decided to automate the process by making a Chrome extension to convert the odds for me. It's a lightweight extension that scans any webpage for instances of American odds (-130, +140, etc), converts it to implied probability as a percentage, and inserts the probability next to the odds.
In javascript, the conversion function looks something like this, where match
is the
moneyline odds as a string:
function replacer(match) {
const num = parseInt(match.slice(1, match.length));
let replacement = '';
if (match[0] === '-') {
replacement = ((num / (num + 100)) * 100).toFixed(1);
} else {
replacement = ((100 / (num + 100)) * 100).toFixed(1);
}
return match + ' (' + replacement + '%)';
}
js
When the extension is activated, it runs through this function to replace the text on the page:
function replaceAllText(active) {
// Replace every text element on the page
let body = document.getElementsByTagName('body')[0];
let textNodes = [...body.querySelectorAll('*')]
.map((l) => [...l.childNodes])
.flat()
.filter((l) => l.nodeType === 3);
for (let i = 0; i < textNodes.length; i++) {
textNodeInnerHTML(textNodes[i], active);
}
}
js
Specifically, textNodeInnerHTML()
is either creating a new div to add to the page, or
removing the already-added div if they're toggling off the 'active' state. That regex is
basically searching for American odds if it's active
, otherwise it's looking for the
inserted probabilities like "(55%)".
unction textNodeInnerHTML(textNode, active) {
let regex = active ? /([+-])(\d\d\d+)/g : / \(\d*\.\d\%\)/g;
var div = document.createElement("div");
textNode.parentNode.insertBefore(div, textNode);
div.insertAdjacentHTML(
"afterend",
textNode.data.replace(regex, active ? replacer : "")
);
div.remove();
textNode.remove();
}
js
And that's pretty much it, other than adding a background script to know when the extension is actually triggered, and adding a listener to call the code above. For that same Bills Rams game, here's what happens when you click on the extension:
Building and deploying to the Chrome Web Store
To deploy it I had to register as a
Chrome Web Store Developer and add a
manifest.json
file with the extension's metadata. I also put together some images for the
Web Store listing. Then to zip it all up:
zip -r build.zip images/ background.js content_script.js manifest.json
bash
Then in the
Chrome Web Store Developer Dashboard,
upload that build.zip
and after a review from Google it was available for download.