Polymarket now publishes a HYPE monthly event
(what-price-will-hyperliquid-hit-in-{month}, no year suffix), so the
old strike-probe fallback was both unnecessary and mis-ranged -- its
strikes [24..52] predated HYPE roughly doubling to ~$72, so every
"above" market ($76-92) was invisible and all 8 above rows came back
NO POLY. Point HYPE at its event like every other asset and re-range
the probe fallback to the current band [52..92].
Regenerated June pairs.json: HYPE now 16/16 with a Poly leg (was 8/16),
total 110/126 (every non-ZEC row paired; ZEC stays Kalshi-only). Only
HYPE rows changed; the other 7 assets are byte-identical.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gj() now retries 5x with exponential backoff (0.5->8s) instead of 3x
flat, so a throttled burst mid-discovery no longer silently truncates a
ladder. build() reports any asset whose Kalshi ladder came back empty,
and main() aborts (exit 2) rather than overwriting pairs.json when that
happens -- a failed fetch must not be mistaken for "no markets listed."
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The monthly scanner is driven by a static curated pairs.json, so new monthly
contracts don't appear until that file is rebuilt. roll_month.py discovers both
legs live for a target month: it anchors on Kalshi's full strike ladder
(series KX{ASSET}MAX/MINMON, target expiry code) and attaches each strike's
nearest same-direction Polymarket market (events forward-parsed; HYPE probed).
Validated against May: re-discovers 100% of curated tickers, all slugs parse,
nearest-match agrees except where the curated file points at now-closed Poly
markets. Safety: backs up the prior file and refuses to overwrite when it finds
fewer than --min-pairs (so running it before the month lists can't wipe it).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Polymarket appends random hash suffixes when re-publishing markets
(e.g. will-bitcoin-dip-to-70k-in-may-2026 → ...-2026-438-356-919).
Our static pairs.json had the unsuffixed slugs from the original
spreadsheet snapshot, so every BTC 'below' pair silently fell through
to NO POLY despite both venues having liquid markets. User flagged
this looking at BTC <70k May.
- scripts/refresh_pairs.py: walks each asset's 'what-price-will-X-
hit-in-may' multi-outcome event on Gamma, rebuilds the
(asset, direction, strike) → slug map, rewrites data/pairs.json.
Probes HYPE strikes individually since there's no container event
for that asset. Has --dry-run.
- data/pairs.json: updated to current live slugs. Two BTC arbs that
were previously hidden now surface (BTC <65k and <70k both ARB).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Earlier ibkr.py assumed each strike = one conid (buy=Yes / sell=No). The
IBKR ForecastEx CP API actually exposes each strike as TWO separate conids
(YES = right=CALL, NO = right=PUT). Local symbol shape is
CFBTC_MMDDYYHH_<strike>_<YES|NO> e.g. CFBTC_05242616_71000_YES means
4pm ET May 24 2026, $71,000 YES side.
- data/ibkr_contracts.example.json: per entry now has yes_conid + no_conid
+ strike + close_iso + label (one row per strike).
- arb/daily.py: snapshots both conids in one batch; yes_ask/no_ask come
directly from each side's ask (no more 1-bid derivation).
- scripts/discover_ibkr.py: walks /iserver/secdef/strikes + secdef/info to
print the full YES/NO ladder for a given underlying + month + maturity.
Filters by maturityDate so it doesn't mix expiries.
End-to-end verified: 3 sample strikes pair cleanly to KXBTCD-26MAY2416
(IBKR $71K -> Kalshi T70999.99 etc.). Live prices still pending the
user's ForecastEx market-data subscription / strikes closer to spot.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a phone-friendly public dashboard served from docs/ via GitHub Pages:
- docs/index.html, app.js, style.css: monthly + hourly tabs only, no
positions/settings (positions stay on the local Mac UI). Reads
./data/scan.json and ./data/hourly.json instead of /api endpoints.
- gha_alerts.py also writes both snapshots to docs/data/ each cron tick.
- Workflow now commits docs/data/ alongside alerts_state.json.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Monthly + hourly Kalshi/Polymarket arb scanner (stdlib-only Python).
- Live positions tab w/ realized P&L history.
- GitHub Actions cron workflow texts SMS via Apps Script webhook on
newly-detected arbs. State persisted in alerts_state.json.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>