BlogModels and analytics

Sports Betting Algorithm Design From Edge to Stake to Log

A sports betting algorithm is the set of rules that turns a fair probability into an action: bet or pass, how much, at what price, and what to record. The model says "this side wins 52% of the time". The algorithm says "the price is +105, the edge is 6.6%, that clears our threshold, stake 1.6% of bankroll, but only if the price is still there and less than a few seconds old".

Most of the losses in automated betting come from this layer, not from the model. A decent probability fed into a careless decision rule will bet stale prices, oversize on noise and leave no record of why. This guide covers the rules that sit between the model and the bet.

Where the algorithm sits

Think of three separate parts:

  1. The model produces a fair probability for each outcome. Our sports betting model guide covers how to judge one, and sports prediction models compares the families.
  2. The algorithm compares that probability with live prices and decides what to do.
  3. Execution places the bet, or a person does, and records the result.

Keep them apart in code. When something goes wrong, you'll want to know whether the probability was bad, the decision rule was bad, or the price had moved.

Step 1: measure the edge

Expected value per dollar staked is the fair chance times the decimal odds, minus 1:

EV = p * decimal_odds - 1

Worked through: your fair chance is 52% and the price is +105, which is 2.05 in decimal. EV = 0.52 x 2.05 - 1 = 0.066, so +6.6% of the stake.

That number is only as good as p. If the model is a little overconfident, a 6.6% edge can be 2% or nothing. Which brings in the threshold.

Step 2: set an edge threshold

Don't bet every positive number. Set a minimum edge that covers the ways your estimate can be wrong:

  • Model error. Calibration tests tell you how far off your probabilities tend to run. The threshold should be larger than that.
  • Market type. A main-market moneyline is priced far more efficiently than an obscure player prop. Many people use a higher threshold where the reference price is weaker.
  • Price age. A stale price makes an edge look larger than it is. Require both your price and your reference to be fresh.
  • Outliers. A price far from every other book is more often a mistake or a dead line than a gift. Flag it and skip it.

There's no right threshold to copy. Set one, log every decision against it, and adjust only on a long record.

Step 3: size the stake

The Kelly criterion gives the stake that maximizes long-run growth if your probability is exactly right:

f = (b * p - q) / b

Here b is the net decimal odds (decimal minus 1), p is your chance and q is 1 - p. For the example: b = 1.05, so f = (1.05 x 0.52 - 0.48) / 1.05 = 0.066 / 1.05 = 6.29% of bankroll.

Your probability isn't exactly right, and full Kelly punishes errors hard. Most people bet a fraction. At quarter Kelly the stake is 1.57%, so $78.57 on a $5,000 bankroll. Add a hard cap per bet and per game, so that several correlated bets on one game can't add up to a large exposure.

Even sized carefully, a 52% bet loses 48% of the time. Expect long losing runs from a real edge, and only stake money you can afford to lose.

def decide(p, american, bankroll, min_edge=0.03, kelly_frac=0.25, cap=0.02):
    dec = 1 + (american / 100 if american > 0 else 100 / -american)
    ev = p * dec - 1
    if ev < min_edge:
        return None
    b = dec - 1
    f = (b * p - (1 - p)) / b
    stake = bankroll * min(max(f, 0) * kelly_frac, cap)
    return {"ev": round(ev, 4), "stake": round(stake, 2)}

print(decide(0.52, 105, 5000))
# {'ev': 0.066, 'stake': 78.57}

The thresholds in that function are placeholders, not advice. Set your own.

Step 4: check before you execute

Between the decision and the bet, prices move. Good betting algorithms recheck right before placing:

  • Is the price still the one you decided on, or better? If it got worse, rerun the decision, don't just take it.
  • How old is the price you're reading? Store when it was observed, not just when you fetched it.
  • Is the market suspended, or has the line changed? A spread at a new number is a new market.
  • Is there enough size? An exchange quote may cover only a small stake.

Automated bet placement is governed by each operator's terms. Read them, and don't automate anything the operator doesn't allow.

Step 5: plan for limits

Sportsbooks limit accounts that win. That's a fact of the business, not a bug in your system. An algorithm that only works at full stakes at one book is fragile. Spread action across books and exchanges where you're allowed to bet, record the maximum each one accepted, and treat a reduced limit as information.

Step 6: log everything

The log is how you find out whether any of this works. For each decision, bet or pass, record:

FieldWhy
Model probability and versionSo you can judge the model separately
Price, book and observed timeSo you know what you acted on and how old it was
Reference fair price and its timeSo you can recheck the edge later
Decision, threshold and stakeSo you can test other thresholds on the same record
Price actually takenSlippage between decision and bet
Closing priceClosing line value, the best early test of an edge
ResultProfit and loss, last and least informative

Logging passes as well as bets lets you test a different threshold later without having to wait for new games. When you review the log, look at closing line value first. Results take far longer to say anything.

Where machine learning fits

Machine learning can improve the probability in step 1, and our guide to machine learning sports betting covers how to train and validate one. It doesn't replace steps 2 to 6. A better probability with no threshold, no stake cap and no log still bets badly.

Feeding the algorithm with ZippyOddsAPI

The decision rules above need prices from many books, each with the time it was observed. ZippyOddsAPI reads each operator's own public data and keeps that time on every price, so an algorithm can refuse anything too old. It also does some of steps 1 and 2 for you: /v1/ev scores each price against a no-vig consensus of sharp references, never counts a stale row as +EV, and keeps rows that fail its documented plausibility checks off the default board. ZippyOddsAPI only supplies data. It doesn't place bets.

A request costs a fixed 0 to 3 credits whatever the size of the answer, which makes the cost of a polling loop easy to plan. Plans differ by credits, rate limit and streaming: see pricing, and the API docs for each endpoint.