Matching & confidence
How dino pairs the same game across Kalshi and Polymarket, how every pair is scored, and why an unconfirmed pair can never fire.
Prices are the easy part of cross-venue data. The hard part is knowing that two listings are the same game: the venues use different tickers, team handles, and market structures, and a wrong pairing produces an opportunity that looks real and settles wrong. dino treats matching as its own system, separate from pricing. This page explains how it works.
Matching is offline, pricing is live
Matching runs in a slow, reviewable loop, apart from the hot pricing path. The matcher sweeps both venues' catalogs — at least every 15 minutes across today's and tomorrow's games, tightening to every 3 minutes in the hour before each kickoff — normalizes what it finds, and writes confirmed pairs to a match table. The pricing loop only ever prices pairs from that table.
A market that has not been paired simply does not fire. The failure mode is a missing opportunity, never a false one.
How a pair is scored
Team and event names are normalized against a per-league registry seeded from each venue's own resolved markets. An exact registry hit keeps full confidence. A fuzzy name match is allowed to form a pair, but its confidence is capped below 0.95 until it is independently confirmed.
Every pair carries two separate scores:
entity_confidence— how sure we are both listings are the same teams and the same game.resolution_confidence— how sure we are both venues settle the outcome the same way.
The headline confidence is always the lower of the two, so a settlement mismatch can never hide behind a perfect name match. Deductions are explicit: a fuzzy name hit, a match made on start-time proximity rather than an exact date, or a resolution rule assumed from league defaults instead of read from the market's own clause.
The firing gate
These scores gate what can serve as an arb. A pair reaches the live feed only when both scores are at or above 0.95 and every outcome of the market is covered on both venues; an unconfirmed pair stays in the review band and never fires. On the v2 API the served signal is then earned per request through four checks: the pair is active with at least two independent confirmations, cross-venue settlement parity is confirmed, no high or critical severity risk stands in the pair's complete risk set, and every outcome is priced on both venues. Anything less serves as spread. The Market object page documents the field.
Incomplete coverage is a hard hold, not a deduction. A three-way soccer market with only two legs matched is never fireable no matter how confident the names are — priced as a two-way, it would silently ignore the Draw.
Candidates: the review band, streamed
Pro plans can subscribe to candidates:{sport}: the pairs the matcher has formed but not yet confirmed — a fuzzy name still below the cap, or an outcome not yet covered on both venues. A new listing typically appears here within minutes of the venue opening it. Confirmation requires independent corroboration, so candidates lead the confirmed feed while those checks complete.
Candidates are facts about pairing, not opportunities. They never fire, they arrive with both confidence scores, and no spread computed from a candidate should be treated as executable. What they give you is the earliest view of what the matcher sees — pairs still being checked.
Confirmation closes the loop
A fuzzy pair is promoted only when independent checks agree — the pairing is corroborated against both venues' own listings, metadata, and market behavior — and it never fires while they disagree. Promoted pairings are re-audited against resolved-market ground truth, and one that settles wrong is demoted. The system is built to heal a bad promotion, not to trust it forever.