On the wire · 04:34:45 UTC Live 547 · Prematch 14,368 · Markets 56,929 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Answer · Bettors · Arbitrage

Arbitrage calculator using Pinnacle odds

Updated · PinnFeed · 2 min read

Answer: an arbitrage exists when the best prices for every outcome of a market, taken across books, have implied probabilities that sum to less than 1: 1/p1 + 1/p2 (+ 1/p3) < 1. Stake each outcome in proportion to 1/price to lock the same return. PinnFeed supplies Pinnacle's side of the comparison in real time (Builder) or via REST; the other book's prices come from your own source, since PinnFeed is Pinnacle-only.

Formula and stake split#

def arb(prices: list[float], bankroll: float = 100):
    inv = [1 / p for p in prices]
    total = sum(inv)
    if total >= 1:
        return None                         # no arbitrage
    stakes = [bankroll * i / total for i in inv]
    return {"margin": 1 - total, "stakes": [round(s, 2) for s in stakes],
            "return": round(bankroll / total, 2)}

print(arb([2.12, 2.02]))   # Pinnacle over 2.12, other book under 2.02 -> small arb

With 2.12 and 2.02 the implied sum is 0.9668, so a 100 bankroll returns about 103.44 whichever side wins, before fees and if both bets stand.

Alert loop (Pinnacle side from PinnFeed)#

import os, time, requests
KEY = os.environ["PINNFEED_KEY"]  # ps_live_...

def other_book_prices(event):        # plug in your own source here
    return {}

while True:
    r = requests.get("https://pinnfeed.io/v1/odds",
        params={"sport": "tennis", "market": "moneyline", "main": "true", "limit": 100},
        headers={"x-api-key": KEY}, timeout=10)
    for e in r.json()["events"]:
        for m in e["markets"]:
            if (m["maxStake"] or 0) < 100:
                continue
            pin = {o["name"]: o["decimal"] for o in m["outcomes"]}
            other = other_book_prices(e)          # {"home": 1.9, "away": 2.2}
            if set(pin) == set(other) and pin:
                best = [max(pin[k], other[k]) for k in pin]
                res = arb(best)
                if res:
                    print(e["home"], "v", e["away"], res)
    time.sleep(5)                                  # Hobby allows 2 req/s

Risks before you bet#

  • Prices move between the two bets; one leg can be missed.
  • Books may void palpable errors or limit arbitrage accounts.
  • Rules differ (retirements in tennis, overtime, dead heats); a "sure" arb can lose if settlement differs.
  • Compare value betting, which often scales better: arbitrage vs value betting.

Plans#

Free is 30 s delayed, too slow for arbitrage except testing. Hobby (€29/mo) is real-time REST; Builder (€99/mo) pushes Pinnacle changes over the WebSocket. 3-day trial; a card is required and it is charged after day 3 unless you cancel.

Educational content for adults (18+). Arbitrage carries execution and settlement risk; nothing here promises a profit.

Related: arbitrage betting bot.

Questions people ask#

Can I find arbitrage with PinnFeed alone?#

No. Arbitrage needs at least two books; PinnFeed provides Pinnacle only.

Does maxStake matter for arbitrage?#

Yes. It caps how much Pinnacle accepts at that price, which caps the Pinnacle leg.

Should I count fees?#

Yes. Exchange commission or payment fees can wipe out a small arbitrage margin.

The WebSocket plan: Builder, €99/mo.

Real-time Pinnacle odds pushed over one WebSocket, all 13 sports, every market, no-vig fair odds and closing lines. 3 days free, cancel in one click.

Form K-1 · Free API key

Get your free API key.

One field. No password, no card: enter your email, click the link we send, and your key is on screen.

By continuing you agree to the Terms and Privacy Policy.

One free key per email. Link valid for 20 minutes.