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 LayerExecution2.13 DustAndRoundingCleaner

2.13 DustAndRoundingCleaner

Execution Execution Utility Reshape PLANNED Spec started capital · Direct P5 · Execution rails pending stub

DustAndRoundingCleaner prevents creation of dust positions — holdings so small that their eventual pUSD value at settlement is less than the transaction cost of acquiring or selling them. It rounds order sizes to economically viable minimums and sweeps existing dust positions on a configurable cron schedule.

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

LayerExecution  Execution
Bot classExecution Utility
AuthorityReshape
StatusPLANNED
ReadinessSpec started
Runs beforeOrder signing and CLOB V2 submission; dust sweep runs on cron
Runs afterPartialFillHandler (remainder below min_economic_size) or strategy sizing step
Applies toAny order with remainder below min_economic_size and any existing dust positions on cron
Default modeshadow_only
User-visibleyes
Developer ownerPolytraders core — Execution pod

Operational profile

Modes supportedquarantine

2. Purpose

DustAndRoundingCleaner prevents creation of dust positions — holdings so small that their eventual pUSD value at settlement is less than the transaction cost of acquiring or selling them. It rounds order sizes to economically viable minimums and sweeps existing dust positions on a configurable cron schedule.

3. Why This Bot Matters

  • Dust position created

    A position of <$1 pUSD accumulates with no path to profitable exit; creates accounting noise and wastes position-budget slots.

  • Rounding not applied before submission

    Orders with fractional pUSD sizes are rejected by CTFExchangeV2 which requires integer token units; submission fails with a parse error.

  • Dust sweep not scheduled

    Over time, hundreds of dust positions accumulate from partial fills, consuming wallet gas allowance on Polygon and polluting portfolio reporting.

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 V2 minimum order size for marketclob_publicYesEnsure order size is above CLOB V2 minimum (1 share unit) after rounding.
Current position holdings for sweepclob_authYesIdentify existing positions below min_economic_size_usd for the sweep cron.

5. Required Internal Inputs

InputSourceRequired?Use
Remainder below min_economic_size from PartialFillHandlerexec.partialfillhandlerNoForward sub-minimum remainders to DustAndRoundingCleaner for sweep or cancellation.
ExecutionPlan size before signingexec.smart_routerYesRound plan.size_usd to nearest integer pUSD unit before signing.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
min_economic_size_usd521Minimum order or position size in pUSD below which the order is rejected (too small) or position is flagged for sweep.
round_strategyround_downRounding strategy for order sizes: round_down (conservative, never oversize), round_nearest (closest integer), or truncate (same as round_down).
auto_sweep_dustTrueAutomatically sweep dust positions (sell or cancel) on the sweep_cron schedule.
sweep_cron0 4 * * *Cron expression defining when the automatic dust sweep runs (default: 04:00 UTC daily).

7. Detailed Parameter Instructions

min_economic_size_usd

What it means

Minimum order or position size in pUSD below which the order is rejected (too small) or position is flagged for sweep.

Default

{ "min_economic_size_usd": 5 }

Why this default matters

At $5 pUSD minimum, a worst-case transaction cost of ~$0.50 (10% of position) is acceptable. Below $2, fees dominate.

Threshold logic

ConditionAction
size_usd >= 5No intervention
2 <= size_usd < 5WARN — DUST_WARN; flag for sweep
size_usd < 1 (hard)HARD_REJECT — DUST_HARD_REJECT; cancel order or sweep position

Developer check

if size_usd < params.min_economic_size_usd: emit(DUST_WARN)

User-facing English

A portion of your order was too small to keep open and was removed.

round_strategy

What it means

Rounding strategy for order sizes: round_down (conservative, never oversize), round_nearest (closest integer), or truncate (same as round_down).

Default

{ "round_strategy": "round_down" }

Why this default matters

Round-down ensures the final order never exceeds the intent size, which is important for risk constraint compliance.

Threshold logic

ConditionAction
round_strategy=round_downfloor(size_usd * 1e6) / 1e6 — never exceeds intent
round_strategy=round_nearestround(size_usd * 1e6) / 1e6
round_strategy=truncateint(size_usd * 1e6) / 1e6

Developer check

roundedSize = toUsdcUnits(plan.size_usd, params.round_strategy)

User-facing English

Your order size was rounded to the nearest valid unit.

auto_sweep_dust

What it means

Automatically sweep dust positions (sell or cancel) on the sweep_cron schedule.

Default

{ "auto_sweep_dust": true }

Why this default matters

Automated sweep prevents dust accumulation without requiring manual operator intervention.

Threshold logic

ConditionAction
auto_sweep_dust=trueDust positions swept on cron
auto_sweep_dust=falseWARN only; operator must sweep manually

Developer check

if params.auto_sweep_dust: scheduleSweep(params.sweep_cron)

User-facing English

Small leftover positions are automatically cleaned up on a schedule.

sweep_cron

What it means

Cron expression defining when the automatic dust sweep runs (default: 04:00 UTC daily).

Default

{ "sweep_cron": "0 4 * * *" }

Why this default matters

04:00 UTC is typically low-volume on Polymarket; sweeping then minimises market impact of small sell orders.

Threshold logic

ConditionAction
valid cron expressionSweep scheduled at specified time
invalid cron expressionReject config — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if not isValidCron(params.sweep_cron): raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')

User-facing English

Small leftover positions are cleaned up automatically each day.

8. Default Configuration

{
  "bot_id": "exec.dustandroundingcleaner",
  "version": "0.1.0",
  "mode": "shadow_only",
  "defaults": {
    "min_economic_size_usd": 5,
    "round_strategy": "round_down",
    "auto_sweep_dust": true,
    "sweep_cron": "0 4 * * *"
  },
  "locked": {
    "min_economic_size_usd": {
      "min": 1
    },
    "sweep_cron": {
      "requires_approval": true
    }
  }
}

9. Implementation Flow

  1. On receipt of ExecutionPlan from SmartRouter: round plan.size_usd to nearest valid integer pUSD unit using round_strategy.
  2. If rounded_size < min_economic_size_usd: emit DUST_WARN or DUST_HARD_REJECT; cancel plan.
  3. If remainder from PartialFillHandler is < min_economic_size_usd: cancel remainder; emit DUST_REMAINDER_CANCELLED.
  4. On sweep_cron trigger: fetch clob_auth /positions; filter positions where value_usd < min_economic_size_usd.
  5. For each dust position: if market is still open, issue a GTC sell order at mid; else wait for settlement.
  6. Emit ExecutionReport(DUST_SWEPT) per swept position with builder_code for attribution.
  7. Log all sweep actions to WAL for 7-year audit retention.

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.

// ── Real-time path: size validation ──
FUNCTION validateSize(plan):
  // Round to 6-decimal pUSD units
  IF params.round_strategy == 'round_down':
    roundedSize = floor(plan.size_usd * 1e6) / 1e6
  ELSE:
    roundedSize = round(plan.size_usd * 1e6) / 1e6

  IF roundedSize < params.hard:  // < 1 pUSD
    DISCARD plan; reason=DUST_HARD_REJECT
    RETURN
  IF roundedSize < params.min_economic_size_usd:
    EMIT ExecutionReport(DUST_WARN)
    // Still proceed (warn only above hard)
  IF roundedSize < plan.size_usd:
    plan.size_usd = roundedSize
    EMIT ExecutionReport(DUST_ROUNDED)
  RETURN plan

// ── Cron path: sweep existing dust positions ──
FUNCTION sweepDust():
  IF NOT params.auto_sweep_dust:
    RETURN
  positions = FETCH clob_auth.GET('/positions')
  IF positions IS NULL:
    EMIT WARN(DUST_SWEEP_POSITIONS_UNAVAILABLE)
    RETURN
  dustPositions = [p FOR p IN positions IF p.value_usd < params.min_economic_size_usd]
  FOR pos IN dustPositions:
    book = FETCH clob_public.GET('/book?market=' + pos.market_id)
    mid = (book.best_bid + book.best_ask) / 2
    sweepOrder = buildOrderTypedData({
      market_id: pos.market_id,
      side: 'SELL',
      price: mid,
      size: pos.size,
      builder: BUILDER_CODE,
      timestamp: now_ms()
    })
    clob_auth.POST('/order', sign(sweepOrder))
    EMIT ExecutionReport(DUST_SWEPT, pos.market_id, pos.value_usd)

SCHEDULE sweepDust AT params.sweep_cron

SDK calls used

  • clob_auth.GET('/positions')
  • clob_public.GET('/book?market=' + market_id)
  • buildOrderTypedData({side: SELL, price: mid, size, builder_code, timestamp})
  • clob_auth.POST('/order', signed_sweep_order)

Complexity: O(P) where P = number of dust positions (sweep path); O(1) real-time

11. Wire Examples

Input — what arrives on the wire

ExecutionPlan with fractional sizeexec.smart_router

{
  "market_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
  "size_usd": 5.73,
  "order_type": "GTC",
  "collateral": "pUSD",
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000"
}

Output — what the bot emits

ExecutionReport — DUST_ROUNDED

{
  "report_id": "rep_8b9c0d1e2f3a4b5c",
  "bot_id": "exec.dustandroundingcleaner",
  "original_size_usd": 5.73,
  "rounded_size_usd": 5.0,
  "verdict": "DUST_ROUNDED",
  "collateral": "pUSD",
  "evaluated_at_ms": 1746770600000
}

12. Decision Logic

APPROVE

size_usd >= min_economic_size_usd after rounding; proceed to signing.

RESHAPE_REQUIRED

Size rounded down to nearest integer unit; DUST_ROUNDED emitted.

REJECT

Rounded size < hard threshold (1 pUSD); DUST_HARD_REJECT; order or remainder cancelled.

WARNING_ONLY

Size between warning (2 pUSD) and default (5 pUSD) threshold; DUST_WARN emitted.

13. Standard Decision Output

This bot returns a ExecutionReport object. See ExecutionReport schema.

{
  "report_id": "rep_8b9c0d1e2f3a4b5c",
  "trace_id": "trc_7a8b9c0d1e2f3a4b",
  "bot_id": "exec.dustandroundingcleaner",
  "market_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
  "original_size_usd": 5.73,
  "rounded_size_usd": 5.0,
  "verdict": "DUST_ROUNDED",
  "collateral": "pUSD",
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "evaluated_at_ms": 1746770600000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
DUST_ROUNDEDRESHAPEOrder size rounded down to nearest valid integer pUSD unit.Update plan.size_usd; forward to signing.Your order size was rounded to the nearest valid unit.
DUST_WARNWARNOrder size below min_economic_size_usd but above hard threshold.Emit WARN; proceed with order (size still valid for CLOB).Your order size is very small and may not be economically optimal.
DUST_HARD_REJECTHARD_REJECTOrder size below hard threshold (1 pUSD) after rounding; not viable.Discard order; emit no submission.Your order was too small to submit.
DUST_SWEPTINFODust position swept via sell order on cron schedule.Emit ExecutionReport; log to WAL.A small leftover position was automatically sold.
DUST_REMAINDER_CANCELLEDINFOSub-minimum remainder from PartialFillHandler cancelled.Cancel remainder; emit ExecutionReport.The tiny remaining portion of your order was cancelled.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_exec_dustandroundingcleaner_decisions_totalcountercountverdictTotal decisions by verdict (ROUNDED/WARN/REJECT/SWEPT).
polytraders_exec_dustandroundingcleaner_dust_positions_swept_totalcountercountTotal dust positions swept by the cron sweep.
polytraders_exec_dustandroundingcleaner_dust_value_swept_usdhistogrampUSDDistribution of pUSD values of swept dust positions.

Alerts

AlertConditionSeverityRunbook
DRCHighDustRejectRaterate(polytraders_exec_dustandroundingcleaner_decisions_total{verdict='DUST_HARD_REJECT'}[5m]) > 0.1P2#runbook-drc-dust-reject
DRCSweepAccumulationincrease(polytraders_exec_dustandroundingcleaner_dust_positions_swept_total[24h]) > 50P3#runbook-drc-sweep-accumulation

16. Developer Reporting

{
  "original_size_usd": 5.73,
  "rounded_size_usd": 5.0,
  "round_strategy": "round_down",
  "min_economic_size_usd": 5,
  "verdict": "DUST_ROUNDED",
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000"
}

17. Plain-English Reporting

SituationUser-facing explanation
Order size roundedYour order size was rounded to the nearest valid unit before submission.
Remainder too small — cancelledThe remaining portion of your order was too small to be worth keeping open and was cancelled.
Dust position sweptA small leftover position in your account was automatically sold to clean up your portfolio.

18. Failure-Mode Block

main_failure_modeSweep order submitted at an unfavourable mid price if market is wide-spread or illiquid at sweep time, resulting in a sweep fill worse than just holding the dust to settlement.
false_positive_riskRounding down a legitimate $5.01 order to $5.00 when the minimum order unit is $1.00 is acceptable, but rounding a $1.01 order to $1.00 could trigger the warning threshold.
false_negative_riskauto_sweep_dust=false: dust accumulates indefinitely if operator does not sweep manually.
safe_fallbackIf clob_auth /positions is unavailable during sweep cron, skip the sweep cycle and emit WARN; do not retry aggressively to avoid rate-limit exhaustion on a degraded exchange.
required_dependenciesclob_auth /positions endpoint for sweep, clob_public minimum order size, PartialFillHandler for sub-minimum remainder forwarding

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
CRON_POSITIONS_UNAVAILABLEBlock clob_auth /positions at sweep cron timeAutomatic on next cron trigger
HARD_REJECT_UNDERSIZED_ORDERSubmit ExecutionPlan with size_usd=0.50Strategy corrects minimum order size
SWEEP_RATE_LIMITCreate 100 dust positions to sweep in one cron cycleAutomatic — all positions swept within sweep window

20. State & Persistence

Cold-start recovery

Rounding state stateless per order. Cron state read from Redis on restart.

21. Concurrency & Idempotency

AspectSpecification
Execution modelper-order goroutine (real-time); single-threaded cron worker (sweep)
Max in-flight50
Idempotency keyorder_id + size_usd (real-time); sweep_run_ts_ms (cron)
Per-call timeout (ms)500
Backpressure strategyCron sweep rate-limited to 5 sweep orders/s to avoid CLOB rate limit
Locking / mutual exclusionRedis SETNX for cron to prevent double-sweep if multiple instances run

22. Dependencies

Depends on (must run first)

BotWhyContract
exec.partialfillhandlerSource of sub-minimum remainders forwarded for dust handling.Receives remainder size and order_id; issues cancel.
exec.smart_routerSource of ExecutionPlan sizes to validate and round.Rounded size replaces original in plan before signing.

Emits to (downstream consumers)

BotWhyContract
gov.builder_attributionEvery sweep sell order includes builder_code for attribution.builder_code bytes32 present on all sweep orders.

External services

ServiceEndpointSLA assumedOn failure
CLOB V2 auth APIhttps://clob.polymarket.com99.95% / 200ms p99If /positions unavailable at cron time, skip sweep; emit WARN.
CLOB V2 public APIhttps://clob.polymarket.com99.9% / 200ms p99If book unavailable for sweep price, skip that position; retry next cron cycle.

23. Security Surfaces

Abuse vectors considered

  • Triggering the sweep cron repeatedly via schedule manipulation to generate excessive CLOB sell orders
  • Injecting a position report with inflated value_usd to prevent legitimate dust positions from being swept

Mitigations

  • Cron uses Redis SETNX to prevent double-sweep; sweep rate limited to 5 sell orders/s
  • Position list fetched directly from clob_auth; not from a cache that could be tampered with

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 sizes rounded to 6-decimal pUSD integer units as required by CTFExchangeV2. Sweep sell orders include builder_code bytes32 for attribution on Polygon.

API surfaces declared

clob_authclob_publicinternal

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
Round-down applied to 5.73 pUSDsize_usd=5.73, round_strategy=round_downrounded_size_usd=5.0; DUST_ROUNDED emitted
HARD_REJECT when rounded size < 1 pUSDsize_usd=0.80rounded_size_usd=0; DUST_HARD_REJECT; order cancelled
Sweep: identify positions < min_economic_size_usdposition value=3.50 pUSD, min_economic_size=5Position flagged for sweep; GTC sell order submitted at mid

Integration Tests

TestExpected result
Cron sweep: fetch positions, filter dust, submit sell orders, emit ExecutionReportsAll dust positions swept; DUST_SWEPT emitted per position; WAL entries written
Remainder from PartialFillHandler: 2.50 pUSD remainder forwarded and cancelledDUST_REMAINDER_CANCELLED emitted; no remainder order left on book

Property Tests

PropertyRequired behaviour
rounded_size_usd always <= original_size_usd when round_strategy=round_downAlways true
Sweep orders carry builder_code for attributionAlways true

27. Operational Runbook

DustAndRoundingCleaner incidents are either high DUST_HARD_REJECT rates (strategy generating undersized orders) or sweep accumulation (dust positions building up faster than cron clears them).

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
DRCHighDustRejectRateIdentify strategy generating undersized orders; check minimum size configuration.Strategy pod lead
DRCSweepAccumulationCheck sweep cron execution log; if cron missed due to clob_auth outage, trigger manual sweep.Exec pod lead

Manual overrides

  • polytraders bot trigger-sweep exec.dustandroundingcleaner — Manual sweep needed outside scheduled cron time; e.g. after a period of high partial fills.

Healthcheck

GET /internal/health/dustandroundingcleaner -> 200 if clob_auth reachable, last_sweep < 25h ago, hard_reject_rate < 0.01/min. Red: last_sweep > 25h ago, hard_reject_rate > 0.1/min, cron not firing.

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
Round-down and hard-reject unit tests pass with known edge casesCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
Sweep cron executes cleanly for 7 consecutive days in shadow rundust_positions_swept_total increments daily; no sweep errors7/7 clean sweeps

Promote to General live

GateHow measuredThreshold
Zero DUST_HARD_REJECT false positives over 7-day limited-live (i.e. no rejections of legitimately sized orders)Audit log of DUST_HARD_REJECT events cross-checked with original strategy intent sizesZero 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