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.2 Maker-Wide

3.2 Maker-Wide

Strategy Alpha Strategy Trade BETA Limited live capital · Direct P8 · Additional strategies pending stub

Maker-Wide is a passive market-making strategy designed for thin, illiquid binary markets where the natural spread is wider than Maker-Tight's target. It posts resting maker quotes at a wider edge (80–200 bps from mid), a smaller clip size, and with a longer expected holding period. The stale-book detector (time since last fill > stale_book_minutes) prevents continuous requoting on markets with no real two-way flow. Volatility of mid over the last hour determines the spread width dynamically: higher vol → wider edge. This is a user-controlled liquidity-provision tool. Maker rebates (20–25% of platform fees, paid in pUSD) contribute to the return profile. No performance claims are made.

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
StatusBETA
ReadinessLimited live
Runs beforeRisk guardrail pipeline
Runs afterMarket scanner / opportunity feed
Applies toThin-book binary markets where 24h volume < 250,000 pUSD, spread > 2 * edge_bps, and no stale-book condition is detected (time since last fill < stale_book_minutes)
Default modelimited_live
User-visibleAdvanced details only
Developer ownerPolytraders core — Strategy pod

2. Purpose

Maker-Wide is a passive market-making strategy designed for thin, illiquid binary markets where the natural spread is wider than Maker-Tight's target. It posts resting maker quotes at a wider edge (80–200 bps from mid), a smaller clip size, and with a longer expected holding period. The stale-book detector (time since last fill > stale_book_minutes) prevents continuous requoting on markets with no real two-way flow. Volatility of mid over the last hour determines the spread width dynamically: higher vol → wider edge. This is a user-controlled liquidity-provision tool. Maker rebates (20–25% of platform fees, paid in pUSD) contribute to the return profile. No performance claims are made.

3. Why This Bot Matters

  • Quoting on a stale book (no fills for > stale_book_minutes)

    Resting orders sit on a market with no real two-way activity. If news arrives and moves the market, the stale quotes are adversely selected at a wide loss.

  • Edge too narrow for wide-spread illiquid markets

    In a thin book, the spread can move by 100–200 bps in a single fill. A narrow edge does not cover this adverse-selection risk.

  • Inventory accumulates without rebalancing

    On a thin market with one-sided flow, the bot can accumulate a large directional inventory before the inventory skew mechanism has time to work. Hard inventory cap prevents runaway accumulation.

  • feeRateBps present on signed maker order (V1 pattern)

    CTFExchangeV2 rejects orders with feeRateBps. Maker fees are operator-set at match time; the signed order must not contain this field. Maker fee_bps is capped at 50 bps.

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
Live order book — best bid, best ask, mid-pricews_market (CLOB WebSocket)YesCompute current spread and mid-price; derive dynamic edge width from mid-volatility.
Time since last fill on the marketclob_public (fills endpoint)YesStale-book detector: if time since last fill > stale_book_minutes, skip requoting.
Hourly mid-price volatilityws_market (trade tape, 60-min rolling)YesSet dynamic edge: edge_bps = base_edge_bps * (1 + vol_multiplier * hourly_vol).
Running inventory position on each marketclob_auth (open positions)YesCompute inventory ratio; apply hard inventory cap; skew quotes to reduce open inventory.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagKillSwitchYesCancel all open maker quotes and emit no new OrderIntents if KillSwitch is active.
Builder code bytes32internal configYesInjected into builder field on every maker OrderIntent. Maker fee_bps <= 50.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
edge_bps1206030Base edge in basis points from mid-price at which the bot will post a wide-spread quote. Actual edge is scaled upward by hourly mid volatility.
clip_size_usd50100200Size in pUSD of each individual wide maker quote (bid or ask). Kept small to limit adverse-selection exposure on thin books.
hard_inventory_cap_usd300200500Maximum total pUSD inventory on a single market. If inventory exceeds this, the bot stops quoting until inventory reduces via natural fill.
stale_book_minutes153060Maximum time in minutes since the last fill before the book is considered stale and quoting is paused.

7. Detailed Parameter Instructions

edge_bps

What it means

Base edge in basis points from mid-price at which the bot will post a wide-spread quote. Actual edge is scaled upward by hourly mid volatility.

Default

{ "edge_bps": 120 }

Why this default matters

120 bps covers expected adverse selection on thin illiquid markets where the spread can move 100+ bps on a single fill. Below 60 bps the edge is unlikely to persist; below 30 bps the bot will not quote regardless.

Threshold logic

ConditionAction
>= 120 bpsPost wide quotes at mid +/- edge_bps/2
60–120 bpsWARN MAKER_WIDE_EDGE_MARGINAL; post at 50% clip size
< 30 bps (hard floor)SKIP — MAKER_WIDE_SPREAD_TOO_TIGHT

Developer check

if edge_bps < params.hard: return skip('MAKER_WIDE_SPREAD_TOO_TIGHT')

User-facing English

The market spread is too narrow for wide-spread maker quoting. Quoting was skipped for this cycle.

clip_size_usd

What it means

Size in pUSD of each individual wide maker quote (bid or ask). Kept small to limit adverse-selection exposure on thin books.

Default

{ "clip_size_usd": 50 }

Why this default matters

50 pUSD per side is modest for thin markets where a single fill can move the book significantly. 200 pUSD is the hard cap to prevent over-concentration in illiquid positions.

Threshold logic

ConditionAction
<= 50 pUSDNormal small clip quote
50–200 pUSDWARN; confirm thin-book depth supports this size
> 200 pUSDReject config — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

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

User-facing English

Quote was sized to keep risk small on this low-activity market.

hard_inventory_cap_usd

What it means

Maximum total pUSD inventory on a single market. If inventory exceeds this, the bot stops quoting until inventory reduces via natural fill.

Default

{ "hard_inventory_cap_usd": 300 }

Why this default matters

300 pUSD on a thin market represents meaningful one-sided exposure. The hard cap prevents runaway accumulation in the absence of two-way flow.

Threshold logic

ConditionAction
<= 300 pUSDNormal quoting
200–300 pUSDWARN MAKER_WIDE_HIGH_INVENTORY; reduce clip to minimum
> 500 pUSDHard cap; stop quoting on market until inventory falls below warning threshold

Developer check

if inventory_usd >= params.hard_inventory_cap_usd: return skip('MAKER_WIDE_INVENTORY_CAP_HIT')

User-facing English

This market's current position is at the maximum allowed size. No new quotes were placed until the position reduces.

stale_book_minutes

What it means

Maximum time in minutes since the last fill before the book is considered stale and quoting is paused.

Default

{ "stale_book_minutes": 15 }

Why this default matters

15 minutes without a fill on a thin market indicates negligible real two-way interest. Quoting into a completely inactive book has no maker rebate benefit and high adverse-selection risk when interest returns.

Threshold logic

ConditionAction
< 15 min since last fillActive book; quote normally
15–60 minWARN MAKER_WIDE_STALE_BOOK; continue quoting with reduced clip
> 60 min (hard floor)SKIP — MAKER_WIDE_BOOK_TOO_STALE; cancel open quotes

Developer check

if time_since_last_fill_min > params.hard: return skip('MAKER_WIDE_BOOK_TOO_STALE')

User-facing English

This market hasn't had any recent trading activity. Quoting was paused to avoid posting into a stale book.

8. Default Configuration

{
  "bot_id": "strat.maker_wide",
  "version": "2.1.0",
  "mode": "limited_live",
  "defaults": {
    "edge_bps": 120,
    "clip_size_usd": 50,
    "hard_inventory_cap_usd": 300,
    "stale_book_minutes": 15
  },
  "locked": {
    "edge_bps": {
      "min": 30
    },
    "clip_size_usd": {
      "max": 200
    },
    "hard_inventory_cap_usd": {
      "max": 500
    },
    "stale_book_minutes": {
      "max": 60
    }
  }
}

9. Implementation Flow

  1. Check KillSwitch active flag; if active, cancel all open maker quotes and emit no new OrderIntents.
  2. Subscribe to ws_market book and trade-tape streams for all eligible thin-book markets.
  3. Filter markets: 24h volume < 250,000 pUSD (thin-book regime). Markets with > 250K volume are handled by Maker-Tight.
  4. On each book tick: compute mid = (best_bid + best_ask) / 2; spread_bps = (best_ask - best_bid) / mid * 10000.
  5. Check stale-book condition: fetch time since last fill from clob_public. If > stale_book_minutes hard (60 min), skip — MAKER_WIDE_BOOK_TOO_STALE; cancel open quotes.
  6. If time_since_last_fill > 15 min (warning), WARN MAKER_WIDE_STALE_BOOK; reduce clip to 50%.
  7. Compute hourly mid volatility from trade tape. Set dynamic_edge_bps = edge_bps * (1 + 0.5 * hourly_vol / 0.01).
  8. If spread_bps < 2 * dynamic_edge_bps hard floor (30 bps): skip — MAKER_WIDE_SPREAD_TOO_TIGHT (sampled 1/100).
  9. Fetch running inventory from clob_auth. If inventory_usd >= hard_inventory_cap_usd: skip — MAKER_WIDE_INVENTORY_CAP_HIT.
  10. Compute inventory skew: bidPrice = mid - dynamic_edge_bps/2/10000 - skew * inventory_ratio; askPrice symmetric.
  11. Emit OrderIntent YES (post_only=true, side=buy, price=bidPrice, tif=GTC, builder={code, fee_bps:30}, size_pUSD=clip_size_usd).
  12. Emit OrderIntent NO (post_only=true, side=buy, price=1-askPrice, tif=GTC, builder={code, fee_bps:30}, size_pUSD=clip_size_usd).
  13. Note: fees are operator-set at match time in V2 — feeRateBps is NOT on the signed order. Maker fee_bps <= 50.
  14. Emit DecisionReport with intent_emitted=true, dynamic_edge_bps, time_since_last_fill_min, reason MAKER_WIDE_QUOTING.

10. Reference Implementation

Subscribes to CLOB WebSocket book and trade-tape streams for thin-book markets, checks spread, stale-book condition, and inventory cap, then emits GTC post_only maker OrderIntents inside the touch at dynamically widened edge.

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. Translate to TS/Python/Go/Rust.

FUNCTION onBookTick(market_id, bookTick):
  // --- 0. KillSwitch gate ---
  ks = FETCH internal.killswitch.status
  IF ks.active:
    CANCEL_OPEN_QUOTES(market_id)
    RETURN

  // --- 1. Stale-book check ---
  fills = FETCH clob_public.GET('/markets/' + market_id + '/fills?limit=1')
  timeSinceFillMin = (now_ms() - fills[0].timestamp_ms) / 60000
  IF timeSinceFillMin > params.stale_book_minutes_hard:  // 60 min
    CANCEL_OPEN_QUOTES(market_id)
    EMIT DecisionReport(intent_emitted=false, reason='MAKER_WIDE_BOOK_TOO_STALE')
    RETURN

  // --- 2. Inventory cap check ---
  position = FETCH clob_auth.GET('/positions?market=' + market_id)
  IF position.notional_usd >= params.hard_inventory_cap_usd:
    EMIT DecisionReport(intent_emitted=false, reason='MAKER_WIDE_INVENTORY_CAP_HIT')
    RETURN

  // --- 3. Dynamic edge from volatility ---
  tape = FETCH ws_market.tradeTape(market_id, window_min=60)
  hourlyVol = stddev(tape.mid_prices)
  dynamicEdgeBps = params.edge_bps * (1 + 0.5 * hourlyVol / 0.01)
  IF dynamicEdgeBps < params.edge_bps_hard:  // 30 bps
    IF random() < 0.01:
      EMIT DecisionReport(intent_emitted=false, reason='MAKER_WIDE_SPREAD_TOO_TIGHT')
    RETURN

  // --- 4. Warning thresholds ---
  clipSize = toPusdUnits(params.clip_size_usd)
  IF dynamicEdgeBps < 60 OR timeSinceFillMin > 15:
    WARN('MAKER_WIDE_EDGE_MARGINAL' OR 'MAKER_WIDE_STALE_BOOK')
    clipSize = toPusdUnits(params.clip_size_usd * 0.5)

  // --- 5. Inventory skew ---
  inventoryRatio = position.notional_usd / params.hard_inventory_cap_usd
  skew = 0.3 * inventoryRatio
  mid = (bookTick.best_bid + bookTick.best_ask) / 2
  edgeHalf = dynamicEdgeBps / 2 / 10000
  bidPrice = round(mid - edgeHalf - skew, 4)
  askPrice = round(mid + edgeHalf - skew, 4)

  // --- 6. Emit wide maker OrderIntents (V2: no feeRateBps; post_only=true) ---
  EMIT OrderIntent(
    market_id = market_id, outcome = 'YES', side = 'buy',
    price = bidPrice, size_pUSD = clipSize, tif = 'GTC', post_only = true,
    builder = {code: internal.builder_code, fee_bps: 30}
  )
  EMIT OrderIntent(
    market_id = market_id, outcome = 'NO', side = 'buy',
    price = round(1 - askPrice, 4), size_pUSD = clipSize, tif = 'GTC', post_only = true,
    builder = {code: internal.builder_code, fee_bps: 30}
  )
  EMIT DecisionReport(intent_emitted=true, dynamic_edge_bps=dynamicEdgeBps,
                      time_since_last_fill_min=timeSinceFillMin, reason='MAKER_WIDE_QUOTING')

SDK calls used

  • ws_market.subscribe('book', [market_id])
  • ws_market.subscribe('trade_tape', [market_id])
  • fetchClobPublic('/markets/' + market_id + '/fills?limit=1')
  • clob_auth.GET('/positions?market=' + market_id)
  • toPusdUnits(rawFloat)
  • buildOrderTypedData(orderParams, { name: 'CTFExchange', version: '2', chainId: 137 })
  • internal.killswitch.status()
  • internal.builder_code

Complexity: O(1) per market book tick

11. Wire Examples

Input — what arrives on the wire

WebSocket book tick — thin market, spread 250 bps, fresh bookws_market

{
  "market_id": "0xmakerwide000000000000000000000000000000000000000000000000000001a",
  "best_bid": "0.570",
  "best_ask": "0.606",
  "mid": "0.588",
  "spread_bps": "61.2",
  "volume_24h_pusd": "42000",
  "time_since_last_fill_min": "4.2",
  "hourly_vol": "0.018",
  "received_at_ms": 1746790400000
}

Output — what the bot emits

OrderIntent — wide maker bid (YES buy, post_only, GTC)

{
  "intent_id": "oi_01HXMKW0000W01A",
  "trace_id": "tr_01HXMKW0000WTR1",
  "market_id": "0xmakerwide000000000000000000000000000000000000000000000000000001a",
  "outcome": "YES",
  "side": "buy",
  "price": "0.582",
  "size_pUSD": "50.00",
  "tif": "GTC",
  "post_only": true,
  "builder": {
    "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
    "fee_bps": 30
  },
  "negrisk_aware": false,
  "decision": {
    "dynamic_edge_bps": 132.0,
    "time_since_last_fill_min": 4.2,
    "inventory_ratio": 0.08,
    "skew_applied": true,
    "reasons": [
      "MAKER_WIDE_QUOTING"
    ]
  },
  "comment": "fees are operator-set at match time in V2 — feeRateBps is NOT on the signed order"
}

DecisionReport — skipped (book too stale), sampled 1/100

{
  "report_id": "dr_01HXMKW999ZZZZ",
  "bot_id": "strat.maker_wide",
  "market_id": "0xmakerwide000000000000000000000000000000000000000000000000000001a",
  "intent_emitted": false,
  "time_since_last_fill_min": 82.0,
  "reasons": [
    "MAKER_WIDE_BOOK_TOO_STALE"
  ],
  "sampled": true,
  "evaluated_at_ms": 1746790401000
}

12. Decision Logic

APPROVE

spread_bps > 2 * dynamic_edge_bps, time_since_last_fill < stale_book_minutes, inventory_usd < hard_inventory_cap_usd, KillSwitch inactive. Emit bid + ask OrderIntents as GTC post_only maker orders.

RESHAPE_REQUIRED

Not applicable — strat bots emit OrderIntents; reshaping is handled downstream by the Risk guardrail pipeline.

REJECT

spread too tight; stale book (> 60 min); inventory cap hit; KillSwitch active; stale feed. Cancel open quotes and emit DecisionReport intent_emitted=false.

WARNING_ONLY

edge_bps between 30 and 60 bps, or time_since_last_fill between 15 and 60 min, triggers warning and 50% size reduction.

13. Standard Decision Output

This bot returns a OrderIntent object. See OrderIntent schema.

{
  "intent_id": "oi_01HXMKW0000W01A",
  "trace_id": "tr_01HXMKW0000WTR1",
  "market_id": "0xmakerwide000000000000000000000000000000000000000000000000000001a",
  "outcome": "YES",
  "side": "buy",
  "price": "0.582",
  "size_pUSD": "50.00",
  "tif": "GTC",
  "post_only": true,
  "builder": {
    "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
    "fee_bps": 30
  },
  "negrisk_aware": false,
  "decision": {
    "dynamic_edge_bps": 132.0,
    "time_since_last_fill_min": 4.2,
    "inventory_ratio": 0.08,
    "skew_applied": true,
    "reasons": [
      "MAKER_WIDE_QUOTING"
    ]
  },
  "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
MAKER_WIDE_QUOTINGINFOSpread is adequate, book is fresh, inventory below cap. Wide maker bid+ask OrderIntents emitted.Emit two GTC post_only OrderIntents.Wide maker quotes were posted inside the market spread.
MAKER_WIDE_SPREAD_TOO_TIGHTINFOMarket spread is narrower than 2 * dynamic_edge_bps hard floor. Wide quoting is not viable.Skip; emit sampled DecisionReport.The market spread was too tight for wide maker quoting.
MAKER_WIDE_BOOK_TOO_STALEWARNTime since last fill exceeds stale_book_minutes hard limit (60 min). Book is considered inactive.Cancel open quotes; emit DecisionReport intent_emitted=false.This market has had no recent trading activity. Quotes were paused.
MAKER_WIDE_STALE_BOOKWARNTime since last fill is between 15 and 60 minutes (warning threshold). Book activity is low.Reduce clip to 50%; continue quoting; log warning.This market has had limited recent activity. Quote sizes were reduced.
MAKER_WIDE_INVENTORY_CAP_HITWARNInventory on this market has reached hard_inventory_cap_usd. Quoting is suspended.Skip; no new OrderIntents; emit DecisionReport.The maximum position size for this market has been reached. No new quotes were placed.
MAKER_WIDE_EDGE_MARGINALWARNDynamic edge is between 30 and 60 bps (warning threshold). Quoting at reduced clip size.Emit OrderIntents at 50% clip size; log warning.Market conditions are borderline. Maker quotes were posted at reduced size.
MAKER_WIDE_HIGH_INVENTORYWARNInventory is between the warning (200 pUSD) and hard cap (300 pUSD). Accumulation risk elevated.Continue quoting; reduce clip to minimum; log warning.The current position on this market is large. Quote sizes were reduced.
STALE_MARKET_DATAHARD_REJECTBook snapshot older than 5s or position data unavailable.Cancel open quotes; no new OrderIntents.Market data was too old. Quotes were cancelled.
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch is active.Cancel all open quotes; no new OrderIntents.Trading is currently paused.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_strat_makerwide_decisions_totalcountercountverdict, reason_codeTotal evaluation cycles by intent_emitted and reason code.
polytraders_strat_makerwide_dynamic_edge_bpshistogrambasis_pointsmarket_idDistribution of dynamic edge in bps at each quoting cycle.
polytraders_strat_makerwide_inventory_usdgaugepusdmarket_idCurrent inventory in pUSD per quoted market.
polytraders_strat_makerwide_intents_emitted_totalcountercountsideTotal wide maker OrderIntents emitted by side (bid/ask).
polytraders_strat_makerwide_stale_book_skips_totalcountercountmarket_idQuoting cycles skipped due to stale book per market.
polytraders_strat_makerwide_eval_latency_mshistogrammillisecondsWall-clock latency from book tick to OrderIntent emit.

Alerts

AlertConditionSeverityRunbook
MakerWideHighInventorypolytraders_strat_makerwide_inventory_usd > 250warn#runbook-makerwide-inventory
MakerWideHighStaleBookRaterate(polytraders_strat_makerwide_stale_book_skips_total[10m]) > 5warn#runbook-makerwide-stale-book
MakerWideStaleFeedrate(polytraders_strat_makerwide_decisions_total{reason_code='STALE_MARKET_DATA'}[5m]) > 0.1warn#runbook-makerwide-stale-feed
MakerWideKillSwitchBlockingrate(polytraders_strat_makerwide_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0page#runbook-killswitch

Dashboards

  • Grafana — Strategy / MakerWide dynamic edge and stale-book rate
  • Grafana — Strategy / MakerWide inventory per market

16. Developer Reporting

{
  "bot_id": "strat.maker_wide",
  "market_id": "0xmakerwide000000000000000000000000000000000000000000000000000001a",
  "mid": 0.588,
  "best_bid": 0.57,
  "best_ask": 0.606,
  "spread_bps": 61.2,
  "base_edge_bps": 120,
  "hourly_vol": 0.018,
  "dynamic_edge_bps": 132.0,
  "time_since_last_fill_min": 4.2,
  "inventory_usd": 24.0,
  "inventory_ratio": 0.08,
  "skew_applied": true,
  "clip_size_pusd": 50.0,
  "intent_emitted": true,
  "reason": "MAKER_WIDE_QUOTING",
  "emitted_at_ms": 1746790400000
}

17. Plain-English Reporting

SituationUser-facing explanation
Wide maker quote postedA resting order was placed at a wider spread on this low-activity market to provide liquidity. The quote will fill if a taker crosses the price.
Quote skipped — stale bookThis market hasn't had any trades recently. Quoting was paused to avoid posting into a potentially stale book.
Quote skipped — inventory cap reachedThe current position on this market is at the maximum allowed size. No new quotes were placed until the position reduces via natural trading.
Spread dynamically widenedRecent price volatility on this market caused the quote spread to widen automatically to provide more protection against adverse selection.

18. Failure-Mode Block

main_failure_modeAdverse selection on news arrival: stale wide-spread quotes get hit by informed takers when an event moves the market sharply, producing a loss larger than the accumulated maker rebates.
false_positive_riskStale-book detector is too sensitive, blocking quoting on thin markets that do have valid two-way interest at infrequent intervals.
false_negative_riskstale_book_minutes set too high causes the bot to continue quoting on truly inactive markets, accumulating positions without offsetting natural flow.
safe_fallbackIf book data is stale (last_seen > 5s) or clob_auth position read fails, cancel all open quotes and emit no new OrderIntents. Hard inventory cap prevents runaway accumulation.
required_dependenciesws_market book and trade-tape stream, clob_public fills endpoint (time since last fill), clob_auth open positions, KillSwitch active flag, internal builder code

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
STALE_WS_FEEDPause ws_market WebSocket; let last_seen age beyond 5sAutomatic on WebSocket reconnect; quotes re-posted on next clean tick.
STALE_BOOK_HARD_LIMITSet mock last fill timestamp to now() - 75 minutesAutomatic when a fill occurs on the market and stale timer resets.
INVENTORY_CAP_HITSet mock position.notional_usd=310 on target market (> hard cap 300)Automatic when inventory falls below warning threshold via natural fills.
KILL_SWITCH_ONSet killswitch.active=trueAutomatic on manual KillSwitch reset.
DYNAMIC_EDGE_COLLAPSESet mock hourly_vol=0 and spread_bps=50bps (below 2*30bps hard floor edge)Automatic when spread widens above threshold.

20. State & Persistence

Cold-start recovery

On cold start, open quote IDs re-fetched from clob_auth. Inventory re-read from clob_auth. Book state rebuilt from first ws_market tick.

21. Concurrency & Idempotency

AspectSpecification
Execution modelactor-per-market
Max in-flight30
Idempotency keyintent_id
Per-call timeout (ms)200
Backpressure strategydrop oldest pending tick per market_id when queue depth > 3
Locking / mutual exclusionper-market_id mutex for inventory state read/write and open quote tracking

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchChecked first; cancels all open quotes and blocks new intent emission when active.

Emits to (downstream consumers)

Sibling bots (same OrderIntent)

BotWhyContract
strat.maker_tight

External services

ServiceEndpointSLA assumedOn failure
Polymarket CLOB WebSocket (ws_market)best-effort
Polymarket CLOB auth API (positions)99.95%
Polymarket CLOB public API (fills)99.9%

23. Security Surfaces

On-chain contract calls

ContractMethodNetworkEffect
CTFExchangeV2polygon

Abuse vectors considered

  • Adversary floods thin book with cancels to advance stale_book timer, then exploits resumed quoting with informed order
  • Quote stuffing on thin market to repeatedly trigger stale-book detection and prevent Maker-Wide from providing liquidity
  • Inventory manipulation via spoofed position to pin inventory near hard cap and halt quoting

Mitigations

  • Stale-book timer is based on last actual fill timestamp, not last quote or cancel
  • Inventory read from clob_auth (authenticated), not from ws_market (public feed)
  • KillSwitch cancels all open quotes immediately on activation
  • V2 order timestamp(ms) prevents replay of old signed maker orders
  • post_only=true prevents inadvertent taker fills on latency spikes

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
NotesAll maker orders use post_only=true to qualify for maker rebates (20-25% of platform fees, paid in pUSD). builder.fee_bps is capped at 30 bps, within the V2 maker maximum of 50 bps. feeRateBps is not present on any signed order — operator-set at match time.

API surfaces declared

clob_publicclob_authws_marketinternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation2.1.0
schema2
released2026-04-28

Migration history

DateFromToReasonAction taken
2026-04-28v1 (USDC.e, feeRateBps on signed order)v2 (pUSD, fees operator-set at match time, maker fee_bps <= 50)CLOB V2 cutoverSwitched to py-clob-client-v2. Removed feeRateBps from all signed maker order construction. Updated collateral denomination to pUSD. Injected builder field (bytes32) on every OrderIntent. Confirmed maker fee_bps cap of 50 bps. EIP-712 Exchange domain version updated from '1' to '2'. Stale-book detection extended to use minute-level resolution for thin books.

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Post bid+ask when spread > 2*dynamic_edge_bps and book is freshspread=250bps, base_edge=120bps, time_since_fill=4min, inventory=0Two OrderIntents (bid + ask) emitted with post_only=true, GTC
Skip when time_since_last_fill > hard limit (60 min)time_since_last_fill_min=75No OrderIntents; open quotes cancelled; reason=MAKER_WIDE_BOOK_TOO_STALE
Skip when inventory_usd >= hard_inventory_cap_usdinventory_usd=310, hard_inventory_cap_usd=300No OrderIntents; reason=MAKER_WIDE_INVENTORY_CAP_HIT
Dynamic edge widens with high volatilitybase_edge_bps=120, hourly_vol=0.03dynamic_edge_bps > 120; quotes placed at wider spread
Reduce clip 50% when edge_bps marginal (50 bps)edge_bps=50, clip_size_usd=50OrderIntents emitted with size=25; WARN MAKER_WIDE_EDGE_MARGINAL
Skip when KillSwitch active; cancel open quoteskillswitch.active=trueNo new OrderIntents; cancellation of open quotes emitted

Integration Tests

TestExpected result
Full cycle: ws_market tick → stale-book check → dynamic edge → two signed V2 post_only GTC OrderIntentsBoth orders have builder.code (bytes32), no feeRateBps, post_only=true, EIP-712 domain version '2'
Stale book detection cancels open quotes and emits MAKER_WIDE_BOOK_TOO_STALEOpen quote IDs cancelled; DecisionReport intent_emitted=false

Property Tests

PropertyRequired behaviour
post_only=true on every maker OrderIntent; bot never submits taker ordersAlways true
feeRateBps never present on any signed OrderIntentAlways true — V2 fees are operator-set at match time
Inventory never exceeds hard_inventory_cap_usd on any single marketAlways true

27. Operational Runbook

Maker-Wide incidents are typically stale-book triggers (no fills for 60+ min), inventory cap breaches on thin markets, or stale feeds. Stale feeds resolve automatically; inventory cap breaches require monitoring until natural fills reduce the position.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
MakerWideHighInventory
MakerWideHighStaleBookRate
MakerWideStaleFeed
MakerWideKillSwitchBlocking

Manual overrides

Healthcheck

GET /internal/health/maker-wide -> 200 if ws_market feed last_seen < 5s, clob_auth reachable, KillSwitch inactive, and at least one thin-book market quoted in last 5 min.

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
All unit tests pass including post_only invariant, stale-book detection, and inventory capCI test run100% pass
feeRateBps absence verified; maker fee_bps <= 50 verified in integration testIntegration test asserting V2 order schemaPass

Promote to Limited live

GateHow measuredThreshold
p99 eval latency < 200ms over 24hpolytraders_strat_makerwide_eval_latency_ms histogramp99 < 200ms
Inventory stays below hard_inventory_cap_usd on all markets over 48h shadow runpolytraders_strat_makerwide_inventory_usd gaugemax < 300 pUSD

Promote to General live

GateHow measuredThreshold
E2E: book tick → stale-book check → two signed V2 post_only GTC OrderIntents submitted and resting on CLOB testnetE2E testPass
Stale-book cancellation verified: open quotes cancelled on hard-limit triggerIntegration 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