Files
Casey Judice a65256ca74 Add Step 1 "Prepare trade" pre-flight ticket (read-only, no execution)
New arb/prepare.py + GET /api/prepare: for one monthly arb pair it re-pulls
fresh quotes (incl. real Kalshi top-of-book ask size) and builds the exact
order pair a cross-venue hedge would need -- sides from best_side, size
capped to the thinner book, limit = ask + slippage buffer, capital required,
guaranteed worst-case, and warnings. Places NO orders and touches no order
endpoints anywhere.

UI: a "Prepare trade" button in the monthly drawer's breakdown card opens a
PREVIEW-ONLY ticket modal (max-size + slippage-cent inputs that re-price live;
a deliberately disabled Confirm button as the Step-2 placeholder). Also
reorders the expanded drawer so the breakdown/Prepare card is the wide left
column.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 22:11:09 -04:00

135 lines
5.4 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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&nbsp;Scanner</h1>
<span class="sub">Kalshi&nbsp;×&nbsp;Polymarket · crypto</span>
</div>
<div class="tabs" id="tabs">
<button data-m="monthly" class="on">Monthly&nbsp;arb</button>
<button data-m="hourly">Hourly&nbsp;<span class="spec">spec</span></button>
<button data-m="daily">IBKR&nbsp;×&nbsp;Kalshi</button>
<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&nbsp;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&nbsp;Benchmarks,
Polymarket on Binance. Different feeds &amp; 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&nbsp;arb</button>
<button data-s="NO POLY">No&nbsp;Poly</button>
<button data-s="BAD BASIS">Bad&nbsp;basis</button>
<button data-s="LOW SIZE">Low&nbsp;size</button>
</div>
<label class="chk"><input type="checkbox" id="favOnly"><span>Favorable&nbsp;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>
<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>
<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 &amp; rescan</button>
<button id="closeSettings" class="ghost">Cancel</button>
</div>
</div>
</div>
<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 &amp; Send — not enabled</button>
<span class="tk-foot-note">Step&nbsp;2 would place these with your keys, only on your click.</span>
</div>
</div>
</div>
<div id="toast" class="toast" hidden></div>
<script src="app.js"></script>
</body>
</html>