Method
How two venues that publish very differently become one feed, how equivalent markets are matched, and — the part that matters more — where the method stops being reliable.
01Neither venue's obvious endpoint is the right one
Kalshi. The natural choice is /markets?status=open. It is almost entirely unusable. The endpoint is saturated with multivariate combination markets — tickers containing MVE — that carry no liquidity. Scanning 25,000 of them across 25 pages returned 24,977 combination markets and exactly 2 tradeable ones, both with a lifetime volume of 1.
Paging deeper does not rescue it, because the junk is not front-loaded — it is very nearly the whole collection. The real catalogue lives behind /events?with_nested_markets=true, which returns roughly 9,000 tradeable markets in under four seconds. That is the feed.
Polymarket. The Gamma API silently caps a page at 100 rows whatever limit says — asking for 500 returns 100 — so breadth comes from offset paging. Ordering matters too: sorting by liquidityNum looks correct and is not, because it surfaces deep-but-dead longshots. One 2028 nomination market showed $2.9M of nominal liquidity resting at 0.0015 against $587 of turnover that day. 24-hour volume is the honest proxy for a market anyone is actually trading.
02One schema, two very different shapes
Kalshi models a multi-outcome event as one event and many markets, so the question and the outcome arrive on separate objects: the event is 2028 Democratic presidential nominee and the market is Gavin Newsom. Polymarket puts both in one string: Will Gavin Newsom win the 2028 Democratic presidential nomination? Titles have to be composed before anything can be compared.
Probability is the mid of the quoted spread, never the last traded price. Kalshi frequently returns 0.0000 for last price, and on a thin market a last trade can be days stale while the book is current.
Categories are inferred from keywords on both sides rather than taken from either venue's own taxonomy. This is deliberate: the venues use different vocabularies — Kalshi says “Elections” where the inferred label is “Politics” — and since category agreement is scored by equality, mixing vocabularies penalises correct matches.
03Matching
Four signals, weighted into one composite. Each catches a failure mode of the others.
| Entity overlap — amounts, percentages, basis points, proper nouns | 42% |
| Title similarity — Jaccard over stopword-filtered tokens | 32% |
| Close-date proximity — decays to zero past 60 days | 16% |
| Category agreement — inferred, so a light prior only | 10% |
Composite ≥ 0.8 is HIGH; 0.4–0.8 is MEDIUM; below 0.4 a pair is discarded before it is ever shown. Current scorer: composite-v0.2.
Entities carry the most weight because two titles can read as near-paraphrases and still describe different markets — “Fed cuts rates” is true of a September decision and a December one. The extractor pulls the load-bearing facts a paraphrase cannot drop.
Two gates sit on top of the composite. A pair must share a substantive entity or show real wording overlap: agreeing only on a year is how “Will Jesus Christ return before 2027?” came to pair with a Greenland market resolving in the same year. And proper-noun runs have sentence-position capitals stripped, because otherwise “Will Tottenham win…” yields the entity will tottenham, which fails to match a bare Tottenham on the other venue and silently costs a correct pair its entity score.
Assignment is global-best-first rather than walking one catalogue in order: every qualifying candidate is scored, sorted, then assigned so each market is used once. Walking in order lets an early weak market consume the counterparty a later, stronger one needed, and makes the output depend on input ordering.
04The confirm/reject log
Every proposed match carries its score breakdown and a confirm/reject control. Those verdicts are written to a database, not held in the page — and the score is captured as it stood at the moment of judgement, along with the scorer version.
That detail is the whole point. Scores drift with the titles and close dates they derive from, so a label without its contemporaneous features is not a training example. What the log accumulates is not data — prices and trades can be re-fetched from either venue — but judgement: a person deciding that two contracts describe the same real-world event. That is the one thing here a competitor cannot reconstruct.
05What this does not do
It does not verify that two markets resolve the same way
Nothing checks the resolution source or the trigger condition. Two contracts can be worded near-identically and still settle opposite ways on a technicality. This is a wording heuristic, not oracle-gated identity resolution, and it is the gap the verdict log exists to close.
Entity extraction is regex, not NER
It works on capitalisation and number patterns, so it misses lowercase names and misfires on generic capitalised phrases. Every match shows its shared entities precisely so the failure is visible rather than buried in a score.
Coverage is the top of each catalogue, not all of it
The top 250 markets by volume per venue. A pair that exists further down is invisible here, and neither venue's full catalogue is small.
A spread is not an opportunity
Two venues disagreeing is a reason to look, not a trade. Whether a gap survives fees, slippage at size, settlement basis and the capital locked until resolution is a separate question this panel does not answer.
There is no fallback data
If a venue cannot be reached it is reported as unavailable and its markets are absent. There is deliberately no bundled snapshot: a stale number is recoverable, a wrong one presented as current is not.
Kramma is analytics. It does not route orders, hold funds, or take the other side of anything it displays. Back to the terminal →