Download or view Odds.frink in plain text format
/** This is a library that helps parse and generate odds statements. There is
a lot of semi-standardized terminology and relationship between probability
and odds but they are not the same.
See:
https://en.wikipedia.org/wiki/Odds
* "X in Y" means that the probability is p = X / Y.
* "X to Y in favor" means that the probability is p = X / (X + Y).
* "X to Y against" means that the probability is p = Y / (X + Y).
* "pays X to Y" means that the bet is a fair bet if the probability is
p = Y / (X + Y).
* "pays X for Y" means that the bet is a fair bet if the probability is
p = Y / X.
* "pays +X" means that the bet is fair if the probability is
p = 100 / (X + 100).
* "pays −X" means that the bet is fair if the probability is
p = X / (X + 100).
*/
class Odds
{
/** This parses an odds statement in any of the formats given above. */
class parseToProbability[str] :=
{
str = trim[str]
// "X in Y" -> p = X / Y
if [x, y] = str =~ %r/^(.*?)\s+in\s+(.*)$/i
{
x = asRational[x]
y = asRational[y]
if x != undef AND y != undef
{
// println["$x in $y"]
return x/y
}
}
// "X to Y in favor" -> p = X / (X+Y)
if [x, y] = str =~ %r/^(.*?)\s+to\s+(.*?)\s+in\s+favor$/i
{
x = asRational[x]
y = asRational[y]
if x != undef AND y != undef
{
// println["$x to $y in favor"]
return x/(x+y)
}
}
// "X to Y against" -> p = Y / (X+Y)
if [x, y] = str =~ %r/^(.*?)\s+to\s+(.*?)\s+against$/i
{
x = asRational[x]
y = asRational[y]
if x != undef AND y != undef
{
// println["$x to $y against"]
return y/(x+y)
}
}
// "pays X to Y" -> p = Y / (X + Y)
if [x, y] = str =~ %r/^pays\s+(.*?)\s+to\s+(.*?)$/i
{
x = asRational[x]
y = asRational[y]
if x != undef AND y != undef
{
// println["pays $x to $y"]
return y/(x+y)
}
}
// "pays X for Y" -> p = Y / X
// This case seems weird and wrong because if you do something like
// "pays 5 for 50" then p = 10 which is outside the range [0,1] and
// is thus not a valid probability.
if [x, y] = str =~ %r/^pays\s+(.*?)\s+for\s+(.*?)$/i
{
x = asRational[x]
y = asRational[y]
if x != undef AND y != undef
{
// println["pays $x for $y"]
return y/x
}
}
// "pays +X" -> p = 100 / (x + 100)
// This is equivalent to American / Moneyline bets for positive numbers
if [x, y] = str =~ %r/^(?:moneyline|pays)\s+(\+.*?)$/i
{
x = asRational[x]
if x != undef
{
// println["pays +$x"]
return 100 / (x + 100)
}
}
// "pays -X" -> p = X / (X + 100) (X is without negative sign!)
// This is equivalent to American / Moneyline bets for negative numbers
if [x, y] = str =~ %r/^(?:moneyline|pays)\s+(\-.*?)$/i
{
x = asRational[x]
if x != undef
{
// println["pays $x"] // Negative sign is emitted
return (-x) / ((-x) + 100) // Note negation of negative number
}
}
return undef
}
/** Returns a string in the format "X in Y" given a probability.
p = x / y
so fixing Y at 1 gives x = p y
*/
class XinY[p, ugly=false] :=
{
// It's easier to calculate "x to 1" than like "3 to 2" so set y=1
y = 1
x = p y
if ugly // You wanted the "3 to 2" behavior though. Okay.
[x,y] = numeratorDenominator[x]
return inputForm[x] + " in " + inputForm[y]
}
/** Returns a string in the format "X to Y in favor" given a probability.
p = X / (X + Y)
so fixing Y at 1 gives x = p y / (1-p)
*/
class XtoYinFavor[p, ugly=false] :=
{
// It's easier to calculate "x to 1" than like "3 to 2" so set y=1
y = 1
x = p y / (1-p)
if ugly // You wanted the "3 to 2" behavior though. Okay.
[x,y] = numeratorDenominator[x]
return inputForm[x] + " to " + inputForm[y] + " in favor"
}
/** Returns a string in the format "X to Y against" given a probability.
p = Y / (X + Y)
so fixing Y at 1 gives x = y/p - y
*/
class XtoYAgainst[p, ugly=false] :=
{
// It's easier to calculate "x to 1" than like "3 to 2" so set y=1
y = 1
x = y/p - y
if ugly // You wanted the "3 to 2" behavior though. Okay.
[x,y] = numeratorDenominator[x]
return inputForm[x] + " to " + inputForm[y] + " against"
}
/** Returns a string in the format "pays X to Y" given a probability.
p = Y / (X + Y)
so fixing Y at 1 gives x = y/p - y
*/
class paysXtoY[p, ugly=false] :=
{
// It's easier to calculate "x to 1" than like "3 to 2" so set y=1
y = 1
x = y/p - y
if ugly // You wanted the "3 to 2" behavior though. Okay.
[x,y] = numeratorDenominator[x]
return "pays " + inputForm[x] + " to " + inputForm[y]
}
/** Returns a string in the format "pays X for Y" given a probability.
p = y / x
so fixing Y at 1 gives x = y/p
*/
class paysXforY[p, ugly=false] :=
{
// It's easier to calculate "x to 1" than like "3 for 2" so set y=1
y = 1
x = y/p
if ugly // You wanted the "3 for 2" behavior though. Okay.
[x,y] = numeratorDenominator[x]
return "pays " + inputForm[x] + " for " + inputForm[y]
}
/** Returns a string in the format "pays +-X" given a probability. The
sign is decided by the probability:
If the probability is less than 1/2, the sign is positive.
If the probability is greater than 1/2, the sign is negative.
Equal odds (p = 1/2) should result in +/- 100 (they are equivalent)
"pays +X" means that the bet is fair if the probability is p = 100 / (X + 100).
"pays −X" means that the bet is fair if the probability is p = X / (X + 10
THINK ABOUT: Add a flag to round/ceil/floor rational numbers to an integer?
*/
class pays[p, word="pays"] :=
{
if p <= 1/2
return paysPlus[p, word]
else
return paysMinus[p, word]
}
/** Returns a string in the format "Moneylines +-X" given a probability.
This is the same as "pays +-X" so see the comments in pays[p].
*/
class moneyline[p] :=
{
return pays[p, "Moneyline"]
}
/** Returns a string in the format "pays +X" given a probability.
"pays +X" means that the bet is fair if the probability is
p = 100 / (X + 100)
so
x = 100/p - 100
This should probably only be called if p <= 1/2 and should be called
from the "pays" function.
You can also choose another word than "pays" by setting the second
argument to a string. This is, for example, used by the "moneyline"
function.
THINK ABOUT: Add a flag to round/ceil/floor rational numbers to an integer?
*/
class paysPlus[p, word="pays"] :=
{
x = 100/p - 100
return "$word +" + inputForm[x]
}
/** Returns a string in the format "pays -X" given a probability.
"pays -X" means that the bet is fair if the probability is
p = x / (x + 100)
so
x = 100 p / (1-p)
This should probably only be called if p >= 1/2 and should be called
from the "pays" function.
THINK ABOUT: Add a flag to round/ceil/floor rational numbers to an integer?
*/
class paysMinus[p, word="pays"] :=
{
x = 100 p / (1-p)
return "$word -" + inputForm[x]
}
/** Parses a numeric dimensionless string and returns it as a rational
number if it is a numeric value, or returns undef otherwise. */
class asRational[str] :=
{
val = eval[str]
if isUnit[val] and (val conforms dimensionless)
return toRational[val]
else
return undef
}
}
println[Odds.parseToProbability["1 in 10"]]
println[Odds.parseToProbability["2 to 20 in favor"]]
println[Odds.parseToProbability["3 to 30 against"]]
println[Odds.parseToProbability["pays 4 to 40"]]
println[Odds.parseToProbability["pays 5 for 50"]]
println[Odds.parseToProbability["pays +600"]]
println[Odds.parseToProbability["pays -700"]]
Download or view Odds.frink in plain text format
This is a program written in the programming language Frink.
For more information, view the Frink
Documentation or see More Sample Frink Programs.
Alan Eliasen was born 20217 days, 23 hours, 21 minutes ago.