Active markets
4,731
Active orders
15,000+
7d volume
$7.95M
7d bets
47,880
GUIDE/sports-betting/guides/back-lay-arbitrage-strategy
Back-Lay Arbitrage Strategy: How to Lock In Guaranteed Profits on a Betting Exchange
Back-lay arbitrage explained: how to back at a sportsbook, lay at an exchange, and calculate stakes for guaranteed profit. Includes worked examples and commission impact.
May 16, 2026·10 min read
import asyncio, os, requests
from centrifuge import (
Client, SubscriptionEventHandler, SubscriptionOptions,
PublicationContext, SubscribedContext,
)
API = "https://api.sx.bet" # REST — no API key needed
WS = "wss://realtime.sx.bet/connection/websocket" # WS token fetched below
# The WebSocket requires a realtime token from /user/realtime-token/api-key
# (this is the only place an API key is used — every REST call below is public).
def fetch_token():
res = requests.get(
f"{API}/user/realtime-token/api-key",
headers={"x-api-key": os.environ["SX_API_KEY"]},
)
res.raise_for_status()
return res.json()["token"]
# Decode one maker order → taker price + taker/maker liquidity.
# percentageOdds = maker_implied * 10^20; taker_implied = 1 - maker_implied.
# A maker betting outcome ONE is quoting a TAKER price on outcome TWO.
def decode(o):
maker_implied = int(o["percentageOdds"]) / 1e20
taker_implied = 1 - maker_implied
if not (0 < taker_implied < 1):
return None
decimal_odds = 1 / taker_implied
maker_usdc = int(o["totalBetSize"]) / 1e6
taker_usdc = maker_usdc / (decimal_odds - 1)
taker_side = "two" if o["isMakerBettingOutcomeOne"] else "one"
return decimal_odds, maker_usdc, taker_usdc, taker_side
def print_book(orders):
book = {"one": [], "two": []}
for o in orders.values():
if o.get("status", "ACTIVE") != "ACTIVE":
continue
row = decode(o)
if row: book[row[3]].append(row)
for side in ("one", "two"):
rows = sorted(book[side], key=lambda r: -r[0])
if not rows:
print(f" {side}: —"); continue
taker = sum(r[2] for r in rows); maker = sum(r[1] for r in rows)
print(f" {side}: best {rows[0][0]:.2f}"
f" · taker_liq={taker:,.0f} · maker_liq={maker:,.0f} USDC")
async def stream(market_hash):
orders, seen, buffer = {}, set(), []
ready = False
# Full REST snapshot — public endpoint, no API key needed.
def reseed():
nonlocal orders
data = requests.get(f"{API}/orders",
params={"marketHashes": market_hash}).json()["data"]
orders = {o["orderHash"]: {**o, "status": "ACTIVE"}
for o in data if o["marketHash"] == market_hash}
def apply_update(batch):
for u in batch:
prev = orders.get(u["orderHash"])
# Compare updateTime as int — drop stale messages.
if prev and "updateTime" in prev and "updateTime" in u:
if int(u["updateTime"]) < int(prev["updateTime"]): continue
orders[u["orderHash"]] = u
print_book(orders)
async def on_subscribed(ctx: SubscribedContext):
nonlocal ready, buffer
# If Centrifugo replayed the full gap, keep local state as-is.
if ctx.was_recovering and ctx.recovered:
ready = True; return
ready = False; buffer = []
reseed() # fresh connect OR history pruned
for b in buffer: apply_update(b) # drain anything that came in meanwhile
buffer = []; ready = True
print_book(orders)
async def on_publication(ctx: PublicationContext):
msg_id = (ctx.tags or {}).get("messageId")
if msg_id: # dedupe — at-least-once delivery
if msg_id in seen: return
seen.add(msg_id)
if not ready: buffer.append(ctx.data)
else: apply_update(ctx.data)
client = Client(WS, get_token=fetch_token)
handler = SubscriptionEventHandler(on_subscribed=on_subscribed,
on_publication=on_publication)
opts = SubscriptionOptions(positioned=True, recoverable=True)
sub = client.new_subscription(f"order_book:market_{market_hash}",
handler, opts)
await client.connect()
await sub.subscribe()
await asyncio.Future() # run forever
# Pick a market (public REST — no API key needed) and stream it live.
markets = requests.get(f"{API}/markets/active",
params={"onlyMainLine": "true", "pageSize": 5}
).json()["data"]["markets"]
asyncio.run(stream(markets[0]["marketHash"]))
Sports Prediction Market
Sign up on sx.bet to start trading instantly
◆ Today's picks
15 featured
esp.1Apr 23
Sevilla
—
vs
Levante
—
Sevilla vs Levante picks, prediction, and live odds for Thursday April 23
Read analysis
NBAApr 23
Cavaliers
—
vs
Toronto Raptors
—
Cleveland Cavaliers vs Toronto Raptors Picks, Prediction & Odds
Read analysis
NBAApr 23
Denver Nuggets
—
vs
Timberwolves
—
Denver Nuggets vs Minnesota Timberwolves Picks, Prediction & Odds
Read analysis
NBAApr 23
York Knicks
—
vs
Atlanta Hawks
—
New York Knicks vs Atlanta Hawks Picks, Prediction & Odds
Read analysis
[01] Latest
All content
Lay Betting Explained: How to Bet Against an Outcome on a Sports Exchange
May 9, 2026 · 9 min read
What Is a Betting Exchange? A Complete Guide for Bettors
May 7, 2026 · 11 min read
Betting Exchange vs. Sportsbook: Which Is Better for Sharp and Recreational Bettors?
May 5, 2026 · 9 min read
Build a Sports Betting MCP Server for Claude Code
Apr 23, 2026 · 12 min read
Cleveland Cavaliers vs Toronto Raptors Picks, Prediction & Odds — Thursday, April 23, 2026
Apr 23, 2026 · 6 min read
Denver Nuggets vs Minnesota Timberwolves Picks, Prediction & Odds — Thursday, April 23, 2026
Apr 23, 2026 · 6 min read
[02] Build
Open API. No key required.
Fetch live odds, stream the order book, place orders — anything sx.bet can do, so can your code.