How Tellmarket actually detects an arb
The walkthrough is for engineers and skeptics who want to know exactly what the scanner is doing before paying $29 to receive alerts from it. No proprietary secret sauce — just the pipeline as it runs in production.
Step 1 · Ingest both order books every 30 seconds
A long-running Python service on Fly.io polls Kalshi’s and Polymarket’s public APIs on a 30-second loop. For each market it pulls the top-of-book bid/ask on both sides (YES + NO) plus the depth tier — what size is fillable at the top quote, and what the next tick down looks like. Snapshots write to Postgres with a captured-at timestamp.
Snapshots older than 7 days get pruned by a daily pg_cron job. The live arb evaluator only looks at the most recent snapshot per market.
Step 2 · Match markets across the two exchanges
Kalshi calls the same event one thing — “Will Bitcoin reach $200,000 by end of 2026?” — and Polymarket calls it another — “Bitcoin >$200K by EOY 2026.” To arb them we have to know they’re the same market.
We compute an embedding for each market’s title + rules text via OpenAI’s embedding model, store it in a pgvector column, and look for candidate pairs where the cosine similarity is ≥ 0.92. That shortlist becomes the queue for the next step. We do not auto-trust embedding similarity — it’s a recall pre-filter, not the matching decision.
Step 3 · Two-model LLM verification
Each candidate pair gets sent to two separate LLMs — gpt-4o-mini as the primary, gpt-5-mini as the second pass — with the two market titles, full rules text, and resolution criteria. Each model returns one of: same / different / unclear plus a one-line reason.
Nothing auto-activates. Both verdicts get logged, but the pair stays in pending_review status until a human (currently the founder) approves or rejects it from the admin queue. This is deliberately conservative — false positives in matching produce phantom arbs, which destroy user trust faster than a missed real arb.
Step 4 · Compute the post-fee edge
For every active matched pair, on every 30-second snapshot, the scanner runs the cost-per-guaranteed-$1 calculation:
(or the other side polarity — whichever is cheaper)
edge = $1.00 − cost
Both costs are all-in — Kalshi’s per-trade fee, Polymarket’s 2% on net winnings, and Polymarket’s flat per-fill gas. We also pessimistically assume the Polymarket side wins (the case that maximizes Polymarket fees), so the edge we show is the guaranteed worst case. The full math: /learn/the-math.
Step 5 · Data-truth gates — what we refuse to surface
A computed edge isn’t enough. Each candidate has to clear three gates before it shows up in /arbs or fires an alert:
- Wide-book gate. If either side’s bid-ask spread exceeds 10¢, the quote isn’t a real fillable price — skipped.
- Capturable-size gate. If less than $50 is fillable at the quoted prices, the edge is real on paper but not worth a trade — skipped.
- Plausibility gate. Any edge above 15% almost always means one side’s book is stale or mis-parsed (it’s the canonical signature of a broken Polymarket mid-point quote). Bucketed out of the live feed pending a re-scan.
Step 6 · Fire the alert
A surviving candidate becomes a row in the opportunitiestable. From there: every Pro subscriber whose alert threshold is ≤ this arb’s net edge gets a transactional email via Resend (and a Slack/Discord webhook ping if they’ve configured one). Free-tier subscribers see the same arbs at a 5-minute delay on /arbs, plus the daily-digest email.
What we don’t do
- Trade for you. The scanner is strictly read-only. Every order is placed manually by the user on the exchange.
- Hold your funds. We never touch your Kalshi or Polymarket balance. There’s no integration that needs your API key.
- Predict whether the arb will fill before you click.Most arbs die in seconds to minutes — that’s a real risk and we don’t hide it.
- Auto-approve LLM matches. Every active pair was a human decision.