Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board
HomeBy LayerIntelligence4.10 ContradictionDetector

4.10 ContradictionDetector

Intelligence Signal Service Read-only PLANNED Spec started capital · Indirect P2 · Data normalisation pending stub

Surface markets whose resolution rule contradicts itself, the title, or a parent neg-risk constraint.

v3 readiness

Docs27/27
donehow scored
Impl0/15
pendinghow scored
Backtest0/4
pendinghow scored
Runtime0/8
pendinghow scored

A bot is done when all four scores are. What does done mean?

1. Bot Identity

LayerIntelligence  Intelligence
Bot classSignal Service
AuthorityRead-only
StatusPLANNED
ReadinessSpec started
Runs before
Runs after
Applies to
Default modeshadow_only
User-visibleAdvanced details only
Developer ownerPolytraders core

2. Purpose

Surface markets whose resolution rule contradicts itself, the title, or a parent neg-risk constraint.

3. Why This Bot Matters

  • Self-contradicting market traded as if resolvable

    A market whose resolution rule contradicts its title or a parent neg-risk constraint may never resolve cleanly. Strategies that trade it can be left holding positions through ambiguous or court-overturned outcomes.

  • Neg-risk parent constraint silently violated

    Polymarket's neg-risk markets share a constraint that exactly one outcome must resolve YES across the set. A child market whose rule allows two simultaneous YES resolutions breaks that invariant; without detection, sizing and hedging logic on those markets is wrong.

  • Operators surprised at resolution time

    Discovery of a contradiction at resolution is the worst possible time. Catching it at listing time lets Discovery exclude the market or Governance flag it for human review days before any trade is placed.

  • No structured reason for excluding a market

    Without this signal, MarketScanner has no defensible reason to filter out a malformed market beyond an operator's gut call. The contradiction record provides the audit trail.

4. Required Polymarket Inputs

InputSourceRequired?Use
YES prices for all markets in a mutually-exclusive groupdataYesSum YES prices to detect contradiction (sum > 1.0 in mutually-exclusive group).
Market group definitions and related condition_idsgammaYesIdentify which markets form mutually-exclusive groups.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagKillSwitchYesSuppress all emissions when KillSwitch is active.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
contradiction_threshold1.051.021.0Minimum sum of YES prices in a mutually-exclusive group to trigger a contradiction signal.
poll_interval_s30120300Seconds between price checks per market group.

7. Detailed Parameter Instructions

contradiction_threshold

What it means

Minimum sum of YES prices in a mutually-exclusive group to trigger a contradiction signal.

Default

{ "contradiction_threshold": 1.05 }

Why this default matters

1.05 absorbs normal bid-ask spread noise while still detecting genuine mis-pricing opportunities.

Threshold logic

ConditionAction
sum >= 1.05Normal — emit contradiction ObservationReport
1.02–1.05WARN — borderline contradiction; emit with low_confidence flag
< 1.0No contradiction — skip emission

Developer check

if (total_yes < p.contradiction_threshold.hard) return;

User-facing English

Contradictions are only reported when market prices are materially inconsistent.

poll_interval_s

What it means

Seconds between price checks per market group.

Default

{ "poll_interval_s": 30 }

Why this default matters

30 s provides near-real-time contradiction detection across related markets.

Threshold logic

ConditionAction
interval <= 30 sNormal
30–120 sWARN — reduced detection speed
> 300 sReject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.poll_interval_s > p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Prices are checked frequently to catch contradictions as soon as they appear.

8. Default Configuration

{
  "bot_id": "intel.contradictiondetector",
  "version": "0.1.0",
  "mode": "planned",
  "defaults": {
    "contradiction_threshold": 1.05,
    "poll_interval_s": 30
  },
  "locked": {
    "contradiction_threshold": {
      "min": 1.0
    },
    "poll_interval_s": {
      "max": 300
    }
  }
}

9. Implementation Flow

— not yet authored —

10. Reference Implementation

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. IF/THEN/ELSE = decision. Translate directly to TypeScript, Python, Go, or Rust.

FUNCTION detectContradictions(market_group):
  // 0. KillSwitch check
  IF FETCH internal.killswitch.status == ACTIVE:
    RETURN

  // 1. Fetch prices for all markets in group
  prices = {}
  FOR cid IN market_group.condition_ids:
    p = FETCH data.GET('/prices/' + cid)
    IF p IS NULL:
      EMIT WARN 'STALE_DATA'
      CONTINUE
    prices[cid] = p

  // 2. Check mutual-exclusivity constraint
  total_yes = sum(prices[c].yes for c in prices)
  IF total_yes > contradiction_threshold:
    EMIT WARN 'CONTRADICTIONDETECTOR_PRICE_CONTRADICTION'
    contradiction_detected = True
  ELSE:
    contradiction_detected = False

  // 3. Emit ObservationReport
  EMIT ObservationReport {
    report_id: gen_id(),
    kind: 'ObservationReport',
    market_group_id: market_group.id,
    contradiction_detected: contradiction_detected,
    total_yes_sum: total_yes,
    emitted_at_ms: now_ms()
  }

SDK calls used

  • data.GET('/prices/<condition_id>')
  • gamma.GET('/market-groups/<group_id>')
  • internal.killswitch.status

Complexity: O(M) per market group where M = markets in group

11. Wire Examples

Input — what arrives on the wire

{
  "label": "Contradiction check for a mutually-exclusive market group",
  "source": "internal",
  "payload": {
    "market_group_id": "grp_uselection2026",
    "condition_ids": [
      "0xabc1000000000000000000000000000000000000000000000000000000000000",
      "0xabc2000000000000000000000000000000000000000000000000000000000000"
    ],
    "timestamp_ms": 1746703000000
  }
}

Output — what the bot emits

{
  "label": "ObservationReport — contradiction detected",
  "payload": {
    "report_id": "rep_cd_grp_uselection_1746703000000",
    "trace_id": "trc_0xbeef0102030405060711",
    "bot_id": "intel.contradictiondetector",
    "kind": "ObservationReport",
    "market_group_id": "grp_uselection2026",
    "contradiction_detected": true,
    "total_yes_sum": 1.18,
    "emitted_at_ms": 1746703005000
  }
}

12. Decision Logic

APPROVE

— not yet authored —

RESHAPE_REQUIRED

— not yet authored —

REJECT

— not yet authored —

WARNING_ONLY

— not yet authored —

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "report_id": "rep_cd_grp_uselection_1746703000000",
  "trace_id": "trc_0xbeef0102030405060711",
  "bot_id": "intel.contradictiondetector",
  "kind": "ObservationReport",
  "market_group_id": "grp_uselection2026",
  "contradiction_detected": true,
  "total_yes_sum": 1.18,
  "warnings": [],
  "emitted_at_ms": 1746703005000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
CONTRADICTIONDETECTOR_PRICE_CONTRADICTIONWARNSum of YES prices in a mutually-exclusive market group exceeds the contradiction_threshold.Emit ObservationReport with contradiction_detected=true; downstream strategies may exploit arbitrage.Inconsistent pricing detected across related markets.
STALE_DATAWARNOne or more market prices in the group could not be fetched.Skip contradiction check for this cycle; retry on next poll.
KILL_SWITCH_ACTIVEHARD_REJECTKillSwitch active; all ContradictionDetector emissions suppressed.Continue polling but suppress all ObservationReport emissions.Contradiction signals paused while trading is suspended system-wide.
CONTRADICTIONDETECTOR_GROUP_INCOMPLETEWARNMarket group has fewer active condition_ids than expected; contradiction check may be incomplete.Emit with incomplete_group=true warning; strategies apply lower confidence weight.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_intel_contradictiondetector_observations_emitted_totalcountercountcontradiction_detectedObservationReports emitted, broken down by contradiction_detected (true/false).
polytraders_intel_contradictiondetector_contradictions_totalcountercountmarket_group_idTotal contradiction events detected per market group.
polytraders_intel_contradictiondetector_yes_sum_gaugegaugescoremarket_group_idLatest sum of YES prices for each market group.

Alerts

AlertConditionSeverityRunbook
ContradictionDetectorContradictionActivepolytraders_intel_contradictiondetector_yes_sum_gauge > 1.05warn#runbook-contradictiondetector-active
ContradictionDetectorStaleDatarate(polytraders_intel_contradictiondetector_observations_emitted_total[10m]) == 0warn#runbook-contradictiondetector-stale

Dashboards

  • Grafana — Intelligence / ContradictionDetector YES-sum by market group

16. Developer Reporting

{
  "bot_id": "intel.contradictiondetector",
  "market_group_id": "grp_uselection2026",
  "total_yes_sum": 1.18,
  "contradiction_detected": true,
  "prices_fetched": 2,
  "prices_missing": 0,
  "killswitch_active": false
}

17. Plain-English Reporting

SituationUser-facing explanation
Strategy identified a pricing inconsistency across related marketsThe prices across related markets don't add up to 100% — one or more outcomes appear over-priced. This is flagged as a potential analytical signal.
No contradiction signal despite related marketsAll related markets are priced consistently — their YES prices sum to approximately 100%.

18. Failure-Mode Block

main_failure_modeData API outage prevents price fetching for a market group, causing ContradictionDetector to miss genuine contradiction windows during the outage.
false_positive_riskMomentary wide spreads during low liquidity cause the YES-sum to exceed the threshold briefly, generating a contradiction signal with no tradeable opportunity.
false_negative_riskA market group with an incomplete set of condition_ids (one market delisted) causes the YES-sum check to be based on a partial group, missing genuine contradictions.
safe_fallbackIf Data API is unavailable for any group member, skip that group's contradiction check entirely rather than computing a partial sum. Emit STALE_DATA WARN.
required_dependenciesPolymarket Data API for live prices, Polymarket Gamma API for group definitions, KillSwitch active flag

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
DATA_API_DOWNBlock Data API for 10 minAutomatic on API recovery; next poll cycle resumes normally
SYNTHETIC_CONTRADICTIONSet mock prices to sum YES > 1.2 for a test groupAutomatic when prices normalise
KILL_SWITCH_ONSet killswitch.active=true during active pollingAutomatic on KillSwitch reset

20. State & Persistence

Cold-start recovery

On cold start, re-fetch all active group prices on first poll cycle.

21. Concurrency & Idempotency

AspectSpecification
Execution modelasync per-group poll loop
Max in-flight20
Idempotency keymarket_group_id + poll_cycle_ms
Per-call timeout (ms)8000
Backpressure strategydrop-after-buffer — max 50 pending group checks
Locking / mutual exclusionRedis SETNX on market_group_id + cycle to prevent duplicate checks

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchSuppress emissions when KillSwitch is active.

Emits to (downstream consumers)

BotWhyContract
strat.arbitrage_strategies

Sibling bots (same OrderIntent)

External services

ServiceEndpointSLA assumedOn failure
Polymarket Data APIhttps://data-api.polymarket.com99.9% / 300 ms p99

23. Security Surfaces

Abuse vectors considered

  • Adversary places thin orders to artificially inflate YES-sum and trigger false contradiction signals
  • Data API returns stale prices during low-liquidity periods, causing spurious contradictions

Mitigations

  • contradiction_threshold parameter tuned above normal spread noise to reduce false positives
  • ObservationReports are informational only — strategies independently validate before acting

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldno
Aware of negative-risk marketsyes
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesDetects mutually contradictory market prices using negRisk and related market linkage. Read-only. No order signing.

API surfaces declared

gammadatainternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation0.1.0
schema2
releasedNone
planned_releaseQ3-2026

Migration history

DateFromToReasonAction taken
2026-04-28n/av2-specSpec drafted post-CLOB-V2 cutover; bot not yet implementedDesigned against V2 schema (pUSD, builder codes, V2 EIP-712 domain)

26. Acceptance Tests

Unit Tests

TestSetupExpected result
YES-sum above threshold emits contradiction ObservationReportGroup of 2 markets, YES prices = [0.62, 0.56], sum = 1.18ObservationReport emitted with contradiction_detected=true, total_yes_sum=1.18
YES-sum below threshold suppresses emissionYES prices = [0.55, 0.43], sum = 0.98No ObservationReport; no alert
KillSwitch suppresses emissionkillswitch.active=true; contradiction presentNo ObservationReport; KILL_SWITCH_ACTIVE logged

Integration Tests

TestExpected result
Full lifecycle: contradiction detected and consumed by arbitrage strategyStrategy receives ObservationReport with contradiction_detected=true and total_yes_sum
Data API down: STALE_DATA emitted; group check skippedSTALE_DATA WARN; no ObservationReport for affected groups

Property Tests

PropertyRequired behaviour
ContradictionDetector never submits or signs ordersAlways true
No ObservationReport emitted when KillSwitch is activeAlways true

27. Operational Runbook

ContradictionDetector incidents are either Data API outages or genuine price contradictions. Contradictions are signals for strategies, not errors.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
ContradictionDetectorContradictionActiveVerify prices on Polymarket.com for the affected market group. If genuine, downstream strategies will consume the signal. No manual action required unless prices are clearly erroneous.Intelligence pod lead if contradiction persists > 30 min
ContradictionDetectorStaleDataCheck Data API health. Verify network connectivity from bot host.Intelligence pod lead if API down > 10 min

Manual overrides

  • adjust_contradiction_threshold — Update config.contradiction_threshold; requires approval gate if threshold < 1.02 — When market spreads widen due to low liquidity causing false positives

Healthcheck

Endpoint: /internal/health/contradictiondetector | Green: Last check < 2x poll_interval_s AND Redis reachable AND Data API returning 200 | Red: No check for > 10 min OR Redis unreachable

28. Promotion Gates

A bot does not advance to the next readiness state until every gate below is green. Gates are observable from production data — no subjective sign-off.

Promote to Shadow

GateHow measuredThreshold
Unit tests pass for contradiction detection, incomplete group handling, and KillSwitch suppressionCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
No false positives on 10 synthetic non-contradictory group checksIntegration test with injected prices0 false positives

Promote to General live

GateHow measuredThreshold
100% detection of synthetic contradiction injection over 7-day soakIntegration test log audit100% detection

29. Developer Checklist

Ready-to-ship score: 27/27 sections complete · 100%

RequirementStatus
Purpose defined✓ done
Required inputs listed✓ done
Parameters defined✓ done
Defaults defined✓ done
Warning thresholds defined✓ done
Hard thresholds defined✓ done
Safe fallback defined✓ done
Structured output defined✓ done
Developer log defined✓ done
Plain-English explanation✓ done
Unit tests defined✓ done
Integration tests defined✓ done
Property tests defined✓ done
Failure-mode block complete✓ done
Reference implementation pseudocode✓ done
Wire examples (input + output)✓ done
Reason codes listed✓ done
Metrics & logs defined✓ done
State & persistence defined✓ done
Concurrency & idempotency defined✓ done
Dependencies declared✓ done
Security surfaces declared✓ done
Polymarket V2 compatibility declared✓ done
Version & migration history declared✓ done
Operational runbook defined✓ done
Promotion gates defined✓ done
Failure-injection recipes defined✓ done