Documentation Index
Fetch the complete documentation index at: https://docs.predexon.com/llms.txt
Use this file to discover all available pages before exploring further.
The same prediction-market question often trades at different prices on different venues. When the spread exceeds fees, it’s an arb. Build a scanner that surfaces them and (optionally) executes both legs.
You’ll build:
- A spread scanner across pre-matched venue pairs
- A profitability filter (net of fees and round-trip cost)
- Two-leg execution via the Order Router
Endpoints used: 2 REST + 2 Trading API. Free + Dev plan (matched pairs is gated).
How matching works
Predexon maintains LLM-curated equivalence between markets across venues — same question, normalized outcome labels, similarity score. You don’t have to re-discover this from scratch.
Step 1 — Pull matched pairs
import os, requests, time
BASE = "https://api.predexon.com"
H = {"x-api-key": os.environ["PREDEXON_API_KEY"]}
def all_pairs(min_similarity=98):
return requests.get(
f"{BASE}/v2/matching-markets/pairs",
headers=H,
params={"min_similarity": min_similarity, "limit": 200},
).json()["pairs"]
min_similarity=98 filters to near-exact matches. Drop to 90 for more pairs at the cost of more false positives.
Step 2 — Quote each side
For each pair, get current YES-side prices on each venue. Polymarket gives prices 0–1, Kalshi gives cents 0–100 — normalize.
def polymarket_yes(token_id):
return float(requests.get(
f"{BASE}/v2/polymarket/market-price/{token_id}",
headers=H,
).json()["price"])
def kalshi_yes(ticker):
# Kalshi prices are in cents; divide by 100
md = requests.get(
f"{BASE}/v2/kalshi/markets",
headers=H,
params={"ticker": ticker, "limit": 1},
).json()["markets"][0]
return md["yes_bid"] / 100.0
def quote(pair):
poly_token = pair["polymarket"]["yes_token_id"]
kalshi_tkr = pair["kalshi"]["ticker"]
return {
"pair": pair,
"poly_yes": polymarket_yes(poly_token),
"kalshi_yes": kalshi_yes(kalshi_tkr),
}
Step 3 — Compute spread net of fees
The naive spread is |poly_yes - kalshi_yes|. The real number includes round-trip fees on both sides.
POLYMARKET_FEE_BPS = 0 # Polymarket itself charges 0; partner fees if you set them
KALSHI_FEE_BPS = 0 # Kalshi maker is 0; taker varies
def net_edge(q):
spread = abs(q["poly_yes"] - q["kalshi_yes"])
fee_cost = (q["poly_yes"] + q["kalshi_yes"]) * (POLYMARKET_FEE_BPS + KALSHI_FEE_BPS) / 10_000
return spread - fee_cost
If Polymarket YES is at 0.62 and Kalshi YES is at 0.58, the gross edge is 0.04 (4¢). Net of fees that’s roughly your profit per share if you buy Kalshi and sell (short) Polymarket — but shorting Polymarket means buying the NO side.
The two-leg trade:
- Buy NO on the venue trading higher (you profit if it resolves NO at $1)
- Buy YES on the venue trading lower (you profit if it resolves YES at $1)
- Total cost:
(higher_no_price + lower_yes_price) per pair
- Total payout: $1 regardless of outcome
- Profit per pair:
$1 - (higher_no_price + lower_yes_price)
def arb_profit_per_pair(q):
# higher venue: sell YES = buy NO; lower venue: buy YES
higher_yes = max(q["poly_yes"], q["kalshi_yes"])
lower_yes = min(q["poly_yes"], q["kalshi_yes"])
cost = (1 - higher_yes) + lower_yes # NO on higher + YES on lower
return 1.0 - cost # net of $1 payout at resolution
Any pair where arb_profit_per_pair > $0.02 is worth a closer look.
Step 4 — Surface (and optionally execute)
def scan():
opportunities = []
for pair in all_pairs():
try:
q = quote(pair)
profit = arb_profit_per_pair(q)
if profit > 0.02:
opportunities.append({**q, "profit_per_pair": profit})
except Exception as e:
print(f"quote failed: {e}")
return sorted(opportunities, key=lambda x: -x["profit_per_pair"])
for op in scan():
print(
f"${op['profit_per_pair']:.3f}/pair "
f"poly={op['poly_yes']:.2f} kalshi={op['kalshi_yes']:.2f} "
f"{op['pair']['polymarket']['question']}"
)
The Trading API supports Polymarket, Predict.fun, Opinion, Limitless, and Hyperliquid for execution — not Kalshi. Kalshi-leg execution requires Kalshi’s native API directly. The full Predexon-only execution path works for Polymarket ↔ Predict.fun, Polymarket ↔ Limitless, Polymarket ↔ Opinion, and any other combination of the 5 trading-supported venues.
For Predexon-supported pairs, use the Order Router — it automatically picks the cheaper venue for each leg, no venue_preference needed:
TRADE = "https://trade.predexon.com"
ACCOUNT_ID = "your-account-id"
def execute_arb_predexon_supported(pair, pairs_to_trade=10):
"""Both legs through Predexon. Router auto-picks the cheaper venue per leg."""
# buy YES — router picks the venue with the lowest YES price
requests.post(
f"{TRADE}/api/accounts/{ACCOUNT_ID}/router/orders",
headers={**H, "Content-Type": "application/json"},
json={
"predexonId": pair["predexon_id_yes"],
"side": "buy", "type": "market", "size": str(pairs_to_trade),
},
)
# buy NO — router picks the venue with the lowest NO price (i.e. highest YES on the other side)
requests.post(
f"{TRADE}/api/accounts/{ACCOUNT_ID}/router/orders",
headers={**H, "Content-Type": "application/json"},
json={
"predexonId": pair["predexon_id_no"],
"side": "buy", "type": "market", "size": str(pairs_to_trade),
},
)
For mixed Polymarket-Kalshi arbs, place the Polymarket leg via Predexon’s venue-specific Place Order, then call Kalshi’s native API for the Kalshi leg — Predexon doesn’t proxy Kalshi trading.
Operational realities
| Risk | Reality | Mitigation |
|---|
| Spread collapses between legs | The first leg fills, the second leg moves — you’re left holding one side | Place both legs in parallel; use market orders; accept some leg-risk |
| Venue restrictions | Kalshi requires KYC + US person status | Run KYC’d accounts only for Kalshi-touching strategies |
| Resolution timing differs | Polymarket and Kalshi resolve the same question on different schedules | Capital is locked until both sides resolve — model as carry cost |
| Both legs partial-fill | You end up with mismatched sizes | After both leg responses come back, place a corrective leg for the difference |
| Match is wrong | Similarity score is high but the questions actually differ | At min_similarity=98 this is rare but possible — verify high-conviction trades by hand |
For systematic arb, set min_similarity=99 and add a manual review step for any new pair before letting the system trade it.
Reference