# Polymarket vs Kalshi Disagreement Monitor (`fetchfinch/prediction-market-disagreement-monitor`) Actor

Detect explainable cross-market and related-market probability inconsistencies as research signals, with persistent monitoring and alerts.

- **URL**: https://apify.com/fetchfinch/prediction-market-disagreement-monitor.md
- **Developed by:** [Fetch Finch](https://apify.com/fetchfinch) (community)
- **Categories:** Automation, Integrations, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 research signals

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Polymarket vs Kalshi Prediction Market Monitor

Compare public Polymarket and Kalshi markets and detect meaningful probability disagreements. The Actor finds likely equivalent contracts, checks related threshold and deadline markets for logical inconsistencies, and returns structured research signals with links and supporting evidence.

Use it for prediction market research, market monitoring, data analysis, dashboards, scheduled reports, and webhook alerts. No exchange account, wallet, or API key is required.

### What you get

- Cross-platform comparisons between Polymarket and Kalshi
- Related-market checks within and across venues
- Confidence scores and match evidence for every inferred relationship
- Direct links to both market pages
- Bid, ask, probability, volume, liquidity, and close-time context when available
- Quote spreads and the exact pricing method used for each signal
- Persistent signal lifecycle tracking for scheduled monitors
- Optional Slack, Discord, Make, Zapier, or generic webhook alerts
- Optional public order book depth analysis for equivalent markets

All results are research signals. The Actor does not place trades or provide financial advice.

### Signals

#### Equivalent-market disagreement

Finds likely equivalent Polymarket and Kalshi propositions whose YES probabilities differ by more than your selected threshold.

Example:

- Polymarket probability: 42%
- Kalshi probability: 51%
- Reported disagreement: 9 percentage points

Automatic matching checks subject similarity, title similarity, numeric thresholds, direction, observation type, year, and close-time compatibility before creating a relationship. Path-dependent contracts such as "reach during the day" are not treated as equivalent to point-in-time observations.

#### Quote quality controls

The Actor does not treat every midpoint as meaningful. A signal qualifies in one of two ways:

- `executable_quote_gap`: the relevant best bid is separated from the opposing best ask by at least your signal threshold
- `quality_filtered_midpoint`: both bid/ask spreads pass `maxQuoteSpreadPctPoints`, then the midpoint difference passes your signal threshold

Markets with missing or excessively wide quotes are suppressed unless their public quotes are genuinely separated. Every result includes the spread on each market, the midpoint gap, the executable gap when available, and the method used.

#### Threshold monotonicity violation

Checks markets where one event is a stricter version of another.

For example, the probability of Bitcoin exceeding $150,000 by a given deadline should not be higher than the probability of Bitcoin exceeding $100,000 by the same deadline.

Expected constraint:

```text
P(stricter threshold) <= P(easier threshold)
```

Threshold ladders are checked between adjacent strikes. This detects monotonicity breaks without returning many redundant combinations from the same ladder.

#### Deadline monotonicity violation

Checks markets where the same event has an earlier and a later deadline.

For example, the probability of an event occurring by June should not be higher than the probability of that event occurring by December, assuming compatible rules.

Expected constraint:

```text
P(earlier deadline) <= P(later deadline)
```

### Quick start

For a focused snapshot, enter topics and choose the relationship checks you want to run:

```json
{
  "mode": "snapshot",
  "topics": ["Bitcoin", "Federal Reserve", "election"],
  "signalTypes": ["equivalent", "threshold_subset", "deadline_subset"],
  "minDisagreementPctPoints": 5,
  "minViolationPctPoints": 3,
  "minMatchConfidence": 0.82,
  "maxQuoteSpreadPctPoints": 15,
  "maxMarketsPerVenue": 2000,
  "maxSignals": 100,
  "excludeSports": true,
  "excludeKalshiMultivariate": true
}
```

Leave `topics` empty to scan the bounded market universe returned by both venues.

### Snapshot and monitor modes

#### Snapshot mode

Snapshot mode returns all current signals that meet your thresholds. Use it for one-time research, dataset generation, manual review, or periodic full reports.

#### Monitor mode

Monitor mode remembers previous observations and returns lifecycle changes instead of repeating unchanged signals.

Lifecycle values include:

- `opened`: a new signal passed the persistence requirement
- `widened`: the disagreement increased materially
- `narrowed`: the disagreement decreased materially but remains active
- `resolved`: the signal disappeared or fell below the configured threshold

For reliable recurring monitoring, run the Actor from an Apify Schedule with the same input and state store.

```json
{
  "mode": "monitor",
  "topics": ["Federal Reserve"],
  "minimumPersistenceRuns": 2,
  "materialChangePctPoints": 1,
  "alertOnFirstRun": false,
  "stateStoreName": "prediction-market-disagreement-state"
}
```

The first monitor run creates a quiet baseline by default. Set `alertOnFirstRun` to `true` if you want current signals emitted immediately.

### Alerts

Add one or more HTTPS webhook URLs to receive aggregated lifecycle updates after each monitor run.

Supported formats:

- Slack incoming webhooks
- Discord webhooks
- Generic JSON webhooks
- Make and Zapier webhook endpoints

```json
{
  "mode": "monitor",
  "webhookUrls": ["https://your-endpoint.example/hook"],
  "webhookFormat": "auto",
  "failOnWebhookError": false
}
```

The `auto` format recognizes Slack and Discord webhook hosts. Other destinations receive structured JSON. Alert URLs and custom headers are treated as secrets and are not included in output records.

Webhook destinations must use HTTPS. Local and private-network destinations are rejected, and redirects are disabled.

### Output

The default Dataset contains one record per current signal in snapshot mode or per lifecycle update in monitor mode.

Each record includes:

- Signal type and stable signal ID
- Lifecycle status
- Severity score
- Disagreement or violation magnitude in percentage points
- Expected probability constraint
- Relationship confidence and matching evidence
- Quote method, spreads, midpoint gap, and executable quote gap
- Both market questions, IDs, URLs, prices, and metadata
- Research caveats
- Optional order book analysis

Example:

```json
{
  "recordType": "signal",
  "signalId": "equivalent-8bda0bcd16d583f2",
  "signalType": "equivalent_disagreement",
  "lifecycleStatus": "current",
  "severity": 72.4,
  "magnitudePctPoints": 9,
  "message": "Polymarket and Kalshi quality-filtered quote midpoints differ by 9.00 percentage points on likely equivalent propositions.",
  "quoteEvidence": {
    "method": "quality_filtered_midpoint",
    "leftSpreadPctPoints": 2,
    "rightSpreadPctPoints": 3,
    "maximumAllowedSpreadPctPoints": 15,
    "midpointGapPctPoints": 9,
    "executableGapPctPoints": 4
  },
  "relationship": {
    "type": "equivalent",
    "confidence": 0.94,
    "source": "automatic",
    "evidence": {
      "reasonCodes": [
        "cross_venue",
        "shared_subject",
        "matching_year",
        "compatible_close_time"
      ]
    }
  },
  "leftMarket": {
    "venue": "polymarket",
    "question": "Example market question",
    "probability": 0.42
  },
  "rightMarket": {
    "venue": "kalshi",
    "question": "Example market question",
    "probability": 0.51
  },
  "researchCaveats": [
    "Public quotes are non-atomic snapshots and can change between venue requests."
  ]
}
```

The default key-value store also contains:

- `SUMMARY`: source counts, filtering diagnostics, relationship totals, signal totals, state details, and redacted alert delivery results
- `RELATIONSHIPS`: the complete relationship audit, including matched relationships that did not produce a signal

### Explicit relationships

Automatic matching is intentionally conservative. If you already know which markets should be compared, provide exact Polymarket IDs, condition IDs, slugs, or Kalshi tickers.

```json
{
  "explicitRelationships": [
    {
      "leftVenue": "polymarket",
      "leftId": "POLYMARKET_ID_OR_SLUG",
      "rightVenue": "kalshi",
      "rightId": "KALSHI_MARKET_TICKER",
      "type": "equivalent",
      "note": "Manually reviewed pair"
    }
  ]
}
```

For `threshold_subset` and `deadline_subset`, the left proposition must imply the right proposition. The expected constraint is `P(left) <= P(right)`.

Use `excludedRelationshipIds` to suppress a known incorrect automatic relationship. Stable relationship IDs are available in the Dataset and `RELATIONSHIPS` output.

### Optional order book analysis

Set `executionContracts` above zero to add depth-aware research for equivalent-market signals.

The Actor fetches public YES and NO order books, walks available asks for an equal contract quantity, checks both opposing outcome combinations, and includes estimated fees.

Relevant fields are deliberately labeled as conditional, including `netPnlIfRulesEquivalentUsd`. This analysis does not guarantee execution or establish that two rulebooks are economically identical.

### Filtering and coverage

- `topics` applies a local text filter after markets are fetched.
- `maxMarketsPerVenue` limits the number of active markets requested from each venue.
- `maxQuoteSpreadPctPoints` prevents wide-spread midpoint artifacts from becoming signals.
- Sports markets are excluded by default because live-game and cancellation rules require specialized comparison logic.
- Kalshi multivariate combinations are excluded by default.
- Only binary YES and NO Polymarket markets enter automatic relationship discovery.
- Polymarket and Kalshi report volume in different units, so volume values should not be directly compared across venues.

Increasing the market limit improves coverage but may increase run time and resource use.

### Common use cases

- Prediction market research dashboards
- Polymarket and Kalshi odds comparison
- Scheduled disagreement monitoring
- Market data quality checks
- Academic and quantitative research
- Newsroom and forecasting workflows
- Alerting on unusual probability changes
- Building reviewed relationship datasets

### Data sources and access

The Actor uses public read-only endpoints from:

- Polymarket Gamma API for market discovery and metadata
- Polymarket CLOB API for optional order books
- Kalshi Trade API v2 for markets and optional order books

No trading credentials, wallet details, exchange API keys, email addresses, or passwords are requested. The Actor does not access trading accounts or place orders.

### Important limitations

Prediction market titles can hide material differences in resolution sources, observation times, boundary handling, cancellation, postponement, disputes, and settlement timing. Automatic relationships are screening candidates and should be reviewed using both linked rule pages.

Public API snapshots are not simultaneous. Prices, spreads, and depth can change between requests. Geographic availability and legal restrictions vary by user and venue.

This Actor is an independent research tool. It is not affiliated with Polymarket or Kalshi. Its outputs are not financial, investment, legal, or trading advice.

# Actor input Schema

## `mode` (type: `string`):

Snapshot returns every current qualifying signal. Monitor remembers state and returns only lifecycle changes after its first baseline run.

## `topics` (type: `array`):

Keep markets mentioning at least one topic, such as Bitcoin, Federal Reserve, election, or government shutdown. Empty scans the bounded fetched universe.

## `signalTypes` (type: `array`):

Equivalent compares likely identical cross-venue contracts. Threshold and deadline checks test logical probability ordering.

## `minDisagreementPctPoints` (type: `number`):

Minimum absolute YES-probability difference for likely equivalent markets.

## `minViolationPctPoints` (type: `number`):

Minimum amount by which a narrower threshold/deadline market may exceed its broader parent before a signal is emitted.

## `minMatchConfidence` (type: `number`):

Higher values favor precision over coverage. Hard numeric, direction, and timing conflicts are rejected independently.

## `maxCloseDifferenceHours` (type: `number`):

Likely equivalent markets farther apart than this are rejected unless explicitly supplied.

## `maxQuoteSpreadPctPoints` (type: `number`):

Suppress midpoint-based signals when either YES bid/ask spread exceeds this limit. Separated executable quotes can still qualify.

## `minimumVolume24h` (type: `number`):

Filter on each venue's source-reported 24-hour volume. Polymarket reports USD; Kalshi reports contracts.

## `maxMarketsPerVenue` (type: `integer`):

Safety cap for each active public market universe. Larger values improve coverage and increase requests.

## `maxSignals` (type: `integer`):

Maximum current or changed signals written in one run, ranked by severity.

## `excludeSports` (type: `boolean`):

Recommended for the initial research workflow because live-game and cancellation rules create noisy matches.

## `excludeKalshiMultivariate` (type: `boolean`):

Excludes combo/parlay-style markets from automatic discovery.

## `executionContracts` (type: `number`):

For equivalent signals, walk both public order books at this equal contract quantity. Use 0 to skip depth requests.

## `kalshiFeeRate` (type: `number`):

Used only for optional hypothetical execution math. Product-specific fee exceptions may differ.

## `explicitRelationships` (type: `array`):

Audit exact market IDs/tickers. A subset relationship means P(left) must be less than or equal to P(right).

## `excludedRelationshipIds` (type: `array`):

Suppress known-bad automatic relationships using IDs from the relationship audit output.

## `materialChangePctPoints` (type: `number`):

Emit widened/narrowed only when magnitude changes by at least this amount.

## `minimumPersistenceRuns` (type: `integer`):

Use 2 or more to suppress one-scan noise in monitor mode.

## `alertOnFirstRun` (type: `boolean`):

Normally monitor mode creates a quiet baseline. Enable to emit and deliver current qualifying signals immediately.

## `stateStoreName` (type: `string`):

Named key-value store shared across scheduled runs. Configuration fingerprints isolate different monitors.

## `resetState` (type: `boolean`):

Delete saved state for this configuration before the run.

## `webhookUrls` (type: `array`):

Optional HTTPS Slack, Discord, Make, Zapier, or generic endpoints. Secrets are redacted from logs and outputs.

## `webhookFormat` (type: `string`):

Auto-detect Slack and Discord by hostname, or force a supported payload shape.

## `webhookHeaders` (type: `object`):

Optional string headers such as Authorization for generic endpoints.

## `failOnWebhookError` (type: `boolean`):

Enable when an external scheduler should retry failed notification delivery.

## Actor input object example

```json
{
  "mode": "snapshot",
  "topics": [
    "Bitcoin",
    "Federal Reserve",
    "election"
  ],
  "signalTypes": [
    "equivalent",
    "threshold_subset",
    "deadline_subset"
  ],
  "minDisagreementPctPoints": 5,
  "minViolationPctPoints": 3,
  "minMatchConfidence": 0.82,
  "maxCloseDifferenceHours": 72,
  "maxQuoteSpreadPctPoints": 15,
  "minimumVolume24h": 0,
  "maxMarketsPerVenue": 2000,
  "maxSignals": 100,
  "excludeSports": true,
  "excludeKalshiMultivariate": true,
  "executionContracts": 0,
  "kalshiFeeRate": 0.07,
  "explicitRelationships": [],
  "excludedRelationshipIds": [],
  "materialChangePctPoints": 1,
  "minimumPersistenceRuns": 1,
  "alertOnFirstRun": false,
  "stateStoreName": "prediction-market-disagreement-state",
  "resetState": false,
  "webhookFormat": "auto",
  "failOnWebhookError": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `relationships` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "topics": [
        "Bitcoin",
        "Federal Reserve",
        "election"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchfinch/prediction-market-disagreement-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "topics": [
        "Bitcoin",
        "Federal Reserve",
        "election",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fetchfinch/prediction-market-disagreement-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "topics": [
    "Bitcoin",
    "Federal Reserve",
    "election"
  ]
}' |
apify call fetchfinch/prediction-market-disagreement-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchfinch/prediction-market-disagreement-monitor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ohiAQyT2BSSD1DQ7a/builds/Ilzt7y0i1oHlXnsks/openapi.json
