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 LayerDiscovery0.3 EventCalendarMapper

0.3 EventCalendarMapper

Discovery Signal Service Read-onlyRecommend PLANNED Spec started capital · Indirect P2 · Data normalisation pending stub

Map every Polymarket market to a known real-world calendar event (elections, sports fixtures, court dates, FOMC releases, earnings, debates) and emit a time-tagged ObservationReport so strategies can act on pre-event positioning logic.

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

LayerDiscovery  Discovery
Bot classSignal Service
AuthorityRead-onlyRecommend
StatusPLANNED
ReadinessSpec started
Runs beforeStrategy OrderIntent generation
Runs afterMarketScanner scan cycle
Applies toAll live Polymarket markets with a known resolution horizon
Default modeshadow_only
User-visibleAdvanced details only
Developer ownerPolytraders core — Intelligence pod

2. Purpose

Map every Polymarket market to a known real-world calendar event (elections, sports fixtures, court dates, FOMC releases, earnings, debates) and emit a time-tagged ObservationReport so strategies can act on pre-event positioning logic.

3. Why This Bot Matters

  • Markets not linked to calendar events

    Strategies cannot distinguish between a stagnant market and one where a price-moving event is imminent, missing pre-event edge opportunities.

  • Stale calendar data

    Rescheduled or cancelled events leave strategies positioned on a timeline that no longer exists.

No worked examples on this bot yet. Worked examples are optional but strongly recommended — they turn an abstract failure mode into something a developer can verify in a fixture.

4. Required Polymarket Inputs

InputSourceRequired?Use
Market resolution date, question text, and linked event identifiersGamma APIYesPrimary source for matching markets to calendar events via resolution date and question NLP.
Market condition_id and neg-risk flagGamma APIYesIdentify neg-risk bundles that share a common real-world event.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagrisk.kill_switchYesSuppress emissions when KillSwitch is active.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
event_lookahead_h72246How many hours ahead the mapper looks for calendar events to associate with markets.
min_event_relevance0.60.40.2Minimum NLP match score between market question and calendar event description for a mapping to be accepted.

7. Detailed Parameter Instructions

event_lookahead_h

What it means

How many hours ahead the mapper looks for calendar events to associate with markets.

Default

{ "event_lookahead_h": 72 }

Why this default matters

72 hours gives strategies enough lead time to build or unwind pre-event positions without excessive false positives.

Threshold logic

ConditionAction
>= 72hStandard lookahead
24–72hNarrow window — WARN
< 6hReject — too short for pre-event positioning

Developer check

if (h < params.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Markets linked to events happening within the next few days are flagged for time-sensitive strategies.

min_event_relevance

What it means

Minimum NLP match score between market question and calendar event description for a mapping to be accepted.

Default

{ "min_event_relevance": 0.6 }

Why this default matters

A relevance floor of 0.6 prevents spurious mappings while allowing reasonable headline variations.

Threshold logic

ConditionAction
>= 0.6Map accepted
0.4–0.6Map accepted with LOW_RELEVANCE flag
< 0.2Map rejected — CALENDAR_MISMATCH

Developer check

if (score < params.hard) emit(HARD_REJECT, 'CALENDAR_MISMATCH');

User-facing English

Markets are only linked to calendar events when there is a strong match between the market question and the event.

8. Default Configuration

{
  "bot_id": "disc.event_calendar_mapper",
  "version": "0.1.0",
  "mode": "shadow_only",
  "defaults": {
    "event_lookahead_h": 72,
    "enabled_calendars": [
      "elections",
      "sports",
      "macro",
      "earnings"
    ],
    "timezone_default": "UTC",
    "min_event_relevance": 0.6
  }
}

9. Implementation Flow

  1. On each mapping cycle, fetch all active markets from Gamma API.
  2. For each market, extract resolution date and normalise to UTC.
  3. Filter to markets with resolution date within event_lookahead_h.
  4. For each candidate, run NLP title-match against each enabled calendar feed to find the best-matching event.
  5. Accept mappings where match_score >= min_event_relevance; reject below hard floor with CALENDAR_MISMATCH.
  6. Emit ObservationReport with market_id, event_id, calendar_source, match_score, event_time_utc, hours_to_event.
  7. Log cycle summary with total markets mapped, unmatched count, and top mapping by relevance.

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 mappingCycle():
  ks = FETCH internal.killswitch.status
  IF ks.active: RETURN

  markets = FETCH gamma.GET('/markets?active=true&closed=false')
  IF markets IS NULL:
    LOG ERROR 'Gamma API unavailable — halting mapping cycle'
    RETURN

  now_utc = current_time_utc()
  horizon_cutoff = now_utc + params.event_lookahead_h * 3600

  FOR market IN markets:
    res_time = parse_utc(market.resolution_date)
    IF res_time > horizon_cutoff: CONTINUE  // outside lookahead

    best_match = NULL; best_score = 0.0
    FOR calendar IN params.enabled_calendars:
      events = calendarFeed(calendar, res_time)
      FOR event IN events:
        score = nlpMatch(market.question, event.description)
        IF score > best_score:
          best_match = event; best_score = score

    IF best_score < params.min_event_relevance.hard:
      LOG reason=CALENDAR_MISMATCH; CONTINUE

    warnings = []
    IF best_score < params.min_event_relevance.default:
      warnings.append('LOW_RELEVANCE')

    hours_to_event = (parse_utc(best_match.time) - now_utc) / 3600
    EMIT ObservationReport(market_id, best_match.id, best_match.calendar,
                           best_score, hours_to_event, warnings)

  LOG cycle summary

SDK calls used

  • gamma.GET('/markets?active=true&closed=false')
  • calendarFeed('elections', resolution_date)
  • nlpMatch(market.question, event.description)

Complexity: O(M × C × E) where M=markets, C=calendars, E=events per calendar window

11. Wire Examples

Input — what arrives on the wire

Gamma market with election resolution dategamma_api

{
  "condition_id": "0x8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b",
  "question": "Will Candidate A win the 2026 midterm election?",
  "active": true,
  "resolution_date": "2026-11-04T00:00:00Z",
  "neg_risk": false
}

Output — what the bot emits

ObservationReport — election market mapped

{
  "report_id": "0xccdd2233445566778899001122334455ccdd2233445566778899001122334455",
  "bot_id": "disc.event_calendar_mapper",
  "market_id": "0x8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b",
  "event_id": "us-election-2026-nov-03",
  "calendar_source": "elections",
  "match_score": 0.87,
  "event_time_utc": "2026-11-03T23:00:00Z",
  "hours_to_event": 48.5,
  "warnings": [],
  "mapped_at_ms": 1746789000000
}

Reproduce locally

curl 'https://gamma-api.polymarket.com/markets?active=true&closed=false&limit=100'

12. Decision Logic

APPROVE

Not applicable — EventCalendarMapper emits ObservationReports, not approvals.

RESHAPE_REQUIRED

Not applicable — read-only mapping bot.

REJECT

Markets that cannot be matched to any calendar event above the hard floor receive CALENDAR_MISMATCH and are not forwarded.

WARNING_ONLY

Markets matched with relevance between warning and default receive LOW_RELEVANCE annotation.

13. Standard Decision Output

This bot returns a ObservationReport object. See ObservationReport schema.

{
  "report_id": "0xccdd2233445566778899001122334455ccdd2233445566778899001122334455",
  "bot_id": "disc.event_calendar_mapper",
  "market_id": "0x8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b",
  "event_id": "us-election-2026-nov-03",
  "calendar_source": "elections",
  "match_score": 0.87,
  "event_time_utc": "2026-11-03T23:00:00Z",
  "hours_to_event": 48.5,
  "warnings": [],
  "mapped_at_ms": 1746789000000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
CALENDAR_MISMATCHHARD_REJECTNo calendar event matched above the hard relevance floor; market not mapped.Exclude from mapping output; log market_id and best attempt score.This market could not be linked to any known upcoming event.
LOW_RELEVANCEWARNBest calendar match is between warning and default threshold.Include mapping with LOW_RELEVANCE warning annotation.This market is tentatively linked to an event but the match is not high-confidence.
STALE_MARKET_DATAHARD_REJECTGamma API unavailable; mapping cycle halted.Halt cycle; retry on next interval.
KILL_SWITCH_ACTIVEHARD_REJECTKillSwitch is active; all emissions suppressed.Return immediately; do not emit any ObservationReports.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_disc_eventcalendarmapper_markets_evaluated_totalcountercountcycleTotal markets evaluated per mapping cycle.
polytraders_disc_eventcalendarmapper_mappings_emitted_totalcountercountcalendar_sourceSuccessful calendar mappings emitted, broken down by calendar source.
polytraders_disc_eventcalendarmapper_match_scorehistogramratioDistribution of NLP match scores for accepted mappings.

Alerts

AlertConditionSeverityRunbook
EventCalendarMapperNoMappingsrate(polytraders_disc_eventcalendarmapper_mappings_emitted_total[30m]) == 0P2#runbook-eventcalendarmapper-no-mappings
EventCalendarMapperGammaAPIDownrate(polytraders_disc_eventcalendarmapper_markets_evaluated_total[5m]) == 0P1#runbook-eventcalendarmapper-gamma-api

Dashboards

  • Grafana — Discovery / EventCalendarMapper mapping coverage

Log levels

LevelWhat gets logged
DEBUGPer-market best match event, score, and calendar source.
INFOCycle summary: markets_evaluated, mappings_emitted.
WARNLow mapping rate (<5%); calendar feed returning empty.
ERRORGamma API unavailable; all calendar feeds unreachable.

16. Developer Reporting

{
  "bot_id": "disc.event_calendar_mapper",
  "cycle": 11,
  "markets_evaluated": 312,
  "markets_mapped": 24,
  "markets_unmatched": 288,
  "top_mapping": "0x8a9b... \u2192 us-election-2026-nov-03 (score 0.87)",
  "killswitch_active": false,
  "mapped_at": "2026-05-09T11:30:00Z"
}

17. Plain-English Reporting

SituationUser-facing explanation
Market shown with an upcoming event tagThis market is linked to a real-world event happening soon. Strategies that are time-sensitive will consider it for pre-event positioning.
Market not linked to any eventNo matching calendar event was found for this market within the current lookahead window.

18. Failure-Mode Block

main_failure_modeNLP title-match produces a false positive mapping linking a market to the wrong event, causing strategies to apply incorrect pre-event timing logic.
false_positive_riskA market with a generic title (e.g. 'Will X win?') may incorrectly match multiple calendar events, causing the bot to emit duplicate conflicting mappings.
false_negative_riskMarkets with non-standard question phrasing may not match any calendar event above threshold, missing genuine pre-event signals.
safe_fallbackIf Gamma API is unavailable, halt the mapping cycle with STALE_MARKET_DATA rather than emitting stale mappings.
required_dependenciesGamma API live market list, Enabled calendar feed data, KillSwitch active flag

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
GAMMA_API_DOWNBlock TCP to gamma-api.polymarket.comAutomatic on next cycle.
ALL_CALENDARS_EMPTYReturn empty event lists from all calendar feedsAutomatic when calendar feeds return data.
KILL_SWITCH_ONSet killswitch.active=trueEmissions resume after KillSwitch reset.

20. State & Persistence

Cold-start recovery

On cold start, calendar feeds are re-fetched on first cycle.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded async loop
Max in-flight1
Idempotency keymapping_cycle_id
Per-call timeout (ms)10000
Backpressure strategydrop newest
Locking / mutual exclusionnone

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchKillSwitch gate suppresses emissions.If active, mapping runs but no reports emitted.

Emits to (downstream consumers)

BotWhyContract
disc.opportunityqueueOpportunityQueue uses event proximity to prioritise opportunities.ObservationReport includes hours_to_event and event_id.

Sibling bots (same OrderIntent)

BotWhyContract
disc.marketscanner

External services

ServiceEndpointSLA assumedOn failure
Gamma APIhttps://gamma-api.polymarket.com99.9% / 500ms p99Halt cycle; retry next interval.
Calendar feed APIsVarious (elections, sports, FRED, earnings)best-effortSkip affected calendar source; log warning.

23. Security Surfaces

Abuse vectors considered

  • External calendar feed returning crafted event data to create false market mappings

Mitigations

  • NLP match score threshold prevents low-confidence mappings from propagating
  • All outputs are ObservationReports (read-only recommendations); downstream bots independently validate

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
NotesGamma API enableNegRisk flag used to group neg-risk bundles under a shared event_id. All timing uses UTC resolution dates from Gamma V2 market metadata.

API surfaces declared

gammadatainternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation0.1.0
schema2
releasedNone
planned_releaseQ4-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
Mapping below hard floor emits CALENDAR_MISMATCHmatch_score=0.15, hard=0.2Market excluded from mapping output with CALENDAR_MISMATCH reason
Mapping in warning band gets LOW_RELEVANCE flagmatch_score=0.5, warning=0.4, default=0.6ObservationReport emitted with warnings=['LOW_RELEVANCE']
KillSwitch suppresses emissionskillswitch.active=trueNo ObservationReports emitted

Integration Tests

TestExpected result
Election market correctly maps to election calendar eventObservationReport with calendar_source='elections' and hours_to_event > 0
Gamma API outage halts cycle with STALE_MARKET_DATANo reports emitted; resumes next cycle

Property Tests

PropertyRequired behaviour
hours_to_event always > 0 at time of emissionAlways true — past events are not mapped
No report emitted when KillSwitch activeAlways true

27. Operational Runbook

EventCalendarMapper incidents are typically calendar feed outages or NLP threshold misconfiguration. Bot is read-only; incidents do not affect active positions.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
EventCalendarMapperNoMappings
EventCalendarMapperGammaAPIDown

Manual overrides

Healthcheck

GET /internal/health/eventcalendarmapper → green if Last cycle completed within 2× mapping interval; at least one event mapped in last 24h.; red if No cycle completed in 2× interval or zero mappings in 24h during active markets.

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
NLP match correctly identifies top-5 market categories in test suiteUnit test suite≥4/5 correct

Promote to Limited live

GateHow measuredThreshold
Mapping coverage ≥10% of active markets over 48h shadow runpolytraders_disc_eventcalendarmapper_mappings_emitted_total≥10%

Promote to General live

GateHow measuredThreshold
No false-positive mappings detected via human spot-check of 50 samplesManual review≤2 false positives

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