Trade Bot Dictionary · Cross-platform

arb_anchor

arb_anchor lets a bot buy a Kalshi market only when the identical event is trading more expensively on Polymarket. Two exchanges, the same real-world question, two different prices — one of them is wrong, and this signal makes your bot wait for the cheap one. It is the closest thing in our library to an entry reason that needs no forecast at all. It is also, as you'll see below, not quite the thing its name says it is.

Bots using it
3 (2 live, 1 never traded)
Data source
Kalshi + Polymarket public APIs
Refresh
Kalshi 1 min · Poly 2 min
Markets it can see
26 of 1,276 open — a snapshot, it moves
Backtest-replayable
No — live engine only
Anchor module
quants/arb_anchor.py
How arb_anchor gets from two exchange APIs to a trade decision Two parallel chains feed one matcher. The Kalshi public API is polled every minute by kalshi_poller into the kalshi_markets table in predictive.db; the Polymarket public API is polled every two minutes by polymarket_poller into polymarket_markets. quants/arb.py pairs rows from the two tables by rule — Fed decisions matched on meeting month plus outcome bucket, MLB games matched on canonical team names plus start time — and builds a ticker index of 26 matched markets, cached for 120 seconds. The arb_anchor gate then requires the cross-platform gap to clear min_gap_cents and, by default, that Kalshi be the cheaper place to buy YES. Any market with no confirmed pair fails closed and is skipped, which is 1,250 of the 1,276 open Kalshi markets. Nothing flows back out to Polymarket: the bot reads that book but never trades on it. SOURCE POLLER · CRON TABLE IN predictive.db MATCHER GATE DECISION BOOKONE BOOKTWO Kalshi API yes_ask · yes_bid close_time · volume 1,276 markets open right now — and the book the bot fills on kalshi_poller * * * * * every minute run_poller.sh flock-guarded: a slow run skips the next kalshi_markets 514,202 rows the matcher reads only KXFEDDECISION-% KXMLBGAME-% Polymarket API yes_ask · yes_bid end_date · raw JSON the price we compare against — we never trade this book polymarket_poller */2 * * * * every 2 minutes run_poller_poly.sh slower on purpose — rate-limited API polymarket_markets 132,159 rows matched from the title and the outcome list — no ticker exists here quants/arb.py MATCHED BY RULE never by fuzzy title text Fed · meeting month + outcome bucket MLB · canonical teams + game start time ticker_index() cached 120 seconds 26 matched markets 17 Fed + 9 MLB arb_anchor gap ≥ min_gap_cents AND Kalshi is the cheaper YES FAILS CLOSED no confirmed pair, no trade. It never assumes a match. TRADE ONE leg, on Kalshi — see catch #1 SKIP 1,250 of the 1,276 open markets Both APIs are public and both pollers are real cron jobs on the production box. Row counts and the 26 matched markets were read from predictive.db on 2026-08-10 at 4:26 PM UTC. Notice what is missing: there is no arrow back out to Polymarket. The bot reads that book. It never buys on it. That one absent arrow is the whole of catch #1.
Two books in, one gate, and a great deal of skipping. The matcher in the middle is the strict part — it will only pair two markets when a rule proves they are the same bet, which is why the funnel narrows from half a million stored rows to twenty-six tradable ones.

Where the data comes from

Two exchanges, both public, both polled by us on cron into the same SQLite file. Nothing here is bought and nothing is scraped off a third party — these are the books' own market feeds.

Chain 1 — Kalshi, every minute

predictive/kalshi_poller.py runs from the wrapper predictive/run_poller.sh on cron * * * * * — once a minute, guarded by flock so a slow run is skipped rather than stacked on top of itself. It writes kalshi_markets in predictive.db: ticker, yes_ask, yes_bid, volume, close_time. That table held 514,202 rows when this page was written, of which 1,276 were markets still open. Kalshi is the book your bot actually fills on.

Chain 2 — Polymarket, every two minutes

predictive/polymarket_poller.py runs from predictive/run_poller_poly.sh on */2 * * * *, also flock-guarded. Its wrapper explains the slower cadence in one line: Polymarket data moves more slowly than Kalshi brackets, and the API is rate-limited. It writes polymarket_markets132,159 rows — with the title, prices, end_date and the raw JSON, which is where the outcome list and the event slug live. Polymarket is the book we price-check against. We never trade it.

The matcher — quants/arb.py

This is the part that matters, and it is stricter than you'd expect. Two markets are paired only when a rule proves they are the same bet. The module's docstring is blunt about why: a pair is emitted only when both legs are found by rule, never by fuzzy title-matching — because a fabricated match turns an ordinary price difference into a fake edge.

arb.ticker_index() then flattens those pairs into a lookup keyed by Kalshi ticker, holding the gap in cents, which book is cheaper, and a blended fair value. It is cached for 120 seconds. That index is the only thing quants/arb_anchor.py ever reads.

One oddity worth explaining, because you'll spot it on the index table: our signal registry lists no poller and no table for arb_anchor. That's honest rather than broken — the anchor module names neither, because it reaches the data second-hand through arb.py. The two pollers above are the real answer.

What it means for your bot

You add arb_anchor to your entry rules with a click, and it takes two options:

The practical consequence is that this signal is a brake, not an accelerator — the most restrictive thing in our library. On the day this page was written, of 1,276 open Kalshi markets, 26 had a confirmed cross-platform twin, and at the setting our own live bots use — min_gap_cents: 4 with cheaper_only on — exactly 5 markets qualified. A bot with this signal attached spends nearly all of its life doing nothing, on purpose.

That is the trade you are making. In exchange for near-total inactivity you get an entry reason that doesn't require you to predict anything. You are not saying the Fed will hold. You are saying two exchanges disagree about whether the Fed will hold, and you would like the cheaper of the two prices.

The catch

There are five, and the first one is the name.

1. It is line-shopping, not arbitrage

Real arbitrage means covering both outcomes — buy YES on the cheap book, buy NO on the expensive one, pay less than a dollar for the pair, collect a dollar whichever way the event goes. The risk is gone. That is not what this does. arb_anchor returns a plain yes-or-no, the engine then takes one position, and it takes it on Kalshi. There is no second leg and no hedge. All 182 trades our arb bots have recorded are single Kalshi fills.

So you keep the full event risk. If the Fed surprises everyone you lose — you simply lost a little less than someone who paid the Polymarket price. This is line-shopping, the thing sports bettors do when they take the better number across books, and it is a real expected-value edge. It is not free money, and a signal called arb_anchor invites you to think it is. Our arbitrage explainer makes the same point at more length.

2. The universe is two market series — and in practice it has been one

ticker_index() is built from exactly two functions: Fed decision pairs and MLB game pairs. Nothing else on either exchange has a matching rule written for it, so every other market fails closed. Crypto, weather, politics, awards — the gate cannot see them, and won't until someone writes another matcher. Twenty-six of 1,276 was the whole board at the moment we measured it; the count moves as games and Fed meetings open and close, but the two series are the hard limit.

It is narrower than that in practice. Across both live arb bots, over two months, every single trade was an MLB game — ticker prefix KXMLBGAME, 182 of 182. Not one Fed trade, even though Fed pairs are the majority of the matched index.

Three things quietly narrow it further, and none of them announce themselves:

  • The Polymarket poller ingests only the top 1,500 markets by 24-hour volume. If the Polymarket leg of a genuine pair sits below that cut, the pair never exists and your bot skips a market that really is listed on both books.
  • For MLB, a game is represented by one ticker only — the team with the bigger disagreement. The other side of the same game is not in the index at all.
  • The index is keyed by Kalshi ticker, so if your bot's platforms list includes poly, those markets can never pass this gate — there is no key for them to match against.

And consensus_anchor is built on the same matcher, so it inherits every one of these limits.

3. The backtester cannot replay it — and it will still hand you a curve

We don't store historical matched-pair gaps, so a replay has nothing to read. arb_anchor sits on the backtester's unsupported list with that exact reason attached, dated 2026-06-10.

Here is the part to be careful about. The backtest does not refuse to run. It runs without the arb gate, so every market that clears your other rules trades, and you get a P&L curve for a strategy you did not build. The result does name arb_anchor in its unsupported_rules list, so the site can tell you — but skim past that line and you are reading the backtest of a different bot entirely. Judge an arb bot on forward results only.

4. cheaper_only only ever checks the YES side — which points a NO bot the wrong way

Read the check literally: it passes when the cheapest place to buy YES is Kalshi. NO costs roughly a dollar minus YES, so if Kalshi's YES is the bargain then Kalshi's NO is the expensive one. A bot with side: "NO" and cheaper_only: true is therefore being steered into precisely the markets where Kalshi is the worse book for the trade it is about to place.

We know because we did it. Arb — Cheap NO is configured that way and has been live since June. It and its YES twin share one gate, so they fire on the same markets in the same instant on opposite sides, which makes them an unusually clean read on whether the direction check matters. Over 2026-06-11 to 2026-08-09, 88 closed trades each:

BotSideClosedWinsP&L
Arb — Cheap YESYES8847+$74.18
Arb — Cheap NONO8841−$96.46
FOMC Arb WindowYES0

Eighty-eight trades is a small sample and we are not going to dress it up as proof. But the mechanism is right there in the source, the split runs the way the mechanism predicts, and the honest summary is that the leg the gate was pointed at made money while the leg it was pointed away from lost more. If you fork a NO-side arb bot, turn cheaper_only off and think it through yourself.

The third bot deserves a line too. FOMC Arb Window stacks arb_anchor on top of eco_anchor — an arb gap and being within two hours of an FOMC or CPI release — and has never taken a single trade. Two fail-closed gates in series make for a very quiet bot.

5. The gap is a screen price, not a fill

The number the gate compares is each book's quoted YES ask, rounded to the cent. Three things it does not account for:

  • Fees. Neither platform's costs are modelled. A gap of a few cents can be worth nothing once you have actually paid to be there.
  • Depth. A 5¢ gap on one contract is not a 5¢ gap on a hundred. The gate looks at the top of the book and nothing behind it.
  • Staleness. Polymarket is polled every 2 minutes and the matched-pair index is cached for a further 120 seconds, so worst case the gap your bot acts on is around four minutes old on the Polymarket side. Small gaps close in less time than that.
  • For MLB, the Polymarket "price" isn't a quote at all. The Fed matcher compares real bids and asks from both books. The MLB matcher reads Polymarket's single outcome price out of the raw JSON and uses that same number as both the bid and the ask — so the one series our bots have actually traded is the series with the least reliable price on the far side of the comparison.

And a gap can be real for a boring reason — different resolution wording, a different settlement source, a different close time — in which case the disagreement is not an error to harvest, it is two contracts that are not quite the same bet. Guarding against that is exactly why the matcher only knows two series. Our bots trade $5 flat in simulated money, so none of this has ever cost us anything; all of it would cost real money.

Where to go next

The live version of this data is the arbitrage scanner — the same matched pairs, the same gaps, with a deep link into both books. If the idea is new, start with how prediction-market arbitrage works, or the glossary. The other profile written so far is wx_anchor, the most-used signal we have.

Every claim on this page traces to one of three things: the output of quants/signal_registry.py, the source of quants/arb_anchor.py, quants/arb.py, quants/backtest.py and the two pollers, or a read-only query against our own database with the window stated. Cron schedules were read from the production crontab. Market counts and the matched-pair index were read at 4:26 PM UTC on 2026-08-10 and will have moved since. P&L is simulated — TinyCorp bots trade paper money only. Verified 2026-08-10.