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.11 MarketOntologyBuilder

4.11 MarketOntologyBuilder

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

Cluster markets into events, themes, and underlying entities for cross-market reasoning.

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

Cluster markets into events, themes, and underlying entities for cross-market reasoning.

3. Why This Bot Matters

  • Cross-market correlation invisible to Risk

    Without an ontology, PortfolioGuard sees fifty independent markets where the underlying ontology says they all resolve on the same NFL game. Aggregate exposure to a single real-world event is then dramatically under-counted.

  • Strategies can't compose multi-market positions

    Cross-market arbitrage and hedging strategies need a structured map of which markets share an underlying. Hand-curated lists drift; a generated ontology stays in sync as new markets list each day.

  • Search and discovery degrade as the catalogue grows

    With ten thousand listed markets and no ontology, MarketScanner becomes a substring search. Themed events and entity-level queries — 'all markets resolving on US election' — require structured clusters.

  • Reconciliation can't verify outcome correctness

    When a real-world event fires, gov.reconciler needs to confirm every market that should have resolved did. Without an ontology mapping events to markets, that check is manual.

4. Required Polymarket Inputs

InputSourceRequired?Use
Full active market list with tags and categories from Gamma APIgammaYesPrimary data source for building the market category ontology.
Tag taxonomy from Data APIdataNoSupplement Gamma tag data with canonical tag hierarchy.

5. Required Internal Inputs

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

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
build_interval_s3600720086400Seconds between full ontology rebuild cycles.
min_market_count50205Minimum number of active markets returned by Gamma before ontology build proceeds.

7. Detailed Parameter Instructions

build_interval_s

What it means

Seconds between full ontology rebuild cycles.

Default

{ "build_interval_s": 3600 }

Why this default matters

3600 s (1 h) captures new market listings and category changes without overloading Gamma API.

Threshold logic

ConditionAction
interval <= 3600 sNormal
3600–7200 sWARN — reduced freshness of ontology
> 86400 sReject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

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

User-facing English

Market category structure is rebuilt hourly to reflect new listings.

min_market_count

What it means

Minimum number of active markets returned by Gamma before ontology build proceeds.

Default

{ "min_market_count": 50 }

Why this default matters

50 ensures the build is not based on a truncated Gamma response.

Threshold logic

ConditionAction
count >= 50Normal — proceed with build
5–50WARN — MARKETONTOLOGYBUILDER_TAG_COUNT_DROP; retain last ontology
< 5Reject — do not rebuild; emit STALE_DATA

Developer check

if (markets.length < p.min_market_count.hard) emit('STALE_DATA');

User-facing English

Ontology is only updated when a sufficient number of markets are available.

8. Default Configuration

{
  "bot_id": "intel.marketontologybuilder",
  "version": "0.1.0",
  "mode": "planned",
  "defaults": {
    "build_interval_s": 3600,
    "min_market_count": 50
  },
  "locked": {
    "build_interval_s": {
      "max": 86400
    },
    "min_market_count": {
      "min": 5
    }
  }
}

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 buildOntology():
  // 0. KillSwitch check
  IF FETCH internal.killswitch.status == ACTIVE:
    RETURN

  // 1. Fetch all active markets from Gamma
  markets = FETCH gamma.GET('/markets?status=active&limit=500')
  IF len(markets) == 0:
    EMIT WARN 'STALE_DATA'
    RETURN

  // 2. Cluster by tag/category
  ontology = {}
  FOR m IN markets:
    FOR tag IN m.tags:
      ontology[tag] = ontology.get(tag, []) + [m.condition_id]

  // 3. Build relationship graph
  graph = buildRelationshipGraph(ontology, markets)

  // 4. Compute ontology_hash
  new_hash = hash(ontology)
  IF new_hash == last_ontology_hash:
    RETURN  // no change

  // 5. Emit ObservationReport
  EMIT ObservationReport {
    report_id: gen_id(),
    kind: 'ObservationReport',
    ontology_hash: new_hash,
    category_count: len(ontology),
    market_count: len(markets),
    emitted_at_ms: now_ms()
  }
  last_ontology_hash = new_hash

SDK calls used

  • gamma.GET('/markets?status=active&limit=500')
  • data.GET('/tags')
  • internal.killswitch.status

Complexity: O(M*T) per build cycle where M = markets, T = average tags per market

11. Wire Examples

Input — what arrives on the wire

{
  "label": "Active markets list from Gamma for ontology build",
  "source": "gamma",
  "payload": {
    "market_count": 312,
    "categories": [
      "politics",
      "crypto",
      "sports",
      "economics"
    ],
    "timestamp_ms": 1746703000000
  }
}

Output — what the bot emits

{
  "label": "ObservationReport — ontology updated",
  "payload": {
    "report_id": "rep_mob_1746703000000",
    "trace_id": "trc_0xbeef0102030405060712",
    "bot_id": "intel.marketontologybuilder",
    "kind": "ObservationReport",
    "ontology_hash": "0xdeadbeef12345678",
    "category_count": 18,
    "market_count": 312,
    "emitted_at_ms": 1746703020000
  }
}

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_mob_1746703000000",
  "trace_id": "trc_0xbeef0102030405060712",
  "bot_id": "intel.marketontologybuilder",
  "kind": "ObservationReport",
  "ontology_hash": "0xdeadbeef12345678",
  "category_count": 18,
  "market_count": 312,
  "warnings": [],
  "emitted_at_ms": 1746703020000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
MARKETONTOLOGYBUILDER_ONTOLOGY_CHANGEDINFOOntology hash changed since last build cycle; new markets or category changes detected.Emit ObservationReport with updated ontology hash; downstream consumers reload ontology.Market category structure has been updated.
STALE_DATAWARNGamma API returned empty or zero markets during build cycle.Skip build; retain last ontology; retry on next poll.
KILL_SWITCH_ACTIVEHARD_REJECTKillSwitch active; all ontology build emissions suppressed.Continue internal builds but suppress ObservationReport emissions.Ontology updates paused while trading is suspended system-wide.
MARKETONTOLOGYBUILDER_TAG_COUNT_DROPWARNTag count decreased by more than expected between cycles, possibly due to Gamma API truncation.Emit with tag_count_drop warning; do not replace last full ontology.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_intel_marketontologybuilder_observations_emitted_totalcountercountObservationReports emitted (ontology change events).
polytraders_intel_marketontologybuilder_market_countgaugecountNumber of active markets indexed in the latest ontology build.
polytraders_intel_marketontologybuilder_category_countgaugecountNumber of distinct categories in the latest ontology build.

Alerts

AlertConditionSeverityRunbook
MarketOntologyBuilderStalerate(polytraders_intel_marketontologybuilder_observations_emitted_total[2h]) == 0warn#runbook-marketontologybuilder-stale
MarketOntologyBuilderTagCountDropdelta(polytraders_intel_marketontologybuilder_category_count[10m]) < -5warn#runbook-marketontologybuilder-tagcount-drop

Dashboards

  • Grafana — Intelligence / MarketOntologyBuilder market and category count over time

16. Developer Reporting

{
  "bot_id": "intel.marketontologybuilder",
  "ontology_hash": "0xdeadbeef12345678",
  "category_count": 18,
  "market_count": 312,
  "build_duration_ms": 4200,
  "ontology_changed": true,
  "killswitch_active": false
}

17. Plain-English Reporting

SituationUser-facing explanation
Strategy used category filter to avoid low-quality market segmentsThe system maintains a live map of market categories to help strategies focus on well-structured markets. Category filters are updated hourly.
Ontology update emitted after new markets addedNew markets were detected in a category during the last rebuild cycle. Category-aware strategies will see these markets in their next scan.

18. Failure-Mode Block

main_failure_modeGamma API outage for > 1 h causes MarketOntologyBuilder to serve a stale ontology, reducing category-aware strategy accuracy for markets that were newly listed during the outage.
false_positive_riskGamma API pagination bug returns a truncated market list, causing MARKETONTOLOGYBUILDER_TAG_COUNT_DROP to fire and retaining the previous (potentially stale) ontology.
false_negative_riskNew market category introduced during a build cycle is missed because the Gamma response is cached, causing the ontology to omit the new category until the next full rebuild.
safe_fallbackIf market_count < min_market_count hard floor, retain last full ontology and emit STALE_DATA WARN. Do not replace a valid ontology with a partial one.
required_dependenciesPolymarket Gamma API, KillSwitch active flag, Redis for ontology snapshot storage

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
GAMMA_TRUNCATED_RESPONSEReturn only 10 markets from mock Gamma API when 300+ expectedAutomatic on next successful full build
GAMMA_API_DOWNBlock Gamma API for 2 hFull rebuild triggered automatically on Gamma recovery
KILL_SWITCH_ONSet killswitch.active=true during ontology build cycleEmission resumes on KillSwitch reset

20. State & Persistence

Cold-start recovery

On cold start, trigger full rebuild on first cycle; last snapshot may be stale.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded periodic batch rebuild
Max in-flight1
Idempotency keyontology_hash
Per-call timeout (ms)30000
Backpressure strategyskip cycle if previous build still in progress
Locking / mutual exclusionRedis SETNX on build_lock key to prevent concurrent rebuilds

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchSuppress emissions when KillSwitch is active.

Emits to (downstream consumers)

BotWhyContract
strat.all_strategies

Sibling bots (same OrderIntent)

External services

ServiceEndpointSLA assumedOn failure
Polymarket Gamma APIhttps://gamma-api.polymarket.com99.9% / 500 ms p99

23. Security Surfaces

Abuse vectors considered

  • Gamma API returns a truncated market list causing category count drop and false TAG_COUNT_DROP alerts
  • Adversary adds misleading tags to markets to skew category ontology

Mitigations

  • TAG_COUNT_DROP guard prevents replacing good ontology with truncated data
  • Ontology is informational only; strategies validate independently before acting on category signals

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
NotesBuilds a structured ontology of Gamma market categories and relationships. 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
Full market list builds ontology and emits ObservationReport on hash change312 markets, 18 categories, ontology_hash differs from previousObservationReport emitted with category_count=18, market_count=312
Truncated response triggers WARN and retains last ontologyGamma returns 3 markets (below min_market_count hard=5)MARKETONTOLOGYBUILDER_TAG_COUNT_DROP WARN; last ontology retained; no rebuild
KillSwitch suppresses emissionkillswitch.active=true; valid ontology built internallyNo ObservationReport; KILL_SWITCH_ACTIVE logged

Integration Tests

TestExpected result
Ontology change detected and consumed by strategyStrategy receives updated ontology_hash and category_count after rebuild
Gamma API down: stale ontology retained; STALE_DATA emitted after 2 hSTALE_DATA WARN after 2 h; last valid ontology snapshot still accessible

Property Tests

PropertyRequired behaviour
MarketOntologyBuilder never submits or signs ordersAlways true
Ontology is never replaced with a build from fewer than min_market_count hard floor marketsAlways true

27. Operational Runbook

MarketOntologyBuilder incidents are typically Gamma API outages or truncated responses. Stale ontology does not block trading but may reduce category signal quality.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
MarketOntologyBuilderStaleCheck Gamma API health and last_build_at_ms. Trigger manual rebuild via override if API is healthy.Intelligence pod lead if stale for > 2 h
MarketOntologyBuilderTagCountDropCheck Gamma API response count. If truncated, do not replace ontology. Investigate Gamma pagination issues.Intelligence pod lead if drop is > 10 categories

Manual overrides

  • force_rebuild — POST /internal/marketontologybuilder/rebuild to trigger an immediate full rebuild — After Gamma API recovery or known category structure changes

Healthcheck

Endpoint: /internal/health/marketontologybuilder | Green: Last build < 2 h ago AND Redis reachable AND Gamma API returning 200 | Red: No successful build for > 2 h 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 tag-count drop guard, ontology hash change detection, and KillSwitch suppressionCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
Full ontology build completes in < 30 s on staging with 500 active marketsIntegration testBuild time < 30 s

Promote to General live

GateHow measuredThreshold
Zero truncation events over 14-day soak with live Gamma APIMarketOntologyBuilderTagCountDrop alert audit0 truncation alerts

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