Pairing back and lay odds across UK bookmakers
To pair back and lay odds across UK bookmakers, take one outcome from a matched board, keep the back offers from the books your user holds, and give each one the lowest lay whose liquidity covers the lay stake.
The board has already agreed the fixture, market and line. What it can't choose is the pair, because that depends on your user.
This post is for teams building arbitrage and trading tools, who already know a back bet from a lay price. Matching bookmaker and exchange odds covers whether two prices are the same bet. This post starts one step later, with which of those prices share a row in your tool.
How do you pair a bookmaker back price with an exchange lay price?
Take both from the same outcome, because each outcome on OddsRelay's standard board carries a back list and a lay list. On a line market, each point has its own outcome, so Over at 2.5 goals never shares a list with Over at 3.5. Three back offers and two lay offers make six possible pairs:
{
"name": "Arsenal",
"back": [
{ "bookmaker": "sky_bet", "price": 2.95, "link": null },
{ "bookmaker": "william_hill", "price": 2.9, "link": null },
{ "bookmaker": "paddy_power", "price": 2.88, "link": null }
],
"lay": [
{ "exchange": "matchbook", "price": 2.94, "available": 38.5, "link": null },
{ "exchange": "smarkets", "price": 2.98, "available": 412, "link": null }
]
}back comes best price first and lay lowest price first, so back[0] and lay[0] look like the answer. Often they're the wrong pair. Here, a user with no Sky Bet account can't take the first back offer, and a back stake of 100 asks more than the 38.5 behind the first lay can take.
Which lay do you pair when several exchanges price the same outcome?
Pair the lowest lay that can take the lay stake, and drop the back offer if none can. The board draws its lays from Betfair, Smarkets and Matchbook, and every lay offer carries available. The list is already in price order, so the first offer that fits is the one you want. The lay stake changes with the back price and each exchange's commission, so work it out per offer:
// Customer-side code. The books, stake and commission rates are your user's own. // One rate per exchange: a missing rate gives NaN and skips that offer. const books = new Set(["william_hill", "paddy_power"]); const commission = { smarkets: 0.02, matchbook: 0.02 }; const backStake = 100; const layStake = (back, lay) => (backStake * back.price) / (lay.price - commission[lay.exchange]); const rows = outcome.back .filter((back) => books.has(back.bookmaker)) .flatMap((back) => { const lay = outcome.lay.find( (l) => l.available !== null && l.available >= layStake(back, l), ); return lay ? [{ back, lay, layStake: layStake(back, lay) }] : []; });
Run that over the outcome above and the books check drops Sky Bet, the liquidity check drops Matchbook's lay, and two rows are left:
| Back offer | Lay stake on Matchbook | Pairs with | Lay stake there |
|---|---|---|---|
| William Hill at 2.9 | 99.32, too much for its liquidity | Smarkets at 2.98 | 97.97 |
| Paddy Power at 2.88 | 98.63, too much for its liquidity | Smarkets at 2.98 | 97.30 |
Drop the back stake to 25 and both rows move to Matchbook, because a lay stake near 25 fits inside Matchbook's 38.5, and Matchbook's price is lower. Nothing in the reply changed, which is why these rows belong in your code and not in a cache of the board. Why lay liquidity matters sets out what a scanner can do with a short lay besides dropping it.
Commission stays in your code too, because each user pays their own rate and that rate can change which offer fits.
One best row hides the rest
Keep a row for every back offer that passes, instead of one best row per outcome. The best price across the market tells a user little if the next-best is the one at a book they can still bet with. Rows also make your screen easy to filter, and when a user loses an account, that book drops out of books with no change to anything else.
The bookmakers and exchanges filters narrow the books on the way in, and on the standard board a filtered call costs less. They still can't replace the check in your code when one reply serves users who hold different accounts. A filter also drops any outcome left with no back offer or no lay offer, as the matched-board filters guide explains.
What does a scanner do with a paired row?
A scanner runs its own sums on each row and decides what to show. The board flags nothing and computes nothing, so each of these is your code:
- The crossing check. Whether the back price beats the lay once commission comes off. Arbitrage data has the line of code, and neither row above crosses.
- The pair's age.
meta.last_gives the last read of each venue and sport. A row is at most as old as the older of its two reads, so check both before an alert goes out.seen - The way to each leg. Each offer's
linkopens that venue's page, the event's own where we have one and otherwise a sport or home page, and it can benull, so check it before you send a user there. - The back side's limit. Back offers carry no size. What a bookmaker accepts is set per account, so only your user's own history can tell you.
A value tool swaps the crossing check for a fair price of its own. Value betting vs arbitrage sets the two side by side.
Pair one market on your users' books
Price a call for one sport and market with quote=, which costs nothing, then run the loop above with your users' books, stakes and rates. Count how many rows survive at each stake. Odds data for trading tools sets out the calls. A scanner other people use runs on Enterprise, and so does one a business keeps for itself.
Related posts
- Arbitrage data: what an arbitrage tool needsAn arb check needs back and lay prices for the same market, and the money behind the lay.
- Matching bookmaker and exchange oddsA back and a lay price pair up only when six things agree. Where venue rules pull them apart.
- Why lay liquidity matters in a matched odds feedA lay price with no money behind it cannot be taken. How to check a pair against your stake.
18+ · OddsRelay supplies odds data only and takes no bets. Please gamble responsibly.