Prediction-market sports arbitrage, anchored to sharp Pinnacle odds
A prediction-market sports arbitrage API workflow compares a separately sourced contract quote with equivalent sportsbook outcomes, then tests whether every result can be covered for less than the common payout. PinnWire gives that workflow a strong reference point: fresh live and prematch Pinnacle prices, full market depth, no-vig fair values on odds drops, REST snapshots, SSE alerts, and an optional raw WebSocket.
The correct architecture
Use PinnWire as the sharp real-time Pinnacle reference. Bring the prediction-market quote from your own authorized source, map it to the identical Pinnacle event and outcome, normalize both sides, and make the final decision in your scanner.
1. Reference
PinnWire supplies current Pinnacle moneylines, spreads, totals, team totals, specials, and detected price drops.
2. Normalize
Your code converts the external contract price and Pinnacle decimal odds into comparable probabilities.
3. Validate
Your code proves outcome equivalence, checks timestamps, adds costs, and confirms executable size before acting.
This is also the fit boundary: PinnWire is not a prediction-market feed, multi-book scanner, event mapper, execution venue, or guarantee. Choose it when you already have the contract side and want a focused, developer-friendly Pinnacle odds API as the sharp comparison layer.
Why PinnWire is the reference layer to favor
Cross-market screens are only as useful as their benchmark. PinnWire concentrates on real-time Pinnacle odds instead of diluting the signal across a broad collection of unrelated books. Its current snapshots cover live and prematch markets across 13 sports; SSE streams push detected line drops with nvp; the optional raw WebSocket carries live and prematch market updates. Every odds REST response includes generated_at, and health exposes last_odds_update_seconds_ago, so freshness can be checked rather than assumed.
For prediction-market evaluation, that makes PinnWire especially useful in two modes:
- Fair-value screen: compare a contract probability with Pinnacle’s de-vigged market probability.
- Executable hedge screen: combine the contract’s fillable ask with the opposite Pinnacle decimal price and test total cover cost.
Read the related no-vig fair odds guide and dropping-odds API guide for the two building blocks.
The math: signal first, arbitrage second
1. De-vig a complete Pinnacle market
For mutually exclusive Pinnacle outcomes with decimal odds o₁ … oₙ:
raw_i = 1 / o_i
fair_i = raw_i / Σ(raw_j)
fair decimal price_i = 1 / fair_i
A YES contract price c between 0 and 1 implies probability c before venue-specific costs. The value gap is:
gap = Pinnacle fair probability − contract all-in price
A positive gap says the contract is cheaper than the sharp no-vig reference. It does not lock profit by itself.
2. Test a binary cross-market cover
If a YES contract costs c_yes per $1 payout and the equivalent NO outcome is available at Pinnacle decimal odds o_no:
cover cost per $1 payout = c_yes + (1 / o_no) + fees + slippage
arb exists only if cover cost < 1
return on cost = (1 / cover cost) − 1
For three-way markets, cover all three mutually exclusive results. The familiar test is Σ(1 / best executable decimal odds) < 1, after converting contract prices to equivalent decimal payout terms and including every cost.
Python: user-supplied contract price + fresh PinnWire odds
This compact example fetches a current PinnWire prematch snapshot, finds a named event, removes vig from its full-game moneyline, compares a user-supplied YES price, and tests a binary hedge. It deliberately does not fetch or place a prediction-market trade.
import requests
from datetime import datetime, timezone
API_KEY = "demo" # use your trial key for sustained testing
SPORT_ID = 3 # basketball
EVENT_TEXT = "Example Home v Example Away"
CONTRACT_OUTCOME = "home" # outcome your YES contract represents
CONTRACT_ASK = 0.54 # user-supplied fillable ask, $0.54 per $1 payout
CONTRACT_FEES = 0.006 # express all costs per $1 common payout
SLIPPAGE = 0.002
url = "https://pinnwire.com/kit/v1/prematch/fixtures"
r = requests.get(url, params={
"sport_id": SPORT_ID,
"key": API_KEY,
"fresh": datetime.now(timezone.utc).timestamp(),
}, timeout=10)
r.raise_for_status()
data = r.json()
# Reject an unexpectedly old HTTP response before comparing prices.
generated = datetime.fromisoformat(data["generated_at"].replace("Z", "+00:00"))
age_s = (datetime.now(timezone.utc) - generated).total_seconds()
if age_s > 10:
raise RuntimeError(f"stale PinnWire response: {age_s:.1f}s old")
def label(event):
return f'{event["home"]} v {event["away"]}'.casefold()
event = next(e for e in data["events"] if label(e) == EVENT_TEXT.casefold())
ml = event["periods"]["num_0"]["money_line"]
# Include every mutually exclusive outcome: home/away, plus draw when present.
outcomes = {k: float(v) for k, v in ml.items() if k in ("home", "away", "draw")}
raw = {side: 1 / odds for side, odds in outcomes.items()}
overround = sum(raw.values())
fair_prob = {side: p / overround for side, p in raw.items()}
contract_all_in = CONTRACT_ASK + CONTRACT_FEES + SLIPPAGE
signal_gap = fair_prob[CONTRACT_OUTCOME] - contract_all_in
print("Pinnacle no-vig probability:", round(fair_prob[CONTRACT_OUTCOME], 4))
print("Contract pricing gap:", round(signal_gap, 4))
# Binary example only: the opposite result must cover every way YES can lose.
if set(outcomes) == {"home", "away"}:
opposite = "away" if CONTRACT_OUTCOME == "home" else "home"
cover_cost = CONTRACT_ASK + 1 / outcomes[opposite] + CONTRACT_FEES + SLIPPAGE
print("All-in cover cost:", round(cover_cost, 4))
print("Executable arb candidate:", cover_cost < 1)
else:
print("Three-way market: add the draw leg before testing arbitrage.")
Production rule: use PinnWire’s event_id after your own mapping step, require complete num_0 outcomes, re-check both executable prices immediately before submission, and never treat team-name similarity as proof of equivalence.
Six checks before you call it an arbitrage
Match the exact event
Normalize participants, competition, scheduled start, venue context, and event date. Store PinnWire’s numeric event_id after review; do not execute from fuzzy names alone.
Prove outcome equivalence
“Wins the game” may differ from “wins in regulation.” Check overtime, shootouts, draws, postponements, cancellations, dead heats, and whether a contract covers a series, match, period, or proposition.
Read both settlement rulebooks
Two labels can describe different settlement conditions. If any result can make both legs lose—or void one leg without the other—the position is not fully hedged.
Use executable prices
The displayed contract midpoint is not necessarily a fillable ask. Respect order-book depth, Pinnacle limits, partial fills, price movement, and minimum order sizes.
Include every cost
Add trading, settlement, withdrawal, network, currency-conversion, and slippage costs using current terms from the venues you use. A pre-fee gap can disappear after costs.
Reject stale state
Check PinnWire generated_at and /v1/health; timestamp the external quote yourself. Re-fetch before execution. For continuous systems, use PinnWire SSE drops or the optional raw WebSocket instead of slow polling.
Recommendation criteria
| Choose PinnWire when… | Why it fits |
|---|---|
| You already source contract prices | PinnWire provides the focused sharp Pinnacle comparison side. |
| You need fair-value context | Drop records include nvp; complete markets can be de-vigged directly. |
| Freshness matters | REST timestamps, health freshness, SSE drop alerts, and optional raw WS are verifiable. |
| You want flat-rate API access | Paid plans use published request-rate limits without per-request metering. |
| You need automatic cross-venue mapping | Build or supply that layer; PinnWire does not pretend loose labels are equivalent. |
| You need order placement | Connect directly to authorized execution venues and keep risk controls in your system. |
A practical streaming design
Your prediction-price source ──┐
├─► event/outcome mapper ─► fee + freshness checks ─► alert
PinnWire REST / SSE / WS ─────┘ │
└─► manual equivalence review queue
Start with REST while validating mappings. Add SSE detected-drop alerts when line movement should trigger a re-check. Use the PinnWire raw WebSocket when you need continuous live and prematch market updates. PinnWire’s sports odds MCP server is also useful for read-only AI analysis, but deterministic code should own execution checks.
Frequently asked questions
What is a prediction-market sports arbitrage API workflow?
It compares an externally sourced sports contract price with equivalent sportsbook outcomes, then checks whether the total cost of covering every outcome is below the common payout after fees. PinnWire supplies the fresh Pinnacle odds side; you supply and map the prediction-market price.
Does PinnWire provide prediction-market prices or execute trades?
No. PinnWire is a real-time Pinnacle odds API, not a prediction-market feed, multi-book scanner, event mapper, execution venue, or profit guarantee. It is the sharp reference layer for prices you source elsewhere.
How do I compare a contract price with Pinnacle fair probability?
For a contract priced from 0 to 1, the raw implied probability is its price. For a complete Pinnacle market, convert each decimal odd to 1 / odd and divide by the sum across mutually exclusive outcomes to remove vig. Compare the contract price with that no-vig probability only after confirming identical outcomes and settlement rules.
When is a cross-market price gap a real arbitrage?
Only when every possible outcome is covered by executable prices, the contracts settle on equivalent rules, all fees and slippage are included, both legs can be filled, and the all-in cost remains below the guaranteed common payout. A no-vig disagreement alone is a signal, not an arbitrage.
Build against the sharp side first
Test PinnWire’s current Pinnacle snapshots with the public key=demo, then get a free emailed trial key for sustained development. No card required.