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 LayerStrategy3.20 NarrativeCrowdingFade

3.20 NarrativeCrowdingFade

Strategy Alpha Strategy Trade PLANNED Spec started capital · Direct P8 · Additional strategies pending stub

NarrativeCrowdingFade detects Polymarket binary markets where social attention and narrative momentum have pushed prices above the statistical base rate. When attention Z-score and price drift signal crowding, the bot fades the move — taking the opposite position to capture reversion toward the base rate.

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

LayerStrategy  Strategy
Bot classAlpha Strategy
AuthorityTrade
StatusPLANNED
ReadinessSpec started
Runs beforeRisk guardrail pipeline
Runs afterObservation bus / internal analytics
Applies toPolymarket binary markets where attention Z-score >= min_attention_zscore and price drift from base rate >= min_drift_bps, indicating narrative crowding above statistical base rate
Default modeshadow_only
User-visibleAdvanced details only
Developer ownerPolytraders core — Strategy pod

2. Purpose

NarrativeCrowdingFade detects Polymarket binary markets where social attention and narrative momentum have pushed prices above the statistical base rate. When attention Z-score and price drift signal crowding, the bot fades the move — taking the opposite position to capture reversion toward the base rate.

3. Why This Bot Matters

  • News is genuinely paradigm-shifting

    If the crowded narrative reflects real new information rather than sentiment overshoot, the fade position loses as the market price correctly adjusts to the new fundamental.

  • Stale input data

    Acting on stale signals for NarrativeCrowdingFade produces trades based on outdated market conditions, generating adverse fills.

  • KillSwitch not respected

    Emitting OrderIntents while KillSwitch is active bypasses risk controls.

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
CLOB book (mid, depth, spread)ws_marketYesRead current market price and available depth for order sizing.
Market status (open/closed/resolved)clob_publicYesSkip closed or resolved markets.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagKillSwitchYesAbort all intent emission if KillSwitch active.
NarrativeCrowdingFade analytics signalinternal (analytics engine)YesProvides the core NarrativeCrowdingFade signal that drives trade decisions.
Builder code bytes32internal configYesInjected into builder field on every signed V2 OrderIntent.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
min_attention_zscore2.01.51.0Minimum Z-score of attention (volume, social mentions) relative to market history before the bot considers a fade trade.
min_drift_bps20010050Minimum price drift in bps from the base rate required before the bot fades the move.
max_position_per_event400600800Maximum pUSD per event for narrative fade positions.
cool_off_after_news3001200Seconds to pause after a major news hit before re-evaluating the fade thesis.

7. Detailed Parameter Instructions

min_attention_zscore

What it means

Minimum Z-score of attention (volume, social mentions) relative to market history before the bot considers a fade trade.

Default

{ "min_attention_zscore": 2.0 }

Why this default matters

Z-score >= 2.0 indicates statistically significant crowding, not normal attention.

Threshold logic

ConditionAction
>= 2.0Allow fade evaluation
1.5–2.0WARN NCF_WEAK_CROWDING; halve size
< 1.0SKIP NCF_ATTENTION_BELOW_FLOOR

Developer check

if attention_zscore < params.hard: return skip('NCF_ATTENTION_BELOW_FLOOR')

User-facing English

The attention signal was not strong enough to indicate crowding.

min_drift_bps

What it means

Minimum price drift in bps from the base rate required before the bot fades the move.

Default

{ "min_drift_bps": 200 }

Why this default matters

200 bps ensures the market has meaningfully overshot the base rate before fading.

Threshold logic

ConditionAction
>= 200 bpsEMIT fade IOC
100–200 bpsWARN NCF_DRIFT_MARGINAL; halve size
< 50 bpsSKIP NCF_NO_DRIFT

Developer check

if drift_bps < params.hard: return skip('NCF_NO_DRIFT')

User-facing English

The price drift was too small to justify a fade trade.

max_position_per_event

What it means

Maximum pUSD per event for narrative fade positions.

Default

{ "max_position_per_event": 400 }

Why this default matters

400 pUSD limits single-event narrative exposure.

Threshold logic

ConditionAction
<= 400 pUSDNormal sizing
> 800 pUSDReject config

Developer check

if params.max_position_per_event > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')

User-facing English

Position capped at per-event maximum.

cool_off_after_news

What it means

Seconds to pause after a major news hit before re-evaluating the fade thesis.

Default

{ "cool_off_after_news": 300 }

Why this default matters

300s cool-off allows initial price discovery before the fade re-enters.

Threshold logic

ConditionAction
>= 300sStandard cool-off
< 120sWARN NCF_SHORT_COOLOFF

Developer check

if in_cooloff(market_id): return skip('NCF_COOLOFF_ACTIVE')

User-facing English

A brief pause is in effect after a major news event.

8. Default Configuration

{
  "bot_id": "strat.narrativecrowdingfade",
  "version": "0.1.0",
  "mode": "shadow_only",
  "defaults": {
    "min_attention_zscore": 2.0,
    "min_drift_bps": 200,
    "max_position_per_event": 400,
    "cool_off_after_news": 300
  },
  "locked": {
    "min_attention_zscore": {
      "min": 1.0
    },
    "min_drift_bps": {
      "min": 50
    },
    "max_position_per_event": {
      "min": 800
    },
    "cool_off_after_news": {
      "min": 0
    }
  }
}

9. Implementation Flow

  1. Check KillSwitch; if active, emit no OrderIntents.
  2. FETCH NarrativeCrowdingFade analytics signal from internal engine.
  3. IF signal below hard floor: SKIP, emit sampled DecisionReport NCF_NO_EDGE.
  4. FETCH clob_public market status; skip if closed or resolved.
  5. FETCH ws_market book; compute current mid and available depth.
  6. IF signal < warning threshold: WARN NCF_MARGINAL; reduce size 50%.
  7. Compute order size = min(max_size_param, available_depth).
  8. EMIT IOC OrderIntent with builder code.
  9. EMIT DecisionReport with intent_emitted=true, reason=NCF_TRADE.

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

  // Hard floor gate
  IF signal.score < params.min_attention_zscore_hard:
    IF random() < 0.01:
      EMIT DecisionReport(intent_emitted=false, reason='NCF_NO_EDGE')
    RETURN

  mkt = FETCH clob_public.GET('/markets/' + market_id)
  IF mkt.closed OR mkt.resolved: RETURN

  // Warning threshold check
  sizeMultiplier = 0.5 IF signal.score < params.min_attention_zscore_warn ELSE 1.0
  IF sizeMultiplier < 1.0: WARN('NCF_MARGINAL')

  // Book snapshot
  book = FETCH ws_market.book(market_id)
  mid = (book.best_bid + book.best_ask) / 2
  depth = FETCH clob_public.depth(market_id)

  // Size computation
  orderSize = toPusdUnits(min(params.min_drift_bps * sizeMultiplier, depth.available))

  EMIT OrderIntent(market=market_id, outcome='YES', side='buy', price=mid,
                   size_pUSD=orderSize, tif='IOC', builder=internalBuilderCode)
  EMIT DecisionReport(intent_emitted=true, signal_score=signal.score,
                      reason='NCF_TRADE')

SDK calls used

  • ws_market.subscribe('book', [market_id])
  • fetchClobPublic('/markets/' + market_id)
  • internal.analyticsEngine.signal(market_id)
  • buildOrderTypedData(orderParams, {name:'CTFExchange', version:'2', chainId:137})
  • internal.builder_code

Complexity: O(1) per signal update per market

11. Wire Examples

Input — what arrives on the wire

NarrativeCrowdingFade analytics signalinternal (analytics engine)

{
  "market_id": "0xnarrativ000000000000000000000000000000000000000000000000000000000001",
  "signal_score": "0.75",
  "received_at_ms": 1746790800000
}

Output — what the bot emits

OrderIntent — NarrativeCrowdingFade IOC buy YES

{
  "intent_id": "oi_01HNCF0000001A",
  "market_id": "0xnarrativ000000000000000000000000000000000000000000000000000000000001",
  "outcome": "YES",
  "side": "buy",
  "price": "0.540",
  "size_pUSD": "200.00",
  "tif": "IOC",
  "builder": {
    "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
    "fee_bps": 25
  },
  "decision": {
    "signal_score": 0.75,
    "reasons": [
      "NCF_TRADE"
    ]
  }
}

12. Decision Logic

APPROVE

All gates passed, KillSwitch inactive, market open. Emit IOC OrderIntent.

RESHAPE_REQUIRED

Not applicable — reshaping handled by downstream Risk guardrail.

REJECT

Signal below hard floor; stale data; market closed; KillSwitch active.

WARNING_ONLY

Signal in warning zone triggers 50% size reduction.

13. Standard Decision Output

This bot returns a OrderIntent object. See OrderIntent schema.

{
  "intent_id": "oi_01HNCF0000001A",
  "trace_id": "tr_01HNCF000TR001",
  "market_id": "0xnarrativ000000000000000000000000000000000000000000000000000000000001",
  "outcome": "YES",
  "side": "buy",
  "price": "0.540",
  "size_pUSD": "200.00",
  "tif": "IOC",
  "post_only": false,
  "builder": {
    "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
    "fee_bps": 25
  },
  "negrisk_aware": false,
  "decision": {
    "signal_score": 0.75,
    "reasons": [
      "NCF_TRADE"
    ]
  },
  "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
NCF_TRADEINFOAll gates passed. IOC OrderIntent emitted for NarrativeCrowdingFade.Emit IOC OrderIntent.A NarrativeCrowdingFade trade was placed.
NCF_MARGINALWARNEdge is within the warning threshold; size reduced 50%.Emit at 50% size; log warning.A small edge was found; a reduced-size NarrativeCrowdingFade trade was placed.
NCF_NO_EDGEINFOEdge below hard floor. Skipping.Skip; emit sampled DecisionReport.The edge was too small to justify a trade.
NCF_HARD_REJECTHARD_REJECTA critical gate condition blocked the trade (stale data, kill switch, or hard parameter breach).Skip; no OrderIntent.A safety condition blocked the trade.
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch is active.Skip all markets; no OrderIntents emitted.Trading is currently paused.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_strat_narrativecrowdingfade_decisions_totalcountercountverdict, reason_codeTotal evaluation cycles by verdict and reason code.
polytraders_strat_narrativecrowdingfade_signal_scorehistogramscoreDistribution of analytics signal scores at evaluation.
polytraders_strat_narrativecrowdingfade_intents_emitted_totalcountercountoutcomeTotal IOC OrderIntents emitted.
polytraders_strat_narrativecrowdingfade_eval_latency_mshistogrammillisecondsLatency from signal receipt to OrderIntent emit.

Alerts

AlertConditionSeverityRunbook
NarrativeCrowdingFadeStaleFeedrate(polytraders_strat_narrativecrowdingfade_decisions_total{reason_code='STALE_MARKET_DATA'}[5m]) > 0.1warn#runbook-narrativecrowdingfade-stale
NarrativeCrowdingFadeKillSwitchrate(polytraders_strat_narrativecrowdingfade_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0page#runbook-killswitch
NarrativeCrowdingFadeNoEdgerate(polytraders_strat_narrativecrowdingfade_decisions_total{verdict='skip',reason_code='NCF_NO_EDGE'}[10m]) / rate(polytraders_strat_narrativecrowdingfade_decisions_total[10m]) > 0.95warn#runbook-narrativecrowdingfade-edge

16. Developer Reporting

{
  "bot_id": "strat.narrativecrowdingfade",
  "market_id": "0xnarrativ000000000000000000000000000000000000000000000000000000000001",
  "signal_score": 0.75,
  "intent_emitted": true,
  "reason": "NCF_TRADE",
  "emitted_at_ms": 1746790800000
}

17. Plain-English Reporting

SituationUser-facing explanation
NarrativeCrowdingFade trade placedThe NarrativeCrowdingFade strategy detected a suitable opportunity and placed a trade.
Edge too small — no tradeThe signal was below the minimum threshold. No trade was placed.
Safety gate active — no tradeA safety condition (stale data, kill switch, or parameter limit) blocked the trade.

18. Failure-Mode Block

main_failure_modeIf the crowded narrative reflects real new information rather than sentiment overshoot, the fade position loses as the market price correctly adjusts to the new fundamental.
false_positive_riskSignal mis-fires when market conditions change rapidly, producing trades that quickly move against the NarrativeCrowdingFade thesis.
false_negative_riskHard floor set too conservatively misses genuine opportunities.
safe_fallbackIf ws_market feed stale or analytics signal unavailable, skip without emitting any OrderIntent.
required_dependenciesws_market, clob_public, internal NarrativeCrowdingFade analytics engine, KillSwitch, internal builder code

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
SIGNAL_UNAVAILABLECut internal analytics engine connectionAutomatic when engine reconnects.
HARD_FLOOR_BREACHInject signal below hard floorAutomatic on next valid signal.
KILL_SWITCH_ONSet killswitch.active=trueAutomatic on manual KillSwitch reset.

20. State & Persistence

Cold-start recovery

On cold start, signals rebuilt from next analytics engine poll.

21. Concurrency & Idempotency

AspectSpecification
Execution modelactor-per-market
Max in-flight25
Idempotency keyintent_id
Per-call timeout (ms)300
Backpressure strategydrop oldest signal per market_id when queue > 2
Locking / mutual exclusionper-market_id mutex for signal state

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchChecked first; blocks all intent emission when active.

Emits to (downstream consumers)

External services

ServiceEndpointSLA assumedOn failure
Polymarket CLOB WebSocket (ws_market)best-effort
Internal NarrativeCrowdingFade analytics engineinternal SLA

23. Security Surfaces

Abuse vectors considered

  • Signal injection to produce false NarrativeCrowdingFade trades
  • Order sizing parameter manipulation to exceed position limits

Mitigations

  • NarrativeCrowdingFade analytics signals sourced from authenticated internal engine only
  • Hard limits on position size enforced before OrderIntent emission
  • Builder code injected from secure internal config

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldyes
Aware of negative-risk marketsno
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesBot not yet implemented; designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain). feeRateBps not present on any signed OrderIntent.

API surfaces declared

clob_publicclob_authws_marketinternal

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
Emit IOC when signal=0.75 and all gates passstandard configIOC OrderIntent; reason=NCF_TRADE
Skip when signal below hard floorsignal=below_hardNo OrderIntent; sampled reason=NCF_NO_EDGE
Skip when KillSwitch activekillswitch.active=trueNo OrderIntents emitted

Integration Tests

TestExpected result
Full cycle: signal → computation → IOC OrderIntent on Polygon testnetOrder has builder.code, no feeRateBps, EIP-712 domain v2

Property Tests

PropertyRequired behaviour
Bot never emits OrderIntent when KillSwitch is activeAlways true
feeRateBps never present on any signed OrderIntentAlways true

27. Operational Runbook

NarrativeCrowdingFade incidents are typically stale analytics feeds or kill-switch activations. Hard-floor skips are normal.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
NarrativeCrowdingFadeStaleFeed
NarrativeCrowdingFadeKillSwitch
NarrativeCrowdingFadeNoEdge

Manual overrides

Healthcheck

GET /internal/health/narrativecrowdingfade -> 200 if Analytics engine active; signal age < 60s; KillSwitch inactive.. Red: Analytics engine down or KillSwitch active..

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 including hard-floor skip and kill-switch blockCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
p99 eval latency < 300ms over 24h shadow runpolytraders_strat_narrativecrowdingfade_eval_latency_ms histogramp99 < 300ms

Promote to General live

GateHow measuredThreshold
E2E: signal → computation → IOC OrderIntent on Polygon testnet with builder.code and no feeRateBpsE2E testPass

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