2026-05-20 19:48:50 -04:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title > Arb Scanner · Kalshi × Polymarket</ title >
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
< link href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;600&display=swap" rel = "stylesheet" >
< link rel = "stylesheet" href = "style.css" >
</ head >
< body >
< div class = "aurora" ></ div >
< header >
< div class = "brand" >
< div class = "logo" > ◆</ div >
< div >
< h1 > Arb Scanner</ h1 >
< span class = "sub" > Kalshi × Polymarket · crypto</ span >
</ div >
< div class = "tabs" id = "tabs" >
< button data-m = "monthly" class = "on" > Monthly arb</ button >
< button data-m = "hourly" > Hourly < span class = "spec" > spec</ span ></ button >
2026-05-23 16:16:52 -04:00
< button data-m = "daily" > IBKR × Kalshi</ button >
2026-05-20 19:48:50 -04:00
< button data-m = "positions" > Positions</ button >
</ div >
</ div >
< div class = "actions" >
< div class = "live" >< span class = "dot" ></ span >< span id = "meta" > connecting…</ span ></ div >
< label class = "auto" >< input type = "checkbox" id = "auto" >< span > Auto 8s</ span ></ label >
< button id = "settingsBtn" class = "ghost" > Settings</ button >
< button id = "refresh" class = "primary" >< span class = "bspin" ></ span >< span class = "blabel" > Refresh</ span ></ button >
</ div >
</ header >
< div id = "hbanner" class = "hbanner" hidden >
< div class = "hb-ico" > ⚠</ div >
< div class = "hb-txt" >
< b > Speculative — not a locked arb.</ b > Kalshi settles on CF Benchmarks,
Polymarket on Binance. Different feeds & the discrete-strike gap mean
legs can both lose. Live Binance↔CF divergence:
< span id = "hbDiv" class = "hb-div" > —</ span >
</ div >
</ div >
< section id = "summary" class = "summary" ></ section >
< section id = "hero" class = "hero" hidden >
< div class = "hero-head" >< h2 > Top opportunities</ h2 >< span class = "muted" > positive guaranteed return, ranked</ span ></ div >
< div id = "heroCards" class = "hero-cards" ></ div >
</ section >
< section class = "toolbar" >
< div class = "search" >
< svg viewBox = "0 0 24 24" width = "15" height = "15" >< path d = "M21 21l-4.3-4.3M11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z" fill = "none" stroke = "currentColor" stroke-width = "2" stroke-linecap = "round" /></ svg >
< input type = "search" id = "search" placeholder = "Search asset, ticker, market…" >
</ div >
< div id = "assetChips" class = "chips" ></ div >
< div class = "seg" id = "statusSeg" >
< button data-s = "" class = "on" > All</ button >
< button data-s = "ARB" > Arb</ button >
< button data-s = "NO ARB" > No arb</ button >
2026-05-29 17:06:42 -04:00
< button data-s = "NO POLY" > No Poly</ button >
2026-05-20 19:48:50 -04:00
< button data-s = "BAD BASIS" > Bad basis</ button >
< button data-s = "LOW SIZE" > Low size</ button >
</ div >
< label class = "chk" >< input type = "checkbox" id = "favOnly" >< span > Favorable basis</ span ></ label >
< span class = "count" id = "rowCount" ></ span >
</ section >
< main >
< table id = "grid" >
< thead >< tr id = "head" ></ tr ></ thead >
< tbody id = "body" ></ tbody >
</ table >
< div id = "empty" class = "empty" hidden >
< div class = "empty-art" > ◇</ div >
< p > No markets match the current filters.</ p >
</ div >
</ main >
< section id = "positions" class = "positions" hidden ></ section >
2026-05-23 16:16:52 -04:00
< div id = "ibkrSetup" class = "hbanner" hidden >
< div class = "hb-ico" > ⚙︎</ div >
< div class = "hb-txt" >
< b id = "ibkrSetupTitle" > IBKR not connected.</ b >
< span id = "ibkrSetupMsg" ></ span >
< span class = "muted" > Local-only feature — needs the IBKR Client Portal Gateway running on this Mac and a populated < code > data/ibkr_contracts.json</ code > .</ span >
</ div >
</ div >
2026-05-20 19:48:50 -04:00
< div id = "settingsModal" class = "modal" hidden >
< div class = "card" >
< h2 > Scanner settings</ h2 >
< p class = "hint" > Mirrors the workbook < em > Settings</ em > sheet · persisted server-side.</ p >
< label > Kalshi fee rate < input type = "number" step = "0.001" id = "s_kfee" ></ label >
< label > Polymarket fee rate < input type = "number" step = "0.001" id = "s_pfee" ></ label >
< label > Min net return < input type = "number" step = "0.0001" id = "s_minret" ></ label >
< label > Min Poly volume ($) < input type = "number" step = "100" id = "s_minvol" ></ label >
< label > Min contracts < input type = "number" step = "1" id = "s_minct" ></ label >
< div class = "row" >
< button id = "saveSettings" class = "primary" > Save & rescan</ button >
< button id = "closeSettings" class = "ghost" > Cancel</ button >
</ div >
</ div >
</ div >
2026-06-02 22:10:57 -04:00
< div id = "ticketModal" class = "modal" hidden >
< div class = "card tk-card" >
< div class = "tk-top" >
< h2 > Prepare trade < span class = "tk-badge" > PREVIEW ONLY</ span ></ h2 >
< button id = "tkClose" class = "ghost tk-x" aria-label = "Close" > ✕</ button >
</ div >
< div class = "tk-banner" > Nothing is sent to Kalshi or Polymarket. This is exactly
what the two orders < b > would</ b > be — for review only.</ div >
< div id = "ticketBody" ></ div >
< div class = "tk-controls" >
< label > Max size < input type = "number" id = "tkMax" min = "1" step = "1" placeholder = "all" ></ label >
< label > Slippage buffer (¢) < input type = "number" id = "tkBuf" min = "0" step = "1" value = "1" ></ label >
</ div >
< div class = "tk-foot" >
< button class = "primary tk-confirm" disabled
title = "Execution is not wired — this is Step 1 (preview only)" > Confirm & Send — not enabled</ button >
< span class = "tk-foot-note" > Step 2 would place these with your keys, only on your click.</ span >
</ div >
</ div >
</ div >
2026-05-20 19:48:50 -04:00
< div id = "toast" class = "toast" hidden ></ div >
< script src = "app.js" ></ script >
</ body >
</ html >