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 LayerGovernance6.16 ExposureExplainer

6.16 ExposureExplainer

Governance Governance Explain PLANNED Spec ready capital · Indirect P7 · Governance & replay pending stub

Produces a plain-English narrative of current portfolio exposure: which markets, which outcomes, how much pUSD is at risk, why each position was opened (linked back to the originating strategy and OrderIntent). Read by the Admin UI, the daily ops digest, and any incident review.

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

LayerGovernance  Governance
Bot classGovernance
AuthorityExplain
StatusPLANNED
ReadinessSpec ready
Runs before
Runs afterexec.order_lifecycle_manager
Applies toContinuous
Default modeshadow
User-visibleYes
Developer ownerGovernance pod

Operational profile

OwnershipGovernance pod · on-call gov-oncall · #polytraders-gov · escalates to Head of Governance · P3
Latency budget1500ms
Modes supportedoffshadowadvisoryenforced
Data freshnessmax_market_data_age_ms=60000 · max_orderbook_age_ms=60000 · on stale → Emit fallback narrative; do not render misleading data.
Human overrideyes · by Governance on-call · logs GOV_EXPOSURE_OVERRIDE · time-bound: Single emission · scope: Single narrative · single approver

2. Purpose

Produces a plain-English narrative of current portfolio exposure: which markets, which outcomes, how much pUSD is at risk, why each position was opened (linked back to the originating strategy and OrderIntent). Read by the Admin UI, the daily ops digest, and any incident review.

3. Why This Bot Matters

  • Opaque positions

    A page of raw outcome IDs and notionals does not tell anyone whether the portfolio is balanced, concentrated, or running through resolution.

  • Audit trail

    When something goes wrong, the first question is always 'why was that position open?' — without ExposureExplainer, the answer requires correlating five logs by hand.

  • Daily review fatigue

    A narrative rendering compresses 200 line items into the 5 things a reviewer actually needs to read.

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
Resolved outcome positionsOn-chain CTFExchangeV2 + PortfolioGuardYesQuantities by token.
Market metadataPolymarket RESTYesFriendly market names and outcome labels.

5. Required Internal Inputs

InputSourceRequired?Use
Trade historyexec.order_lifecycle_managerYesOriginating intent_id and strategy_id per fill.
Strategy registryConfigYesMaps strategy_id to friendly description.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
digest_cadence_minutes60How often a fresh narrative is emitted to the ops feed.
concentration_warn_pct251525Single-market exposure (% of bankroll) at which the narrative flags concentration risk.

7. Detailed Parameter Instructions

digest_cadence_minutes

What it means

How often a fresh narrative is emitted to the ops feed.

Default

{ "digest_cadence_minutes": 60 }

Why this default matters

Hourly is the right grain for trading hours; daily for overnight.

Threshold logic

ConditionAction
60Default

Developer check

schedule.every(p.digest_cadence_minutes).do(emit_narrative);

User-facing English

(Internal — not user-facing.)

concentration_warn_pct

What it means

Single-market exposure (% of bankroll) at which the narrative flags concentration risk.

Default

{ "concentration_warn_pct": 25 }

Why this default matters

25% in one market is significant; flagging keeps reviewers honest.

Threshold logic

ConditionAction
≤ 15%Silent
15–25%MENTION
> 25%FLAG

Developer check

if (mkt.exposure_pct > p.concentration_warn_pct) flag(mkt);

User-facing English

(Internal — not user-facing.)

8. Default Configuration

{
  "digest_cadence_minutes": 60,
  "concentration_warn_pct": 25
}

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.

positions = portfolio.snapshot()
rows = [(p.strategy_id, p.market_id, p.outcome_id, p.size_usd) for p in positions]
rows.sort(key=lambda r: -r[3])
for r in rows:
  narrative.append(render_row(r))
  if exposure_pct(r) > p.concentration_warn_pct: flags.append(r.market_id)
emit('ExposureNarrative', narrative, flags)

11. Wire Examples

Input — what arrives on the wire

{
  "snapshot_ts_ms": 1715260000000,
  "positions": [
    {
      "strategy_id": "event_drift",
      "market_id": "0xabc",
      "outcome_id": "YES",
      "size_usd": 1200
    }
  ]
}

Output — what the bot emits

{
  "kind": "ExposureNarrative",
  "summary_markdown": "## Exposure as of 14:00 UTC\n- Strategy 'event_drift' is long $1,200 on YES in market 0xabc.\n",
  "concentration_flags": []
}

12. Decision Logic

APPROVE

Group by strategy → market → outcome. Annotate each row with originating intent and decision rationale.

RESHAPE_REQUIRED

This bot does not reshape orders.

REJECT

No reject path defined for this bot — it is observe-only.

WARNING_ONLY

Highlight concentration above warn threshold.

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "kind": "ExposureNarrative",
  "summary_markdown": "## Exposure as of 14:00 UTC\n- Strategy 'event_drift' is long $1,200 on YES in market 0xabc (Trump Q1 announcement); concentration 18%.\n- Strategy 'late_unwind' is short $400 on NO in market 0xdef.\n",
  "concentration_flags": []
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
GOV_EXPOSURE_NARRATIVEP3Gov Exposure NarrativeSee decision output and developer log for context.Hourly summary of every open position the system holds, in plain language.
GOV_EXPOSURE_CONCENTRATION_FLAGP3Gov Exposure Concentration FlagSee decision output and developer log for context.Hourly summary of every open position the system holds, in plain language.
GOV_EXPOSURE_FALLBACKP3Gov Exposure FallbackSee decision output and developer log for context.Hourly summary of every open position the system holds, in plain language.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
narratives_emitted_totalcountereventbot_idNarratives emitted total.
concentration_flags_totalcountereventbot_idConcentration flags total.
narrative_render_failures_totalcountereventbot_idNarrative render failures total.

Dashboards

  • 6.16 overview dashboard

16. Developer Reporting

"Per emission: digest_id, ts_ms, position_count, total_exposure_usd, flagged_markets."

17. Plain-English Reporting

SituationUser-facing explanation
When this bot actsHourly summary of every open position the system holds, in plain language.

18. Failure-Mode Block

main_failure_modeStale strategy registry produces narratives that label positions with the wrong strategy.
false_positive_riskConcentration flag fires for a position that is intentionally large; mitigation: allow per-strategy whitelist with required reviewer notes.
false_negative_riskOn-chain position is held outside the standard portfolio path and is missed; mitigation: reconcile against on-chain CTFExchangeV2 token balances every digest.
safe_fallbackOn any data fetch failure, emit a narrative containing only the failure summary plus the last successful narrative's timestamp. Never produce a misleading narrative.
required_dependencies

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
Drop the strategy registry and assert fallback narrative rendersDrop the strategy registry and assert fallback narrative renders.Bot detects within its latency budget and emits the corresponding reason code.Remove the injected fault; bot returns to healthy state within one debounce window.
Inject a position with no originating intent and assert it is flagged as ORPHANInject a position with no originating intent and assert it is flagged as ORPHAN.Bot detects within its latency budget and emits the corresponding reason code.Remove the injected fault; bot returns to healthy state within one debounce window.

20. State & Persistence

Last successful narrative cached for fallback emission.

State stores

NameKindKeyValue shapeTTLDurability
exposure_explainer_statein-memory + fast KV mirrorbot_idLast successful narrative cached for fallback emission.24hcrash-safe via KV mirror

Cold-start recovery

Cold-start hydrates from fast KV; missing keys default to safe fallback.

On restart

All in-flight decisions are re-evaluated; no bot decision is trusted across restart without re-emit.

21. Concurrency & Idempotency

AspectSpecification
Execution modelSingle emitter. Reads upstream snapshots only.
Max in-flight32
Idempotency keyorder_intent_id
Replay-safeTrue
DeduplicationBy idempotency_key within a 60s window.
Ordering guaranteesPer-market_id FIFO; cross-market unordered.
Per-call timeout (ms)250
Backpressure strategyBounded queue; oldest-dropped with metric increment when full.
Locking / mutual exclusionPer-market_id mutex; no global locks.

22. Dependencies

Depends on (must run first)

Requires (graph.requires)

exec.order_lifecycle_manager

ConsumesPortfolioSnapshot TradeHistory
EmitsOperationsReport(kind=ExposureNarrative)
Blocks ordersno

23. Security Surfaces

Read-only on portfolio + history. Emits to internal ops feed only.

Signing surface

None — bot does not sign or submit.

Mitigations

  • Rate-limit per source
  • Audit-log every override
  • Require role-based authz on admin paths

24. Polymarket V2 Compatibility

AspectValue
CLOB versionV2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldyes
Aware of negative-risk marketsyes
Multi-chain readyyes
SDK usedPolymarket CLOB V2 SDK
Settlement contractCTFExchangeV2
NotesReads V2 token balances and lifecycle state.

25. Versioning & Migration

FieldValue
current0.1.0
contract_version1.0.0
last_breaking_changenone
deprecation_window_days30

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Concentration > 25% flags the market.Synthetic fixture per template.Behaviour matches the rule described in the test name.
Empty portfolio renders 'No open positions.'.Synthetic fixture per template.Behaviour matches the rule described in the test name.

Integration Tests

TestExpected result
Run against a recorded portfolio snapshot; the rendered narrative matches the golden file.End-to-end behaviour matches the spec without manual intervention.

Property Tests

PropertyRequired behaviour
Narrative line count is bounded by position_count + flagged_markets + 5.Always true across all generated inputs.

27. Operational Runbook

If narratives stop emitting: check the digest scheduler heartbeat in monitoring.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
6.16_anomalyOpen the bot's reporting page and confirm the alert is real (not a metric hiccup).Inspect developer log entries for the affected market_id over the last 30 minutes.Force-clear via Admin UI if the rule is clearly stale; otherwise leave engaged and notify owner.Governance pod

Manual overrides

  • polytraders bot pause 6.16 — Disables the bot's enforcement layer; downstream consumers fall back to safe defaults.

Healthcheck

GET /healthz/exposure_explainer → 200 if last successful evaluation < 60s ago.

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
Stubgolden-file diff.Documented threshold met for the full window.

Promote to Limited live

GateHow measuredThreshold
Shadow14 days; narratives reviewed by ops.Documented threshold met for the full window.
Advisory7 days.Documented threshold met for the full window.

Promote to General live

GateHow measuredThreshold
Enforcedfeeds the daily ops digest.Documented threshold met for the full window.

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