Tags: programming | betfair
Like I had said in a previous post, I tried my hand at automated trading on Betfair for a couple of months. While I didn't make much money from it, I still think there are a lot of cool things I could write about. Here's a rough outline of what you will be able to read about in this series when I'm done:
Let's get into it, shall we?
Betfair is a betting exchange, similar to other betting exchanges (BetDAQ, Smarkets etc). Unlike a classical bookmaker (say PaddyPower or Ladbrokes), Betfair allows one to bet against an outcome (a lay bet) as well as for it. In addition, all bets on an exchange are offered by other participants in the market, not Betfair itself.
This allows people to make money not only by betting on something (essentially having a better judgment of the likelihood of a given event than other participants) but also by trading (locking in a profit by backing and laying at different odds as they move through time).
The exchange consists of events (such as, say, a given match between Arsenal and Huddersfield). Events consist of markets (such as the Match Odds market or the Exact Score market), which consists of runners (for a Match Odds market, the runners are Huddersfield, Arsenal and Draw. For the Exact Score market, it's all the scores from 0-0 to 3-3 + any Unquoted).
Arsenal vs Huddersfield event view
Runners are probably named like that because most gambling used to be on horses. In essence, runners are almost always an exact set cover of the outcomes in a given market (they're disjoint and account for all outcomes). Almost, because Betfair has "Dead Heat" rules regarding what happens to bets when, say, two horses cross the finish line at the same time.
Inside a runner, we have an order book: the odds one can take to back or lay a given runner, as well as the amount of money one can put on it. Since when backing, we always want bigger odds (if I put £1 on something at 2.0, I get back £2 if it wins and if the odds are 3.0, I get back £3 while risking the same amount of money), back offers with higher odds take priority and get matched first.
Note that a back offer appears in the order book if and only if there's someone offering to take the other side (i.e. lay the runner). When laying, lower odds are obviously better (by laying £1 pound, I get £1 pound no matter what, but, if the runner wins, I will have to pay back more with higher odds).
Order book for Arsenal winning the match
This is similar to a real exchange order book where, slightly counter-intuitively, the odds are prices and the money we're betting on something are volumes (in fact, that's how the Betfair API internally refers to order book lines).
Unlike a real-world exchange, however, the odds have varying granularities and aren't in one-cent increments. One can bet at odds of 1.01, 1.02, ..., 2.02, 2.04, ..., 3.05, 3.10, ... This is to reflect that at higher odds, risks/rewards are higher for the same bet size and so a move from odds of 1.01 to 2.0 is similar to a move from 500.0 to 1000.0. More information here.
When betting, we're looking at odds and payoffs. This is different from trading a financial instrument, where we think about it in terms of our position. For example, if I'm long an Apple share, I can sell it, thus having 0 shares and my net worth now being independent of Apple's performance, or sell 2 shares, becoming short and benefiting from Apple shares dropping in price.
It would be nice to produce something similar for betting: that way, when we're trading, we can operate with familiar terms (I'm long 1 contract on this market, I sell 1, I'm now neutral: no matter whether the horse/football team loses or wins, the money I have at the end doesn't change).
Let's think of Betfair bets in terms of one-pound contracts (or an OPC): when a given event has ended, such contract can have a value of either £1 (if say the horse we're betting on has won) or £0.
A normal bet can be either a back (betting for) or a lay (betting against). For a back of \( b \) at odds \( o \), the payoff is \( b * o \) if the runner wins and 0 if the runner loses. The actual contract costs us \( b \).
For a lay of \( b \) at odds \( o \), the payoff is opposite: \( -b * o \) if the runner wins and 0 if the runner loses — but we get \( b \) from selling the contract in any case.
How do we transform between the normal odds world and the OPC world? Let's say we back an outcome by putting \(b \) pounds on it at odds of \( o\). This is the same as buying \(b * o\) OPCs at the price \(\frac{1}{o}\) each. Hence:
Laying a runner for \( b \) at \( o \) is the same as selling \( b * o \) OPCs at the price of \(\frac{1}{o} \) each: we immediately get \(\frac{1}{o} * b * o = b \) pounds and:
Note that the back outcomes and the lay outcomes are inverses of each other: one's gain is another's loss, that is, betting is a zero-sum game.
What does this price mean? It's actually the implied probability of a given runner winning. Consider the expected payoff of backing a runner: it's \( (1 - p) * 0 + p * (b * o) - b = p * b * o - b \). On average, we would expect to make zero money from this (otherwise we could easily make money by backing a given runner all the time). Hence \( p = \frac{1}{o} \).
So essentially we can use the price of an OPC and the implied probability interchangeably and then:
It's possible to offset a back bet with a lay bet to, say, lock in a profit or a loss (also known as hedging or greening-up: no matter who wins, we get or lose the same amount of money). Using the OPC framework, however, this becomes much more intuitive.
Let's say we're trading a runner where the odds moved from \( o_1 \) down to \( o_2 \). We have placed a back bet of \( b_1 \) at \( o_1 \). Hence we have bought \( b_1 * o_1 \) OPCs for \( b_1 \).
When the odds become \( o_2 \), we want to sell these OPCs: we get \( \frac{b_1 * o_1}{o_2} \) pounds for them (in Betfair world, we have placed a lay bet of \( \frac{b_1 * o_1}{o_2} \) at the available odds of \( o_2 \)). Now we have \(\frac{b_1 * o_1}{o_2} - b_1 \) pounds and 0 OPCs, so we are market neutral in our framework.
Does this actually work? If the runner wins:
If the runner loses:
So the payoff is the same in both cases.
On Betfair, one can often bet on a conjunction of disjoint events (events in the probability theory sense, not in the Betfair sense) as well as as each of those events separately. For example, Arsenal winning a given football match is the same as Arsenal winning with a score of 1-0 or Arsenal winning with a score of 2-0 or Arsenal winning with a score of 2-1 etc. Technically, the available bets in that case only go up to 3 goals on each side, but outcomes beyond that are fairly rare (and in the proposed strategy we end up better off with an unquoted outcome).
Assuming these two events (Arsenal winning at all and Arsenal winning with either of the available scores) are the same, their implied probabilities and hence prices should also be the same. So it's possible that we can sometimes lock in a small risk-free profit by backing the total outcome and laying each one of its constituents.
How do we calculate whether this arbitrage exists?
Let's say an event \(E\) is a conjunction of disjoint events \(E_1 ... E_k\). If we can bet on these events with implied probabilities (prices) \(p, p_1, ... p_k\) we can buy 1 OPC on \(E\) for \(p\) and then immediately turn around and sell \(k\) OPCs on \(E_i\) for \(p_i\) each (getting \(p_1 + p_2 + ... + p_k\) in total).
Since these events are disjoint, if \(E\) happens, only one of \(E_1, ..., E_k\) happens. Then the contract on \(E\) becomes worth £1, the contract on one and only one \(E_i\) is worth -£1 and the rest are worth £0, hence we're only left with the money from the original trade (the contracts offset each other).
If \(E\) doesn't happen, all OPCs expire worthless. Hence we're market-neutral.
The money we get from buying a contract and immediately selling its constituents is \(-p + p_1 + p_2 + ... + p_k\). So if \(p\) is greater (or less) than the constituents, there's an arbitrage.
In fact, in this case, if the first team wins with an unquoted score (i.e. above 3), then neither of \(E_i\) happens and we pocket a profit of \(p_1 + p_2 + ... + p_k\).
In practice, there are a few real-world issues that make this unrealistic, such as bid-offer spreads. If we back something and then immediately lay it, we lose money, since there's a difference between the best available back odds and best available lay odds. Even with the smallest spreads (for example, backing at 1.71 and laying at 1.72), the cost is about 0.5%. Worse even, Betfair charges a 5% commission on the winnings in a given market: since in our case, we're trading 2 markets (Match Odds and Exact Score) with only one of them winning, we have to have an expected profit of 5% just to break even.
Thanks for reaching the end of this math-heavy bit! Next up, we'll start collecting data from the Betfair Stream API and things might begin moving faster.
Posts in this series will be available at https://kimonote.com/@mildbyte:betfair or on this RSS feed . Alternatively, follow me on twitter.com/mildbyte.
Interested in this blogging platform? It's called Kimonote, it focuses on minimalism (the only JavaScript is MathJax on this and another page), ease of navigation and control over what content a user follows. Try the demo here and/or follow it on Twitter as well at twitter.com/kimonote!