mirror of
https://github.com/cjudice-commits/prediction-market-arb.git
synced 2026-07-27 21:47:46 +00:00
Map XRP/ETH below-pairs + harden Kalshi quote handling
Populate the XRP and ETH MINMON (below) pairs that were silently NO PAIR: they were never mapped to Polymarket's "dip to" markets (and two ETH pairs pointed at the now-resolved dip-to-2000). Map each to the nearest live Poly strike; leave strikes with no comparable Poly market (ETH $250, below Poly's $400 floor) as NO PAIR. Also fix two correctness gaps surfaced while investigating: - Settled/finalized Kalshi markets returned stale 1.0/0.0 sentinel prices and were scored as live; now reported as NO KALSHI (timely as month-end monthly contracts settle). - A Kalshi ask of exactly $1.00 means "no resting offer" (real asks are 1-99c); it was treated as a tradeable quote, producing phantom BAD BASIS rows at -100%. Drop it at the source so no fake leg enters the arb math. - When both venues are live but no opposite-side hedge can be built, report NO ARB instead of the misleading NO DATA. No real arb affected (a $1.00 leg can never be profitable); ARB count holds at 3 and phantom BAD BASIS rows dropped 16 to 9. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -22,6 +22,12 @@ all resolution regions and key off the guaranteed worst case, NOT (1 - cost).
|
||||
"""
|
||||
from datetime import date
|
||||
|
||||
# Kalshi market statuses that mean the market is no longer live/tradeable.
|
||||
# A settled/finalized market still returns prices, but they are stale 1.0/0.0
|
||||
# sentinels (e.g. a month-end MINMON whose strike already resolved) and must
|
||||
# not be treated as a real quote.
|
||||
_DEAD_KALSHI = {"finalized", "settled", "closed", "determined"}
|
||||
|
||||
|
||||
def direction(ticker):
|
||||
return "Below" if "MINMON" in (ticker or "").upper() else "Above"
|
||||
@@ -125,7 +131,8 @@ def evaluate(pair, kq, pq, settings, today=None):
|
||||
if not pq:
|
||||
row["status"] = "NO POLY"
|
||||
return row
|
||||
if not kq:
|
||||
if not kq or (kq.get("status") in _DEAD_KALSHI):
|
||||
# Settled/finalized Kalshi leg: prices are stale sentinels, not a quote.
|
||||
row["status"] = "NO KALSHI"
|
||||
return row
|
||||
|
||||
@@ -149,7 +156,10 @@ def evaluate(pair, kq, pq, settings, today=None):
|
||||
cands.append(("NO+YES", "NO", kq["no_ask"], k_nsz,
|
||||
"YES", pq["yes_ask"], pq.get("yes_ask_size")))
|
||||
if not cands:
|
||||
row["status"] = "NO DATA"
|
||||
# Both venues returned live quotes, but the opposite-side asks needed to
|
||||
# build a hedge aren't both offered (e.g. deep-OTM 'below' markets where
|
||||
# each venue only quotes the cheap YES side). There's data, just no arb.
|
||||
row["status"] = "NO ARB"
|
||||
return row
|
||||
|
||||
best = None # (worst_pnl, ...)
|
||||
|
||||
+10
-2
@@ -31,6 +31,14 @@ def _f(v):
|
||||
return None
|
||||
|
||||
|
||||
def _ask(v):
|
||||
"""Like _f, but also drops the '1.0000' sentinel Kalshi returns when there
|
||||
is no resting offer on that side. Real Kalshi asks are 1-99c, so a $1.00
|
||||
ask is never executable and must not be treated as a tradeable quote."""
|
||||
x = _f(v)
|
||||
return x if (x is not None and x < 1.0) else None
|
||||
|
||||
|
||||
def parse_expiry(ticker):
|
||||
"""KX...-26MAY31-7000 -> '2026-05-31', else None."""
|
||||
m = re.search(r"-(\d{2})([A-Z]{3})(\d{2})-", ticker)
|
||||
@@ -49,9 +57,9 @@ def _quote(mkt):
|
||||
return {
|
||||
"ticker": t,
|
||||
"yes_bid": _f(mkt.get("yes_bid_dollars")),
|
||||
"yes_ask": _f(mkt.get("yes_ask_dollars")),
|
||||
"yes_ask": _ask(mkt.get("yes_ask_dollars")),
|
||||
"no_bid": _f(mkt.get("no_bid_dollars")),
|
||||
"no_ask": _f(mkt.get("no_ask_dollars")),
|
||||
"no_ask": _ask(mkt.get("no_ask_dollars")),
|
||||
"yes_ask_size": None,
|
||||
"no_ask_size": None,
|
||||
"open_interest": _f(mkt.get("open_interest_fp")) or 0.0,
|
||||
|
||||
+28
-28
@@ -316,16 +316,16 @@
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-75000",
|
||||
"kalshi_strike": 750.0,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-ethereum-dip-to-800-in-may-2026",
|
||||
"poly_strike": 800.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-50000",
|
||||
"kalshi_strike": 500.0,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-ethereum-dip-to-400-in-may-2026",
|
||||
"poly_strike": 400.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
@@ -340,32 +340,32 @@
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-200000",
|
||||
"kalshi_strike": 2000.0,
|
||||
"poly_slug": "will-ethereum-dip-to-2000-in-may-2026",
|
||||
"poly_strike": 2000.0
|
||||
"poly_slug": "will-ethereum-dip-to-1800-in-may-2026",
|
||||
"poly_strike": 1800.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-175000",
|
||||
"kalshi_strike": 1750.0,
|
||||
"poly_slug": "will-ethereum-dip-to-2000-in-may-2026",
|
||||
"poly_strike": 2000.0
|
||||
"poly_slug": "will-ethereum-dip-to-1800-in-may-2026",
|
||||
"poly_strike": 1800.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-150000",
|
||||
"kalshi_strike": 1500.0,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-ethereum-dip-to-1400-in-may-2026",
|
||||
"poly_strike": 1400.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "ETH",
|
||||
"kalshi_ticker": "KXETHMINMON-ETH-26MAY31-125000",
|
||||
"kalshi_strike": 1250.0,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-ethereum-dip-to-1200-in-may-2026",
|
||||
"poly_strike": 1200.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
@@ -564,64 +564,64 @@
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-130",
|
||||
"kalshi_strike": 1.3,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-1pt2-in-may-2026",
|
||||
"poly_strike": 1.2
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-120",
|
||||
"kalshi_strike": 1.2,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-1pt2-in-may-2026",
|
||||
"poly_strike": 1.2
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-110",
|
||||
"kalshi_strike": 1.1,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-1-in-may-2026",
|
||||
"poly_strike": 1.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-100",
|
||||
"kalshi_strike": 1.0,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-1-in-may-2026",
|
||||
"poly_strike": 1.0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-090",
|
||||
"kalshi_strike": 0.9,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-0pt8-in-may-2026",
|
||||
"poly_strike": 0.8
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-080",
|
||||
"kalshi_strike": 0.8,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-0pt8-in-may-2026",
|
||||
"poly_strike": 0.8
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-070",
|
||||
"kalshi_strike": 0.7,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-0pt6-in-may-2026",
|
||||
"poly_strike": 0.6
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"asset": "XRP",
|
||||
"kalshi_ticker": "KXXRPMINMON-XRP-26MAY31-060",
|
||||
"kalshi_strike": 0.6,
|
||||
"poly_slug": null,
|
||||
"poly_strike": null
|
||||
"poly_slug": "will-xrp-dip-to-0pt6-in-may-2026",
|
||||
"poly_strike": 0.6
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
|
||||
Reference in New Issue
Block a user