1. Bot Identity
| Layer | Execution Execution |
|---|
| Bot class | Execution Utility |
|---|
| Authority | Reshape |
|---|
| Status | PLANNED |
|---|
| Readiness | Spec started |
|---|
| Runs before | Order signing and CLOB V2 submission |
|---|
| Runs after | PartialFillHandler or strategy-driven requote signal |
|---|
| Applies to | Any request to modify a resting order's price or size |
|---|
| Default mode | shadow_only |
|---|
| User-visible | summary-only |
|---|
| Developer owner | Polytraders core — Execution pod |
|---|
Operational profile
| Modes supported | quarantine |
|---|
2. Purpose
CancelReplaceOptimizer selects the cheapest path to modify a resting order: amend-in-place if queue priority is preserved, or cancel-then-replace if the delta exceeds the amend threshold. It enforces rate-limit budgets across cancel and amend operations.
3. Why This Bot Matters
Always using cancel-then-replace
Loses queue position on every requote, resulting in consistently worse fills on passive strategies where queue position matters.
Amend threshold set too wide
Large price changes that would trigger queue-position loss are processed as amends, causing the strategy to hold a stale order at a position that was meant to be cancelled.
Rate limit not enforced across cancels
A burst of cancel-replace operations hits the CLOB V2 rate limit, causing 429 errors and leaving orders unmodified at stale prices.
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.
6. Parameter Guide
| Parameter | Default | Warning | Hard | What it controls |
|---|
| amend_threshold_ticks | 2 | 4 | 8 | Maximum tick distance between current and target price for which an amend-in-place is preferred over cancel-then-replace. |
| preserve_queue_when_possible | True | — | — | Prefer amend-in-place over cancel-then-replace whenever the CLOB V2 amend semantics permit queue priority to be preserved. |
| burst_max_per_s | 10 | 15 | 20 | Maximum number of cancel or amend operations per second across all orders. |
| fallback_strategy | cancel_replace | — | — | Fallback when amend endpoint is unavailable: cancel_replace (issue cancel then new order) or hold (leave resting, no modification). |
7. Detailed Parameter Instructions
amend_threshold_ticks
What it means
Maximum tick distance between current and target price for which an amend-in-place is preferred over cancel-then-replace.
Default
{ "amend_threshold_ticks": 2 }
Why this default matters
Amending within 2 ticks typically preserves queue priority on CLOB V2; larger moves force a queue-position reset anyway.
Threshold logic
| Condition | Action |
|---|
| delta_ticks <= 2 | Amend in place (queue priority preserved) |
| 2 < delta_ticks <= 4 | WARN — amend may lose queue priority; proceed |
| delta_ticks > 8 (hard) | Always cancel-then-replace; CANCEL_REPLACE_FORCED |
Developer check
if deltaTicks > params.amend_threshold_ticks: cancelReplace(order)
User-facing English
Your order was adjusted to improve your position in the market.
preserve_queue_when_possible
What it means
Prefer amend-in-place over cancel-then-replace whenever the CLOB V2 amend semantics permit queue priority to be preserved.
Default
{ "preserve_queue_when_possible": true }
Why this default matters
Preserving queue position is generally better for passive strategies; only disable if the strategy always wants a fresh queue position.
Threshold logic
| Condition | Action |
|---|
| preserve_queue=true AND delta within threshold | Amend in place |
| preserve_queue=false | Always cancel-then-replace regardless of delta |
Developer check
if not params.preserve_queue_when_possible: cancelReplace(order)
User-facing English
Your order's position in the queue was preserved where possible.
burst_max_per_s
What it means
Maximum number of cancel or amend operations per second across all orders.
Default
{ "burst_max_per_s": 10 }
Why this default matters
CLOB V2 rate limits restrict cancel/amend to ~20/s; staying at 10/s leaves budget for other exec operations.
Threshold logic
| Condition | Action |
|---|
| ops_per_s <= 10 | Normal |
| 10 < ops_per_s <= 15 | WARN — approaching rate limit |
| ops_per_s > 20 (hard) | Shed requests; CANCEL_REPLACE_RATE_LIMIT_SHED |
Developer check
if opsThisSecond > params.burst_max_per_s: shedRequest()
User-facing English
Order updates were briefly delayed to stay within exchange limits.
fallback_strategy
What it means
Fallback when amend endpoint is unavailable: cancel_replace (issue cancel then new order) or hold (leave resting, no modification).
Default
{ "fallback_strategy": "cancel_replace" }
Why this default matters
cancel_replace is always possible when amend is unavailable; hold risks leaving a stale order.
Threshold logic
| Condition | Action |
|---|
| amend unavailable AND fallback=cancel_replace | Cancel and resubmit new order |
| amend unavailable AND fallback=hold | Leave order resting; emit WARN |
Developer check
if amendFailed: cancelReplace(order) if params.fallback_strategy == 'cancel_replace'
User-facing English
Your order was updated using an alternative method to keep it competitive.
8. Default Configuration
{
"bot_id": "exec.cancelreplaceoptimizer",
"version": "0.1.0",
"mode": "shadow_only",
"defaults": {
"amend_threshold_ticks": 2,
"preserve_queue_when_possible": true,
"burst_max_per_s": 10,
"fallback_strategy": "cancel_replace"
},
"locked": {
"amend_threshold_ticks": {
"max": 8
},
"burst_max_per_s": {
"max": 20
}
}
}
9. Implementation Flow
- Receive requote instruction: {order_id, current_price, target_price, target_size, market_id}.
- Fetch tick size from clob_public; compute delta_ticks = abs(target_price - current_price) / tick_size.
- Check rate-limit budget; if burst_max_per_s exceeded, shed request and emit CANCEL_REPLACE_RATE_LIMIT_SHED.
- If preserve_queue_when_possible and delta_ticks <= amend_threshold_ticks: attempt amend via clob_auth PATCH /order/{id}.
- If amend succeeds: emit ExecutionReport(AMEND_IN_PLACE); done.
- If amend fails or delta_ticks > amend_threshold_ticks: cancel via clob_auth DELETE /order/{id}; submit new order at target_price/target_size.
- Emit ExecutionReport with path_taken (amend or cancel_replace), delta_ticks, and rate_limit_budget_remaining.
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 optimizeCancelReplace(instruction):
// 1. Fetch tick size
market = FETCH clob_public.GET('/markets/' + instruction.market_id)
tickSize = market.minimum_tick_size
deltaTicks = abs(instruction.target_price - instruction.current_price) / tickSize
// 2. Rate limit check
IF rateLimiter.opsThisSecond >= params.burst_max_per_s:
EMIT ExecutionReport(CANCEL_REPLACE_RATE_LIMIT_SHED, WARN)
RETURN
// 3. Decide path
IF params.preserve_queue_when_possible
AND deltaTicks <= params.amend_threshold_ticks:
// Attempt amend
result = clob_auth.PATCH('/order/' + instruction.order_id,
{price: instruction.target_price,
size: instruction.target_size})
IF result.ok:
EMIT ExecutionReport(AMEND_IN_PLACE)
RETURN
// Amend failed — fall through to cancel-replace
// 4. Cancel-then-replace
clob_auth.DELETE('/order/' + instruction.order_id)
newOrder = buildOrderTypedData({
price: instruction.target_price,
size: instruction.target_size,
builder: instruction.builder_code,
timestamp: now_ms()
})
clob_auth.POST('/order', sign(newOrder))
EMIT ExecutionReport(CANCEL_REPLACE_EXECUTED)
SDK calls used
clob_public.GET('/markets/' + market_id)clob_auth.PATCH('/order/' + order_id, {price, size})clob_auth.DELETE('/order/' + order_id)clob_auth.POST('/order', signed_new_order)buildOrderTypedData({price, size, builder_code, timestamp})
Complexity: O(1) per requote instruction
11. Wire Examples
Input — what arrives on the wire
Requote instruction — exec.partialfillhandler
{
"order_id": "0xdddd4444eeee5555ffff6666aaaa7777bbbb8888cccc9999dddd0000eeee1111",
"market_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"current_price": 0.62,
"target_price": 0.63,
"target_size_usd": 250,
"builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
"collateral": "pUSD"
}
Output — what the bot emits
ExecutionReport — amend in place
{
"report_id": "rep_4d5e6f7a8b9c0d1e",
"bot_id": "exec.cancelreplaceoptimizer",
"path_taken": "amend_in_place",
"delta_ticks": 1,
"original_price": 0.62,
"target_price": 0.63,
"collateral": "pUSD",
"evaluated_at_ms": 1746770200000
}
12. Decision Logic
APPROVE
Amend succeeds within amend_threshold_ticks; queue priority preserved.
RESHAPE_REQUIRED
Cancel-then-replace path: order modified at cost of queue priority.
REJECT
Rate limit budget exhausted; request shed. Or KillSwitch active.
WARNING_ONLY
delta_ticks between warning and hard threshold; WARN emitted but amend proceeds.
13. Standard Decision Output
This bot returns a ExecutionReport object. See ExecutionReport schema.
{
"report_id": "rep_4d5e6f7a8b9c0d1e",
"trace_id": "trc_3c4d5e6f7a8b9c0d",
"bot_id": "exec.cancelreplaceoptimizer",
"order_id": "0xdddd4444eeee5555ffff6666aaaa7777bbbb8888cccc9999dddd0000eeee1111",
"path_taken": "amend_in_place",
"delta_ticks": 1,
"original_price": 0.62,
"target_price": 0.63,
"collateral": "pUSD",
"builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
"evaluated_at_ms": 1746770200000
}
14. Reason Codes
| Code | Severity | Meaning | Action | User-facing message |
|---|
AMEND_IN_PLACE | INFO | Order amended without cancelling; queue priority preserved. | Emit ExecutionReport; no further action. | |
CANCEL_REPLACE_EXECUTED | INFO | Order cancelled and resubmitted at new price/size. | Emit ExecutionReport. | Your order was updated to a new price. |
CANCEL_REPLACE_FORCED | RESHAPE | Delta exceeds amend_threshold_ticks; cancel-replace forced. | Cancel and resubmit. | Your order price changed significantly and was resubmitted. |
CANCEL_REPLACE_RATE_LIMIT_SHED | WARN | burst_max_per_s exceeded; request shed. | Drop request; emit WARN; retry on next tick. | Your order update was briefly delayed. |
KILL_SWITCH_ACTIVE | HARD_REJECT | KillSwitch active; no cancel or amend operations. | Halt all operations. | Trading is currently paused. |
15. Metrics & Logs
Metrics emitted
| Metric | Type | Unit | Labels | Meaning |
|---|
polytraders_exec_cancelreplaceoptimizer_ops_total | counter | count | path_taken | Total cancel-replace operations by path (amend_in_place vs cancel_replace). |
polytraders_exec_cancelreplaceoptimizer_delta_ticks | histogram | count | | Distribution of price delta in ticks on each requote instruction. |
polytraders_exec_cancelreplaceoptimizer_rate_limit_sheds_total | counter | count | | Total requests shed due to burst_max_per_s rate limit. |
Alerts
| Alert | Condition | Severity | Runbook |
|---|
CROHighRateLimitShed | rate(polytraders_exec_cancelreplaceoptimizer_rate_limit_sheds_total[1m]) > 1 | P2 | #runbook-cro-rate-limit |
CROHighDeltaTicks | histogram_quantile(0.95, rate(polytraders_exec_cancelreplaceoptimizer_delta_ticks_bucket[5m])) > 5 | P3 | #runbook-cro-delta-ticks |
16. Developer Reporting
{
"order_id": "0xdddd4444eeee5555ffff6666aaaa7777bbbb8888cccc9999dddd0000eeee1111",
"delta_ticks": 1,
"amend_threshold_ticks": 2,
"path_taken": "amend_in_place",
"rate_limit_budget_remaining": 8,
"amend_success": true
}
17. Plain-English Reporting
| Situation | User-facing explanation |
|---|
| Order amended in place | Your order was updated to a new price while keeping its place in the queue. |
| Order cancelled and resubmitted | Your order price changed by more than a small amount, so it was cancelled and a new order was submitted. |
| Order update delayed — rate limit | Your order update was briefly delayed because the exchange has a limit on how many changes can be made per second. |
18. Failure-Mode Block
| main_failure_mode | Amend endpoint returns success but CLOB V2 actually cancels and recreates the order (losing queue position), causing the strategy to believe queue was preserved when it was not. |
|---|
| false_positive_risk | Amending within the tick threshold when the book has moved and queue position is already lost anyway, wasting an amend operation. |
|---|
| false_negative_risk | Always cancelling-and-replacing when amend would have preserved queue priority, causing systematic queue-position loss on passive strategies. |
|---|
| safe_fallback | If amend endpoint returns error, fall back to cancel-then-replace per fallback_strategy config. If both fail, emit WARN and leave order resting. |
|---|
| required_dependencies | clob_auth amend/cancel endpoints, clob_public tick size, rate-limit budget tracker |
|---|
19. Failure-Injection Recipes
| Scenario | How to inject | Expected behaviour | Recovery |
|---|
AMEND_ENDPOINT_UNAVAILABLE | Block PATCH /order endpoint; leave DELETE and POST available | | Amend endpoint restored; subsequent requotes attempt amend first |
BURST_RATE_LIMIT_HIT | Submit 25 requote instructions in 1 second (burst_max_per_s=10) | | Automatic on next second |
KILL_SWITCH_DURING_AMEND | Set killswitch.active=true mid-amend sequence | | Manual KillSwitch reset |
20. State & Persistence
Cold-start recovery
Rate counter resets on restart; conservative: assume full burst budget used until first tick completes.
21. Concurrency & Idempotency
| Aspect | Specification |
|---|
| Execution model | single-threaded rate-gated queue |
| Max in-flight | 20 |
| Idempotency key | order_id + target_price + target_size |
| Per-call timeout (ms) | 500 |
| Backpressure strategy | Shed excess beyond burst_max_per_s; WARN emitted |
| Locking / mutual exclusion | per-instance atomic counter for rate limiting |
22. Dependencies
Depends on (must run first)
| Bot | Why | Contract |
|---|
| exec.partialfillhandler | Primary source of requote instructions for partial remainder chasing. | Receives cancel+new instruction; executes optimal path. |
Emits to (downstream consumers)
| Bot | Why | Contract |
|---|
| gov.builder_attribution | Every new replacement order carries builder_code for attribution logging. | builder_code bytes32 present on every signed replacement order. |
External services
| Service | Endpoint | SLA assumed | On failure |
|---|
| CLOB V2 auth API | https://clob.polymarket.com | 99.95% / 200ms p99 | Retry amend once; fall back to cancel-replace; emit WARN on persistent failure. |
| CLOB V2 public API | https://clob.polymarket.com | 99.9% / 200ms p99 | If tick size unavailable, use last-known tick size; emit WARN. |
23. Security Surfaces
Abuse vectors considered
- Flooding with rapid requote instructions to exhaust rate limit budget and suppress legitimate order modifications
- Injecting a requote with a target_price far from market to trigger a forced cancel-replace that loses queue priority
Mitigations
- burst_max_per_s enforced atomically; excess requests shed with WARN before hitting CLOB rate limit
- target_price validated against PriceBandValidator before requote is accepted
24. Polymarket V2 Compatibility
| Aspect | Value |
|---|
| CLOB version | v2 |
| Collateral asset | pUSD |
| EIP-712 Exchange domain version | 2 |
| Aware of builderCode field | yes |
| Aware of negative-risk markets | no |
| Multi-chain ready | no |
| SDK used | py-clob-client-v2 |
| Settlement contract | CTFExchangeV2 |
| Notes | Replacement orders carry the same builder_code bytes32 for continuous attribution. CLOB V2 amend semantics differ from V1; queue-priority preservation must be verified per V2 API docs. |
API surfaces declared
clob_authclob_publicinternal
Networks supported
polygon
25. Versioning & Migration
| Field | Value |
|---|
| spec | 2.0.0 |
| implementation | 0.1.0 |
| schema | 2 |
| released | None |
| planned_release | Q4-2026 |
Migration history
| Date | From | To | Reason | Action taken |
|---|
| 2026-04-28 | n/a | v2-spec | Spec drafted post-CLOB-V2 cutover; bot not yet implemented | Designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain) |
26. Acceptance Tests
Unit Tests
| Test | Setup | Expected result |
|---|
| Amend chosen when delta_ticks <= amend_threshold_ticks | delta_ticks=1, amend_threshold_ticks=2 | path_taken=amend_in_place |
| Cancel-replace when delta_ticks > amend_threshold_ticks | delta_ticks=5, amend_threshold_ticks=2 | path_taken=cancel_replace |
| Rate-limit shed when burst_max_per_s exceeded | ops_this_second=11, burst_max_per_s=10 | CANCEL_REPLACE_RATE_LIMIT_SHED; request dropped |
Integration Tests
| Test | Expected result |
|---|
| Amend endpoint failure → fallback to cancel-replace | clob_auth PATCH returns 4xx; fallback cancel+resubmit executed; ExecutionReport path_taken=cancel_replace |
| Sequential amends within rate budget | All amends processed within burst_max_per_s; no shedding |
Property Tests
| Property | Required behaviour |
|---|
| path_taken is always either amend_in_place or cancel_replace — never both | Always true |
| Target order price after modification always equals target_price rounded to tick_size | Always true |
27. Operational Runbook
CancelReplaceOptimizer incidents are typically rate-limit sheds (strategy generating too many requotes) or amend-endpoint failures causing unexpected cancel-replaces.
On-call actions
| Alert | First step | Diagnosis | Mitigation | Escalate to |
|---|
CROHighRateLimitShed | Identify which strategy is generating excess requotes; reduce requote frequency or increase burst_max_per_s temporarily. | | | Exec pod lead |
CROHighDeltaTicks | Investigate why strategies are requesting large price moves; may indicate stale quote data. | | | Strategy pod lead |
Manual overrides
polytraders bot force-cancel-replace exec.cancelreplaceoptimizer --order <order_id> --price <price> — Manual cancel-replace needed when amend endpoint is persistently unavailable.
Healthcheck
GET /internal/health/cancelreplaceoptimizer → green if clob_auth reachable, rate_limit_shed_rate < 0.1/min, amend success rate > 80%; red if clob_auth unreachable, rate_limit_shed_rate > 1/min, amend success rate < 50%
29. Developer Checklist
Ready-to-ship score: 27/27 sections complete · 100%
| Requirement | Status |
|---|
| 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 |