Assymetrix Launches the Deepest Independent Prediction Market Data APIs
Read more
Read more
Assymetrix Launches the Deepest Independent Prediction Market Data APIs
Read more
Read more
How to Detect Price Divergence Between Polymarket and Kalshi
How to Detect Price Divergence Between Polymarket and Kalshi
How to Detect Price Divergence Between Polymarket and Kalshi
Learn how to detect price divergence between Polymarket and Kalshi with our detailed pipeline. Maximize your trading accuracy and profits!

How to Detect Price Divergence Between Polymarket and Kalshi
TL;DR:
Detecting genuine mispricings between Polymarket and Kalshi requires matching markets using a stable canonical ID and applying a symmetric KL-divergence threshold above 0.15. Confirm liquidity depth and identical resolution text before executing trades, ensuring the net-of-fee edge exceeds 1.5 percent for viability. Building such a pipeline benefits from Assymetrix’s pre-normalized data API, which simplifies market pairing and facilitates robust backtesting and live detection.
A reproducible detection pipeline combines normalized cross-venue market matching, spread and z-score screening, and a symmetric KL-divergence above 0.15 threshold to flag likely genuine mispricings between Polymarket and Kalshi. When a signal fires, run three immediate checks before sizing any position:
Confirm resolution text is word-for-word identical across both venues
Verify order-book depth at your target notional on both sides
Compute net-of-fee edge; if it clears 1.5% after combined taker costs, flag for execution
If any check fails, log the signal for analysis and move on. The rest of this guide shows you how to build every layer of that pipeline.
Table of Contents
How to detect price divergence between Polymarket and Kalshi: the core metrics
How do you match the same market across both venues?
What data infrastructure does real-time divergence detection require?
How do you execute both legs without creating directional exposure?
Worked example: a Fed decision market divergence
How should you backtest and calibrate detection thresholds?
How does the Assymetrix Data API accelerate this pipeline?
Why do resolution-rule differences create false arbitrage signals?
What does a live divergence monitoring system look like?
Key Takeaways
The trade-off most detection systems get wrong
Assymetrix gives you the canonical layer this pipeline needs
Useful sources and further reading
How to detect price divergence between Polymarket and Kalshi: the core metrics
Spread, z-score, and symmetric KL divergence

Raw spread, the absolute difference between Polymarket’s yes-price and Kalshi’s yes-price on a matched market, is the fastest first filter. It is also the most misleading in isolation. A 3-cent spread at 50¢ probability is structurally different from a 3-cent spread at 92¢, because the information content and reversion dynamics differ across probability space.
The z-score corrects for that. Compute it as the current spread divided by the rolling standard deviation of that paired spread over a lookback window (typically 500–2,000 ticks per market category). A z-score above 2.0 on a stable pair warrants attention; above 3.0 it is worth escalating to the distributional check.
Symmetric KL divergence is the definitive filter. For binary markets where each venue quotes a yes-probability p and q:
KL_sym = 0.5 * (p * log(p/q) + (1-p) * log((1-p)/(1-q))) + 0.5 * (q * log(q/p) + (1-q) * log((1-q)/(1-p)))
KL_sym = 0.5 * (p * log(p/q) + (1-p) * log((1-p)/(1-q))) + 0.5 * (q * log(q/p) + (1-q) * log((1-q)/(1-p)))
A symmetric KL above 0.15 signals a probability distribution inconsistency large enough to be a genuine mispricing rather than transient noise. Below that threshold, the gap is more likely bid-ask friction or a momentary liquidity imbalance.
A preliminary gross-arb screen runs in parallel: if the cheapest Yes across both venues plus the cheapest No across both venues sums to less than $1.00, a gross arbitrage exists before fees. That check is O(1) and should run on every tick.
Pro Tip: Calibrate KL and z-score thresholds separately by market category. Political markets tend to have higher baseline volatility and wider natural spreads than economic indicator markets (CPI, FOMC). A threshold that generates clean signals on Fed-decision markets will produce excessive false positives on election markets in the final 48 hours before resolution.
How do you match the same market across both venues?
Canonical normalization and fuzzy-matching pipeline
Polymarket and Kalshi use incompatible internal identifiers, different title conventions, and sometimes different outcome sets for what is functionally the same event. The normalization pipeline is where most detection systems fail.

The canonical schema fields to align are: normalized title, event date, resolution source, outcome set (yes/no vs. binary contract), and category tag. Strip punctuation, lowercase everything, remove stopwords, and apply named-entity alignment to catch “Fed” vs. “Federal Reserve” or “FOMC” vs. “Federal Open Market Committee.”
Pipeline stage | Operation | Output |
|---|---|---|
Ingestion | Pull raw title, slug, resolution text, tags | Raw record per venue |
Canonicalization | Lowercase, tokenize, strip stopwords, NER alignment | Normalized token set |
Candidate scoring | Jaccard similarity on token sets + resolution-source match | Score 0–1 per pair |
Human-overwatch gate | Flag pairs scoring 0.70–0.85 for manual review | Confirmed or rejected |
Confirmed pair | Assign canonical_market_id, store venue_ids | Canonical paired record |
Pairs scoring above 0.85 on both token similarity and resolution-source match can be auto-confirmed. Anything below 0.70 is discarded. The middle band requires a human review queue, which shrinks quickly once you build a confirmed-pair library.
A rule-based resolution-source check is non-negotiable. Two markets with identical titles but different resolution sources (BLS advance release vs. BLS revised release, for example) are not the same market. That check alone eliminates the most dangerous false arbs.
What data infrastructure does real-time divergence detection require?
Feed requirements, canonical schema, and latency budgets
Infrastructure layer | Requirement | Notes |
|---|---|---|
Feed type | WebSocket or streaming REST | Polling introduces detection lag |
Timestamp accuracy | UTC, millisecond precision | Required for cross-venue tick alignment |
Sequence IDs | Per-venue event sequence | Detects dropped messages |
Canonical schema fields | market_id, canonical_title, venue_ids, yes_price, no_price, best_depth, timestamp_utc | Minimum viable schema |
End-to-end detection latency | — | Budget per layer |
Time synchronization matters more than most teams expect. Clock skew between your Polymarket feed handler and your Kalshi feed handler will produce phantom divergences that disappear on closer inspection. Run NTP or PTP synchronization on all feed-ingestion nodes and log the measured skew in your provenance records.
For depth reconstruction, prefer normalized tick-level snapshots supplemented by periodic full book-dumps. Top-of-book prices are insufficient for sizing; you need the incremental slippage curve at each notional bucket to know whether your target size is actually fillable at the quoted price.
Pro Tip: Store raw ticks with full provenance metadata (venue, sequence ID, ingest timestamp, processing timestamp) even if you only query aggregated snapshots in production. That audit trail is what makes backtests reproducible and lets you diagnose whether a missed signal was a feed gap or a genuine absence of divergence.
How do you execute both legs without creating directional exposure?
Leg risk, fee-aware edge, and execution controls
Leg risk is the primary operational hazard: one leg fills, the other fails due to latency, rate limits, or thin liquidity, and you hold an unintended directional position. Professional systems minimize this by firing both legs as close together as possible, with a kill switch that cancels the unfilled leg and closes the filled leg if the second order does not confirm within a defined timeout (typically 500 ms to 2 s depending on venue API rate limits).
Net-of-fee edge is the number that actually matters. Combined taker fees on common Kalshi-Polymarket pairs often consume 3–5 cents per contract round-trip. A gross edge under roughly 4–5 cents is marginal at best after fees and slippage; practitioners treat sub-4-cent gross edges as non-viable. The minimum viable net edge rule of thumb is 1.5% after all costs.
Execution checklist before firing:
Confirm canonical_market_id match and identical resolution text
Reconstruct order-book slices at target notional; verify fill is achievable at posted prices
Compute net-of-fee edge; reject if below threshold
Check withdrawal and settlement constraints (Kalshi uses ACH/USD; Polymarket uses USDC on Polygon)
Verify neither venue has a pending resolution event within the next 60 minutes
Fire both legs simultaneously with retry logic and a hard kill switch on timeout
Worked example: a Fed decision market divergence
Suppose Polymarket quotes “Fed cuts rates at May FOMC” at 0.62 yes, and Kalshi quotes the same event at 0.55 yes. The gross-arb screen fires immediately: 0.55 + (1 - 0.62) = 0.93 < 1.00.
Compute symmetric KL: with p = 0.62 and q = 0.55, KL_sym ≈ 0.018. That is well below 0.15, meaning the distributional gap is within noise range despite the 7-cent headline spread. The z-score on this pair’s 30-day spread history comes back at 1.4, also below the 2.0 threshold. No signal. Log and continue.
Now shift the scenario: Polymarket at 0.72, Kalshi at 0.55. KL_sym ≈ 0.089, z-score at 2.8. Still below the KL threshold, but the z-score warrants a depth check.
Scenario | Polymarket yes | Kalshi yes | KL_sym | Z-score | Signal? |
|---|---|---|---|---|---|
Baseline | 0.62 | 0.55 | 0.018 | 1.4 | No |
Moderate gap | 0.72 | 0.55 | 0.089 | 2.8 | Depth check |
Strong gap | 0.82 | 0.55 | 0.21 | — | Yes — verify resolution |
At the strong-gap scenario, KL_sym = 0.21 exceeds 0.15. Reconstruct book slices: Polymarket shows $8,000 available at 0.82; Kalshi shows $12,000 at 0.55. For a $5,000 notional position, both sides are fillable. Net-of-fee edge after 3 cents per side: approximately 24 cents per dollar, well above the 1.5% minimum.
Pro Tip: Use fixed-fraction sizing (e.g., 2–5% of capital per signal) rather than full Kelly in early live trading. Kelly sizing is theoretically optimal but requires an accurate edge estimate, which your backtest may overstate before you have live fill data to calibrate against.
How should you backtest and calibrate detection thresholds?
Backtesting a divergence detector requires four inputs: canonical historical ticks for both venues, per-venue order-book reconstructions at the time of each signal, the fee schedule in effect during the test period, and a realistic fill model that accounts for queue position and partial fills.
Build a canonical tick database from historical exports, joining on canonical_market_id
Replay ticks chronologically; compute spread, z-score, and KL_sym at each step
At each threshold exceedance, simulate fills using reconstructed book slices
Apply fee schedules and record net PnL per signal
Split into in-sample calibration and out-of-sample validation sets; use bootstrap resampling to estimate confidence intervals on hit rate and mean return per signal
KL threshold | Approx. signal rate | Approx. true-positive rate | Mean net edge per signal |
|---|---|---|---|
> 0.10 | High | Lower (more noise) | Marginal |
> 0.15 | Moderate | Higher | Positive after fees |
> 0.20 | Low | Highest | Strong but rare |
The 0.15 threshold sits at the practical optimum for most political and economic market categories: enough signals to generate meaningful returns, with a true-positive rate that justifies automated execution. Tighten to 0.20 for fully automated systems with no human review; loosen to 0.10 only in monitored paper-trade mode.
How does the Assymetrix Data API accelerate this pipeline?
Assymetrix provides the canonical normalization layer that replaces the fuzzy-matching pipeline described above. The Data API at data.assymetrix.com returns pre-matched market pairs with a stable canonical_market_id, normalized yes/no prices from both venues, depth metadata, resolution text, and UTC timestamps in a single response. The historical coverage spans nearly one billion rows of trading activity, which is the sample size required for statistically stable threshold calibration across market categories.
Sample canonical schema returned by the API:
Field | Description |
|---|---|
canonical_market_id | Stable cross-venue identifier |
venue_map |
|
yes_price_poly | Polymarket yes mid |
yes_price_kalshi | Kalshi yes mid |
best_depth_poly | Top-of-book depth, Polymarket |
best_depth_kalshi | Top-of-book depth, Kalshi |
resolution_text | Full resolution clause (both venues) |
timestamp_utc | Millisecond-precision UTC |
A Python snippet to fetch a paired record and compute KL_sym in-stream:
import requests, math def kl_sym(p, q, eps=1e-9): p, q = max(p, eps), max(q, eps) p_, q_ = 1 - p, 1 - q return 0.5 * (p*math.log(p/q) + p_*math.log(p_/q_) + q*math.log(q/p) + q_*math.log(q_/p_)) resp = requests.get( "https://data.assymetrix.com/v1/markets/paired", headers={"Authorization": "Bearer YOUR_KEY"} ) for market in resp.json()["markets"]: kl = kl_sym(market["yes_price_poly"], market["yes_price_kalshi"]) if kl > 0.15: print(f"Signal: {market['canonical_market_id']} | KL={kl:.3f}")
import requests, math def kl_sym(p, q, eps=1e-9): p, q = max(p, eps), max(q, eps) p_, q_ = 1 - p, 1 - q return 0.5 * (p*math.log(p/q) + p_*math.log(p_/q_) + q*math.log(q/p) + q_*math.log(q_/p_)) resp = requests.get( "https://data.assymetrix.com/v1/markets/paired", headers={"Authorization": "Bearer YOUR_KEY"} ) for market in resp.json()["markets"]: kl = kl_sym(market["yes_price_poly"], market["yes_price_kalshi"]) if kl > 0.15: print(f"Signal: {market['canonical_market_id']} | KL={kl:.3f}")
Pro Tip: Use Assymetrix’s canonical_market_id as the join key in your backtest database. Fuzzy-match drift, where a pair that was correctly matched at ingestion silently diverges as market titles update, is one of the hardest bugs to catch post-hoc. A stable canonical ID eliminates it.
Why do resolution-rule differences create false arbitrage signals?
Resolution-rule mismatches are the single most common source of false arb signals. Two markets with nearly identical titles can resolve on different data releases (BLS advance vs. BLS revised), different official sources, or different cutoff times, producing asymmetric outcomes that make a convergence trade a directional bet in disguise.
The workflow is non-negotiable: always fetch and compare the full resolution text from both venues before confirming a pair. Any textual mismatch, even a difference in the cited data release vintage, disqualifies the pair from arb treatment. Flag it as a non-arb divergence and route it to a separate research queue.
Regulatory settlement mechanics also differ. Kalshi settles in USD via ACH with custodial fund holding; Polymarket settles in USDC on Polygon with non-custodial wallet control. Settlement timing differences affect capital lock-up calculations and should be factored into annualized return estimates.
This article is general information, not legal or financial advice. Confirm current regulatory requirements and fee schedules with each venue’s official documentation and a qualified professional before trading.
What does a live divergence monitoring system look like?
Dashboard KPIs and runbook steps
A production monitoring system needs four dashboard KPIs: signal rate (signals per hour by market category), true-positive rate after manual spot-checks, mean detection-to-execution latency, and fill success rate (both legs filled vs. one-leg failures).
Alert tiers:
P0 (auto-execute): KL > 0.15 + depth sufficient at target notional + resolution text confirmed identical + net edge > 1.5%
P1 (human review): KL > 0.10 + depth borderline or resolution text flagged for review
P2 (log only): Gross-arb screen fires but KL < 0.10
Runbook for a P0 alert:
Automated system fires both legs with kill-switch timeout
Post-trade reconciliation confirms both fills within 30 seconds
If one leg fails, automated cancel-and-close sequence runs; alert on-call engineer
Log signal, fill prices, slippage, and net PnL to the audit database
Flag for threshold review if fill slippage exceeded the pre-trade estimate by more than 20%
Data-quality exceptions (missing ticks, feed gaps, anomalous prices) should trigger an automatic pause on automated execution for the affected market pair until the feed is confirmed clean.
Key Takeaways
Detecting genuine price divergence between Polymarket and Kalshi requires canonical market matching, a symmetric KL-divergence threshold above 0.15, confirmed liquidity depth, and identical resolution text before any position is sized.
Point | Details |
|---|---|
Core detection rule | Canonical pair match + symmetric KL > 0.15 + depth check + identical resolution text = valid signal |
Fee-aware edge minimum | Net edge must exceed 1.5% after combined taker fees; gross edges under ~4–5 cents are typically non-viable |
Backtest sample size | Use nearly one billion rows of historical activity to calibrate KL and z-score thresholds by market category |
Resolution text is mandatory | Always compare full resolution clauses; title similarity alone produces false arb signals |
Assymetrix integration | The Assymetrix Data API delivers pre-matched canonical pairs with depth metadata, eliminating fuzzy-match drift |
The trade-off most detection systems get wrong
The conventional framing treats divergence detection as a data problem: get faster feeds, tighter spreads, lower latency. That framing is incomplete. The durable edge in cross-venue prediction market arbitrage has shifted toward resolution-rule analysis and superior normalization, precisely because mechanical price gaps close faster as more automated systems enter the space.
The teams that consistently extract edge are not the ones with the lowest-latency feed. They are the ones who built a canonical pair library with verified resolution text, who track false positives systematically and retrain their pair-scoring thresholds quarterly, and who treat leg risk as a first-class engineering problem rather than an afterthought. A 500-millisecond kill switch on a failed second leg is worth more than shaving 50 milliseconds off detection latency.
One operational note worth internalizing: Kalshi’s ACH settlement and Polymarket’s USDC settlement create asymmetric capital lock-up periods that most backtest models ignore. A trade that looks attractive on a 7-day resolution horizon may tie up capital on the Kalshi side for 2–3 additional business days post-resolution. That friction compounds across a portfolio of simultaneous positions and should be modeled explicitly.
Assymetrix gives you the canonical layer this pipeline needs
Building a cross-venue divergence detector from raw Polymarket and Kalshi feeds means solving canonical normalization, fuzzy-match drift, depth reconstruction, and provenance logging before you write a single line of detection logic. Assymetrix solves that layer first.

(unsupported figure removed)
The Assymetrix Data API delivers pre-normalized paired market records with stable canonical IDs, yes/no prices from both venues, depth metadata, full resolution text, and millisecond UTC timestamps through a single integration. The historical archive covers nearly one billion rows of trading activity across Polymarket and Kalshi, giving your backtest the sample size it actually needs for statistically stable threshold calibration. Cross-venue arbitrage signals and Smart Money wallet tracking are surfaced on top of the same canonical feed.
Streaming subscriptions, historical exports, and developer API access are available at data.assymetrix.com. Start with the free tier to validate your detection pipeline, then scale to a streaming subscription when you move to live execution.
FAQ
What symmetric KL-divergence threshold signals a real mispricing?
A symmetric KL above 0.15 between Polymarket and Kalshi yes-prices on a confirmed canonical pair indicates a genuine mispricing rather than transient noise. Below that threshold, the gap is more likely bid-ask friction.
How do fees affect the minimum viable edge for a Polymarket-Kalshi arb?
Combined taker fees on common pairs often consume 3–5 cents per contract round-trip, making gross edges under roughly 4–5 cents non-viable. Target a net edge of at least 1.5% after all costs before sizing a position.
Why do identical-sounding markets sometimes resolve differently?
Resolution-rule mismatches, such as one venue using a BLS advance release and another using the revised figure, or different cutoff times, can cause two markets with nearly identical titles to produce asymmetric outcomes. Always compare full resolution text before confirming a pair.
How does Assymetrix simplify cross-venue market matching?
Assymetrix assigns a stable canonical_market_id to pre-matched Polymarket and Kalshi pairs, eliminating fuzzy-match drift and providing normalized prices, depth metadata, and resolution text through a single API call.
What is leg risk and how do you control it?
Leg risk occurs when one side of a two-venue trade fills and the other fails, leaving an unintended directional position. Control it with simultaneous order submission and a hard kill switch that cancels the filled leg if the second order does not confirm within a defined timeout.
Useful sources and further reading
The methods and thresholds in this guide draw on the following sources. Use them to reproduce the KL calibration, validate fee assumptions, and audit the execution risk framework described above.
Cross-platform arbitrage: prediction markets — symmetric KL threshold rationale, fee impact analysis, and resolution-mismatch taxonomy
Assymetrix Prediction Markets Intelligence — canonical feed documentation, historical data coverage, and API reference
Backtesting Prediction Market Strategies with the Assymetrix Data API — practical guide to constructing reproducible backtests with large historical datasets
Kalshi vs Polymarket: Best Data for Algorithmic Trading — data-quality comparison for algorithmic feed integration
How to Detect Price Divergence Between Polymarket and Kalshi
TL;DR:
Detecting genuine mispricings between Polymarket and Kalshi requires matching markets using a stable canonical ID and applying a symmetric KL-divergence threshold above 0.15. Confirm liquidity depth and identical resolution text before executing trades, ensuring the net-of-fee edge exceeds 1.5 percent for viability. Building such a pipeline benefits from Assymetrix’s pre-normalized data API, which simplifies market pairing and facilitates robust backtesting and live detection.
A reproducible detection pipeline combines normalized cross-venue market matching, spread and z-score screening, and a symmetric KL-divergence above 0.15 threshold to flag likely genuine mispricings between Polymarket and Kalshi. When a signal fires, run three immediate checks before sizing any position:
Confirm resolution text is word-for-word identical across both venues
Verify order-book depth at your target notional on both sides
Compute net-of-fee edge; if it clears 1.5% after combined taker costs, flag for execution
If any check fails, log the signal for analysis and move on. The rest of this guide shows you how to build every layer of that pipeline.
Table of Contents
How to detect price divergence between Polymarket and Kalshi: the core metrics
How do you match the same market across both venues?
What data infrastructure does real-time divergence detection require?
How do you execute both legs without creating directional exposure?
Worked example: a Fed decision market divergence
How should you backtest and calibrate detection thresholds?
How does the Assymetrix Data API accelerate this pipeline?
Why do resolution-rule differences create false arbitrage signals?
What does a live divergence monitoring system look like?
Key Takeaways
The trade-off most detection systems get wrong
Assymetrix gives you the canonical layer this pipeline needs
Useful sources and further reading
How to detect price divergence between Polymarket and Kalshi: the core metrics
Spread, z-score, and symmetric KL divergence

Raw spread, the absolute difference between Polymarket’s yes-price and Kalshi’s yes-price on a matched market, is the fastest first filter. It is also the most misleading in isolation. A 3-cent spread at 50¢ probability is structurally different from a 3-cent spread at 92¢, because the information content and reversion dynamics differ across probability space.
The z-score corrects for that. Compute it as the current spread divided by the rolling standard deviation of that paired spread over a lookback window (typically 500–2,000 ticks per market category). A z-score above 2.0 on a stable pair warrants attention; above 3.0 it is worth escalating to the distributional check.
Symmetric KL divergence is the definitive filter. For binary markets where each venue quotes a yes-probability p and q:
KL_sym = 0.5 * (p * log(p/q) + (1-p) * log((1-p)/(1-q))) + 0.5 * (q * log(q/p) + (1-q) * log((1-q)/(1-p)))
A symmetric KL above 0.15 signals a probability distribution inconsistency large enough to be a genuine mispricing rather than transient noise. Below that threshold, the gap is more likely bid-ask friction or a momentary liquidity imbalance.
A preliminary gross-arb screen runs in parallel: if the cheapest Yes across both venues plus the cheapest No across both venues sums to less than $1.00, a gross arbitrage exists before fees. That check is O(1) and should run on every tick.
Pro Tip: Calibrate KL and z-score thresholds separately by market category. Political markets tend to have higher baseline volatility and wider natural spreads than economic indicator markets (CPI, FOMC). A threshold that generates clean signals on Fed-decision markets will produce excessive false positives on election markets in the final 48 hours before resolution.
How do you match the same market across both venues?
Canonical normalization and fuzzy-matching pipeline
Polymarket and Kalshi use incompatible internal identifiers, different title conventions, and sometimes different outcome sets for what is functionally the same event. The normalization pipeline is where most detection systems fail.

The canonical schema fields to align are: normalized title, event date, resolution source, outcome set (yes/no vs. binary contract), and category tag. Strip punctuation, lowercase everything, remove stopwords, and apply named-entity alignment to catch “Fed” vs. “Federal Reserve” or “FOMC” vs. “Federal Open Market Committee.”
Pipeline stage | Operation | Output |
|---|---|---|
Ingestion | Pull raw title, slug, resolution text, tags | Raw record per venue |
Canonicalization | Lowercase, tokenize, strip stopwords, NER alignment | Normalized token set |
Candidate scoring | Jaccard similarity on token sets + resolution-source match | Score 0–1 per pair |
Human-overwatch gate | Flag pairs scoring 0.70–0.85 for manual review | Confirmed or rejected |
Confirmed pair | Assign canonical_market_id, store venue_ids | Canonical paired record |
Pairs scoring above 0.85 on both token similarity and resolution-source match can be auto-confirmed. Anything below 0.70 is discarded. The middle band requires a human review queue, which shrinks quickly once you build a confirmed-pair library.
A rule-based resolution-source check is non-negotiable. Two markets with identical titles but different resolution sources (BLS advance release vs. BLS revised release, for example) are not the same market. That check alone eliminates the most dangerous false arbs.
What data infrastructure does real-time divergence detection require?
Feed requirements, canonical schema, and latency budgets
Infrastructure layer | Requirement | Notes |
|---|---|---|
Feed type | WebSocket or streaming REST | Polling introduces detection lag |
Timestamp accuracy | UTC, millisecond precision | Required for cross-venue tick alignment |
Sequence IDs | Per-venue event sequence | Detects dropped messages |
Canonical schema fields | market_id, canonical_title, venue_ids, yes_price, no_price, best_depth, timestamp_utc | Minimum viable schema |
End-to-end detection latency | — | Budget per layer |
Time synchronization matters more than most teams expect. Clock skew between your Polymarket feed handler and your Kalshi feed handler will produce phantom divergences that disappear on closer inspection. Run NTP or PTP synchronization on all feed-ingestion nodes and log the measured skew in your provenance records.
For depth reconstruction, prefer normalized tick-level snapshots supplemented by periodic full book-dumps. Top-of-book prices are insufficient for sizing; you need the incremental slippage curve at each notional bucket to know whether your target size is actually fillable at the quoted price.
Pro Tip: Store raw ticks with full provenance metadata (venue, sequence ID, ingest timestamp, processing timestamp) even if you only query aggregated snapshots in production. That audit trail is what makes backtests reproducible and lets you diagnose whether a missed signal was a feed gap or a genuine absence of divergence.
How do you execute both legs without creating directional exposure?
Leg risk, fee-aware edge, and execution controls
Leg risk is the primary operational hazard: one leg fills, the other fails due to latency, rate limits, or thin liquidity, and you hold an unintended directional position. Professional systems minimize this by firing both legs as close together as possible, with a kill switch that cancels the unfilled leg and closes the filled leg if the second order does not confirm within a defined timeout (typically 500 ms to 2 s depending on venue API rate limits).
Net-of-fee edge is the number that actually matters. Combined taker fees on common Kalshi-Polymarket pairs often consume 3–5 cents per contract round-trip. A gross edge under roughly 4–5 cents is marginal at best after fees and slippage; practitioners treat sub-4-cent gross edges as non-viable. The minimum viable net edge rule of thumb is 1.5% after all costs.
Execution checklist before firing:
Confirm canonical_market_id match and identical resolution text
Reconstruct order-book slices at target notional; verify fill is achievable at posted prices
Compute net-of-fee edge; reject if below threshold
Check withdrawal and settlement constraints (Kalshi uses ACH/USD; Polymarket uses USDC on Polygon)
Verify neither venue has a pending resolution event within the next 60 minutes
Fire both legs simultaneously with retry logic and a hard kill switch on timeout
Worked example: a Fed decision market divergence
Suppose Polymarket quotes “Fed cuts rates at May FOMC” at 0.62 yes, and Kalshi quotes the same event at 0.55 yes. The gross-arb screen fires immediately: 0.55 + (1 - 0.62) = 0.93 < 1.00.
Compute symmetric KL: with p = 0.62 and q = 0.55, KL_sym ≈ 0.018. That is well below 0.15, meaning the distributional gap is within noise range despite the 7-cent headline spread. The z-score on this pair’s 30-day spread history comes back at 1.4, also below the 2.0 threshold. No signal. Log and continue.
Now shift the scenario: Polymarket at 0.72, Kalshi at 0.55. KL_sym ≈ 0.089, z-score at 2.8. Still below the KL threshold, but the z-score warrants a depth check.
Scenario | Polymarket yes | Kalshi yes | KL_sym | Z-score | Signal? |
|---|---|---|---|---|---|
Baseline | 0.62 | 0.55 | 0.018 | 1.4 | No |
Moderate gap | 0.72 | 0.55 | 0.089 | 2.8 | Depth check |
Strong gap | 0.82 | 0.55 | 0.21 | — | Yes — verify resolution |
At the strong-gap scenario, KL_sym = 0.21 exceeds 0.15. Reconstruct book slices: Polymarket shows $8,000 available at 0.82; Kalshi shows $12,000 at 0.55. For a $5,000 notional position, both sides are fillable. Net-of-fee edge after 3 cents per side: approximately 24 cents per dollar, well above the 1.5% minimum.
Pro Tip: Use fixed-fraction sizing (e.g., 2–5% of capital per signal) rather than full Kelly in early live trading. Kelly sizing is theoretically optimal but requires an accurate edge estimate, which your backtest may overstate before you have live fill data to calibrate against.
How should you backtest and calibrate detection thresholds?
Backtesting a divergence detector requires four inputs: canonical historical ticks for both venues, per-venue order-book reconstructions at the time of each signal, the fee schedule in effect during the test period, and a realistic fill model that accounts for queue position and partial fills.
Build a canonical tick database from historical exports, joining on canonical_market_id
Replay ticks chronologically; compute spread, z-score, and KL_sym at each step
At each threshold exceedance, simulate fills using reconstructed book slices
Apply fee schedules and record net PnL per signal
Split into in-sample calibration and out-of-sample validation sets; use bootstrap resampling to estimate confidence intervals on hit rate and mean return per signal
KL threshold | Approx. signal rate | Approx. true-positive rate | Mean net edge per signal |
|---|---|---|---|
> 0.10 | High | Lower (more noise) | Marginal |
> 0.15 | Moderate | Higher | Positive after fees |
> 0.20 | Low | Highest | Strong but rare |
The 0.15 threshold sits at the practical optimum for most political and economic market categories: enough signals to generate meaningful returns, with a true-positive rate that justifies automated execution. Tighten to 0.20 for fully automated systems with no human review; loosen to 0.10 only in monitored paper-trade mode.
How does the Assymetrix Data API accelerate this pipeline?
Assymetrix provides the canonical normalization layer that replaces the fuzzy-matching pipeline described above. The Data API at data.assymetrix.com returns pre-matched market pairs with a stable canonical_market_id, normalized yes/no prices from both venues, depth metadata, resolution text, and UTC timestamps in a single response. The historical coverage spans nearly one billion rows of trading activity, which is the sample size required for statistically stable threshold calibration across market categories.
Sample canonical schema returned by the API:
Field | Description |
|---|---|
canonical_market_id | Stable cross-venue identifier |
venue_map |
|
yes_price_poly | Polymarket yes mid |
yes_price_kalshi | Kalshi yes mid |
best_depth_poly | Top-of-book depth, Polymarket |
best_depth_kalshi | Top-of-book depth, Kalshi |
resolution_text | Full resolution clause (both venues) |
timestamp_utc | Millisecond-precision UTC |
A Python snippet to fetch a paired record and compute KL_sym in-stream:
import requests, math def kl_sym(p, q, eps=1e-9): p, q = max(p, eps), max(q, eps) p_, q_ = 1 - p, 1 - q return 0.5 * (p*math.log(p/q) + p_*math.log(p_/q_) + q*math.log(q/p) + q_*math.log(q_/p_)) resp = requests.get( "https://data.assymetrix.com/v1/markets/paired", headers={"Authorization": "Bearer YOUR_KEY"} ) for market in resp.json()["markets"]: kl = kl_sym(market["yes_price_poly"], market["yes_price_kalshi"]) if kl > 0.15: print(f"Signal: {market['canonical_market_id']} | KL={kl:.3f}")
Pro Tip: Use Assymetrix’s canonical_market_id as the join key in your backtest database. Fuzzy-match drift, where a pair that was correctly matched at ingestion silently diverges as market titles update, is one of the hardest bugs to catch post-hoc. A stable canonical ID eliminates it.
Why do resolution-rule differences create false arbitrage signals?
Resolution-rule mismatches are the single most common source of false arb signals. Two markets with nearly identical titles can resolve on different data releases (BLS advance vs. BLS revised), different official sources, or different cutoff times, producing asymmetric outcomes that make a convergence trade a directional bet in disguise.
The workflow is non-negotiable: always fetch and compare the full resolution text from both venues before confirming a pair. Any textual mismatch, even a difference in the cited data release vintage, disqualifies the pair from arb treatment. Flag it as a non-arb divergence and route it to a separate research queue.
Regulatory settlement mechanics also differ. Kalshi settles in USD via ACH with custodial fund holding; Polymarket settles in USDC on Polygon with non-custodial wallet control. Settlement timing differences affect capital lock-up calculations and should be factored into annualized return estimates.
This article is general information, not legal or financial advice. Confirm current regulatory requirements and fee schedules with each venue’s official documentation and a qualified professional before trading.
What does a live divergence monitoring system look like?
Dashboard KPIs and runbook steps
A production monitoring system needs four dashboard KPIs: signal rate (signals per hour by market category), true-positive rate after manual spot-checks, mean detection-to-execution latency, and fill success rate (both legs filled vs. one-leg failures).
Alert tiers:
P0 (auto-execute): KL > 0.15 + depth sufficient at target notional + resolution text confirmed identical + net edge > 1.5%
P1 (human review): KL > 0.10 + depth borderline or resolution text flagged for review
P2 (log only): Gross-arb screen fires but KL < 0.10
Runbook for a P0 alert:
Automated system fires both legs with kill-switch timeout
Post-trade reconciliation confirms both fills within 30 seconds
If one leg fails, automated cancel-and-close sequence runs; alert on-call engineer
Log signal, fill prices, slippage, and net PnL to the audit database
Flag for threshold review if fill slippage exceeded the pre-trade estimate by more than 20%
Data-quality exceptions (missing ticks, feed gaps, anomalous prices) should trigger an automatic pause on automated execution for the affected market pair until the feed is confirmed clean.
Key Takeaways
Detecting genuine price divergence between Polymarket and Kalshi requires canonical market matching, a symmetric KL-divergence threshold above 0.15, confirmed liquidity depth, and identical resolution text before any position is sized.
Point | Details |
|---|---|
Core detection rule | Canonical pair match + symmetric KL > 0.15 + depth check + identical resolution text = valid signal |
Fee-aware edge minimum | Net edge must exceed 1.5% after combined taker fees; gross edges under ~4–5 cents are typically non-viable |
Backtest sample size | Use nearly one billion rows of historical activity to calibrate KL and z-score thresholds by market category |
Resolution text is mandatory | Always compare full resolution clauses; title similarity alone produces false arb signals |
Assymetrix integration | The Assymetrix Data API delivers pre-matched canonical pairs with depth metadata, eliminating fuzzy-match drift |
The trade-off most detection systems get wrong
The conventional framing treats divergence detection as a data problem: get faster feeds, tighter spreads, lower latency. That framing is incomplete. The durable edge in cross-venue prediction market arbitrage has shifted toward resolution-rule analysis and superior normalization, precisely because mechanical price gaps close faster as more automated systems enter the space.
The teams that consistently extract edge are not the ones with the lowest-latency feed. They are the ones who built a canonical pair library with verified resolution text, who track false positives systematically and retrain their pair-scoring thresholds quarterly, and who treat leg risk as a first-class engineering problem rather than an afterthought. A 500-millisecond kill switch on a failed second leg is worth more than shaving 50 milliseconds off detection latency.
One operational note worth internalizing: Kalshi’s ACH settlement and Polymarket’s USDC settlement create asymmetric capital lock-up periods that most backtest models ignore. A trade that looks attractive on a 7-day resolution horizon may tie up capital on the Kalshi side for 2–3 additional business days post-resolution. That friction compounds across a portfolio of simultaneous positions and should be modeled explicitly.
Assymetrix gives you the canonical layer this pipeline needs
Building a cross-venue divergence detector from raw Polymarket and Kalshi feeds means solving canonical normalization, fuzzy-match drift, depth reconstruction, and provenance logging before you write a single line of detection logic. Assymetrix solves that layer first.

(unsupported figure removed)
The Assymetrix Data API delivers pre-normalized paired market records with stable canonical IDs, yes/no prices from both venues, depth metadata, full resolution text, and millisecond UTC timestamps through a single integration. The historical archive covers nearly one billion rows of trading activity across Polymarket and Kalshi, giving your backtest the sample size it actually needs for statistically stable threshold calibration. Cross-venue arbitrage signals and Smart Money wallet tracking are surfaced on top of the same canonical feed.
Streaming subscriptions, historical exports, and developer API access are available at data.assymetrix.com. Start with the free tier to validate your detection pipeline, then scale to a streaming subscription when you move to live execution.
FAQ
What symmetric KL-divergence threshold signals a real mispricing?
A symmetric KL above 0.15 between Polymarket and Kalshi yes-prices on a confirmed canonical pair indicates a genuine mispricing rather than transient noise. Below that threshold, the gap is more likely bid-ask friction.
How do fees affect the minimum viable edge for a Polymarket-Kalshi arb?
Combined taker fees on common pairs often consume 3–5 cents per contract round-trip, making gross edges under roughly 4–5 cents non-viable. Target a net edge of at least 1.5% after all costs before sizing a position.
Why do identical-sounding markets sometimes resolve differently?
Resolution-rule mismatches, such as one venue using a BLS advance release and another using the revised figure, or different cutoff times, can cause two markets with nearly identical titles to produce asymmetric outcomes. Always compare full resolution text before confirming a pair.
How does Assymetrix simplify cross-venue market matching?
Assymetrix assigns a stable canonical_market_id to pre-matched Polymarket and Kalshi pairs, eliminating fuzzy-match drift and providing normalized prices, depth metadata, and resolution text through a single API call.
What is leg risk and how do you control it?
Leg risk occurs when one side of a two-venue trade fills and the other fails, leaving an unintended directional position. Control it with simultaneous order submission and a hard kill switch that cancels the filled leg if the second order does not confirm within a defined timeout.
Useful sources and further reading
The methods and thresholds in this guide draw on the following sources. Use them to reproduce the KL calibration, validate fee assumptions, and audit the execution risk framework described above.
Cross-platform arbitrage: prediction markets — symmetric KL threshold rationale, fee impact analysis, and resolution-mismatch taxonomy
Assymetrix Prediction Markets Intelligence — canonical feed documentation, historical data coverage, and API reference
Backtesting Prediction Market Strategies with the Assymetrix Data API — practical guide to constructing reproducible backtests with large historical datasets
Kalshi vs Polymarket: Best Data for Algorithmic Trading — data-quality comparison for algorithmic feed integration
