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 LayerRisk1.14 FeeAndGasGuard

1.14 FeeAndGasGuard

Risk Guardrail VetoReshape PLANNED Planned capital · Direct P4 · Core risk pending stub

FeeAndGasGuard estimates the total transaction cost (CLOB maker/taker fee plus Polygon gas) for a proposed order and rejects or downsizes it when the cost-to-edge ratio exceeds the configured ceiling. It prevents strategies from placing orders where fee drag exceeds the expected edge, turning a positive-expected-value intent into a negative-EV execution.

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

LayerRisk  Risk
Bot classGuardrail
AuthorityVetoReshape
StatusPLANNED
ReadinessPlanned
Runs beforeExecutionPlan emit
Runs afterStrategy OrderIntent
Applies toEvery OrderIntent — refuses trades whose realistic fee and Polygon gas burden consumes more than the configured fraction of expected edge
Default modeplanned
User-visiblesummary-only
Developer ownerPolytraders core — Risk pod

Operational profile

Modes supportedquarantine

2. Purpose

FeeAndGasGuard estimates the total transaction cost (CLOB maker/taker fee plus Polygon gas) for a proposed order and rejects or downsizes it when the cost-to-edge ratio exceeds the configured ceiling. It prevents strategies from placing orders where fee drag exceeds the expected edge, turning a positive-expected-value intent into a negative-EV execution.

3. Why This Bot Matters

  • Fee drag exceeds edge

    Placing an order where fees exceed the expected edge results in a systematically negative-EV trade that erodes capital over many repetitions.

  • Gas spike not accounted for

    Polygon gas spikes during congestion can materially increase transaction cost; ignoring gas makes fee estimates unreliable during high-traffic periods.

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 fee rate for the target market (operator-set, bps)clob_publicYesCompute taker or maker fee for the proposed order size and probability.
Current Polygon gas price estimateonchainYesEstimate the Polygon gas cost for the CTFExchangeV2.matchOrders() settlement transaction.
Current market probability (best bid/ask midpoint)clob_publicYesCompute the fee using the V2 formula: C * feeRate * p * (1-p), which peaks at p=0.5.

5. Required Internal Inputs

InputSourceRequired?Use
Strategy expected edge (bps) per orderinternalYesCompare against the estimated fee-to-notional ratio to enforce the max_fee_to_edge_ratio limit.
KillSwitch active flagKillSwitchYesIf active, reject immediately.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
max_fee_to_edge_ratio0.50.350.5Maximum ratio of total transaction cost (fee + gas) to expected edge. Orders where cost/edge > this threshold are blocked or downsized.
max_fee_bps10075100Absolute maximum fee rate in basis points regardless of the edge ratio. Protects against operator-set fee spikes on specific markets.
min_order_usd10NoneNoneMinimum order size in pUSD. Below this threshold, fixed gas costs dominate the fee structure and the cost-to-edge ratio is always unfavourable.

7. Detailed Parameter Instructions

max_fee_to_edge_ratio

What it means

Maximum ratio of total transaction cost (fee + gas) to expected edge. Orders where cost/edge > this threshold are blocked or downsized.

Default

{ "max_fee_to_edge_ratio": 0.5 }

Why this default matters

At 0.5, at least 50% of the expected edge survives after costs; below this threshold execution becomes net-negative in expectation.

Threshold logic

ConditionAction
cost_to_edge <= 0.35APPROVE
0.35 < cost_to_edge <= 0.5WARN — FEE_GUARD_COST_APPROACHING
cost_to_edge > 0.5HARD_REJECT — FEE_GUARD_COST_EXCEEDS_EDGE

Developer check

if (totalCost / expectedEdge > params.max_fee_to_edge_ratio) return reject('FEE_GUARD_COST_EXCEEDS_EDGE');

User-facing English

This trade's fees would consume too much of the expected gain.

max_fee_bps

What it means

Absolute maximum fee rate in basis points regardless of the edge ratio. Protects against operator-set fee spikes on specific markets.

Default

{ "max_fee_bps": 100 }

Why this default matters

100 bps is the taker fee ceiling defined in the V2 protocol; orders on markets above this rate indicate a misconfiguration or anomalous market.

Threshold logic

ConditionAction
effective_fee_bps <= 75APPROVE
75 < effective_fee_bps <= 100WARN
effective_fee_bps > 100HARD_REJECT — FEE_GUARD_RATE_ANOMALY

Developer check

if (effectiveFeeBps > params.max_fee_bps) return reject('FEE_GUARD_RATE_ANOMALY');

User-facing English

The fee rate for this market is unusually high. The order was blocked to protect against unexpected costs.

min_order_usd

What it means

Minimum order size in pUSD. Below this threshold, fixed gas costs dominate the fee structure and the cost-to-edge ratio is always unfavourable.

Default

{ "min_order_usd": 10 }

Why this default matters

Gas costs are fixed per transaction; below 10 pUSD the gas component alone can exceed the expected edge on a typical order.

Threshold logic

ConditionAction
intent.size_usd >= 10Proceed with fee check
intent.size_usd < 10HARD_REJECT — FEE_GUARD_ORDER_TOO_SMALL

Developer check

if (intent.size_usd < params.min_order_usd) return reject('FEE_GUARD_ORDER_TOO_SMALL');

User-facing English

The order size is too small to trade economically after fees. Please use a larger order size.

8. Default Configuration

{
  "bot_id": "risk.fee_and_gas_guard",
  "version": "0.1.0",
  "mode": "hard_guard",
  "defaults": {
    "max_fee_to_edge_ratio": 0.5,
    "max_fee_bps": 100,
    "min_order_usd": 10
  },
  "locked": {
    "max_fee_bps": {
      "max": 100
    },
    "min_order_usd": {
      "min": 1
    }
  }
}

9. Implementation Flow

  1. Receive OrderIntent with market_id, side, size_usd, price, and expected_edge_bps from strategy.
  2. Check KillSwitch; if active, HARD_REJECT(KILL_SWITCH_ACTIVE).
  3. Fetch current market probability p = (best_ask + best_bid) / 2 from CLOB.
  4. Fetch operator-set fee rate for the market from CLOB (taker: max 100 bps; maker: max 50 bps).
  5. Compute CLOB fee: fee_usd = size_usd * fee_rate_bps / 10000 * p * (1-p).
  6. Fetch current Polygon gas price; estimate gas cost for CTFExchangeV2.matchOrders().
  7. Compute total_cost_usd = fee_usd + gas_cost_usd.
  8. If intent.size_usd < min_order_usd, HARD_REJECT(FEE_GUARD_ORDER_TOO_SMALL).
  9. If effective_fee_bps > max_fee_bps, HARD_REJECT(FEE_GUARD_RATE_ANOMALY).
  10. Compute edge_usd = size_usd * expected_edge_bps / 10000.
  11. If total_cost_usd / edge_usd > max_fee_to_edge_ratio, HARD_REJECT(FEE_GUARD_COST_EXCEEDS_EDGE).
  12. APPROVE with fee_estimate_usd and cost_to_edge_ratio attached.

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 evaluateFeeAndGas(intent):
  ks = FETCH internal.killswitch.status
  IF ks.active: EMIT RiskVote(HARD_REJECT, KILL_SWITCH_ACTIVE); RETURN

  IF intent.size_usd < params.min_order_usd:
    EMIT RiskVote(HARD_REJECT, FEE_GUARD_ORDER_TOO_SMALL); RETURN

  feeRate = FETCH clob_public.fee_rate(intent.market_id)
  prices = FETCH clob_public.prices(intent.market_id)
  gasPrice = FETCH onchain.gas_price()
  IF feeRate IS NULL OR prices IS NULL OR gasPrice IS NULL:
    EMIT RiskVote(HARD_REJECT, FEE_GUARD_DATA_UNAVAILABLE); RETURN

  IF feeRate.bps > params.max_fee_bps:
    EMIT RiskVote(HARD_REJECT, FEE_GUARD_RATE_ANOMALY); RETURN

  p = (prices.ask + prices.bid) / 2
  feeUsd = intent.size_usd * (feeRate.bps / 10000) * p * (1 - p)
  gasUsd = estimateGasCost(gasPrice, 'matchOrders')
  totalCost = feeUsd + gasUsd

  edgeUsd = intent.size_usd * (intent.expected_edge_bps / 10000)
  ratio = totalCost / edgeUsd

  IF ratio > params.max_fee_to_edge_ratio:
    EMIT RiskVote(HARD_REJECT, FEE_GUARD_COST_EXCEEDS_EDGE,
                  total_cost=totalCost, ratio=ratio); RETURN
  IF ratio > params.max_fee_to_edge_ratio * 0.7:
    annotations.append(WARN(FEE_GUARD_COST_APPROACHING, ratio=ratio))

  EMIT RiskVote(APPROVE, fee_estimate_usd=totalCost, cost_to_edge=ratio)

SDK calls used

  • clob_public.fee_rate(market_id)
  • clob_public.prices(market_id)
  • onchain.gas_price()
  • internal.killswitch.status()

Complexity: O(1)

11. Wire Examples

Input — what arrives on the wire

OrderIntent — fee checkinternal

{
  "intent_id": "int_f6a7b8c9d0e10006",
  "market_id": "0x4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e",
  "size_usd": 150,
  "price": 0.5,
  "expected_edge_bps": 40,
  "generated_at_ms": 1746800000000
}

Output — what the bot emits

RiskVote — HARD_REJECT cost exceeds edge

{
  "guard_id": "risk.fee_and_gas_guard",
  "decision": "HARD_REJECT",
  "severity": "HARD",
  "reason_code": "FEE_GUARD_COST_EXCEEDS_EDGE",
  "message": "Cost 4.20 pUSD / edge 6.00 pUSD = ratio 0.70 exceeds ceiling 0.50.",
  "constraints": {},
  "checked_at": "2026-05-10T13:00:00Z"
}

12. Decision Logic

APPROVE

Total transaction cost is within the max_fee_to_edge_ratio and both fee_bps and order size are within limits.

RESHAPE_REQUIRED

Not currently implemented; fee-to-edge ratio cannot be improved by resizing (it is largely size-independent for the CLOB fee formula). Future version may restrict to maker-only to reduce fee.

REJECT

Fee-to-edge ratio exceeds the hard ceiling, fee rate is anomalously high, order size is below min_order_usd, or KillSwitch is active.

WARNING_ONLY

— not yet authored —

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "guard_id": "risk.fee_and_gas_guard",
  "decision": "HARD_REJECT",
  "severity": "HARD",
  "reason_code": "FEE_GUARD_COST_EXCEEDS_EDGE",
  "message": "Estimated cost 4.20 pUSD (ratio 0.70) exceeds max_fee_to_edge_ratio 0.50 for edge 6.00 pUSD.",
  "constraints": {},
  "inputs_used": [
    "clob_public.fee_rate",
    "clob_public.prices",
    "onchain.gas_price",
    "internal.expected_edge"
  ],
  "checked_at": "2026-05-10T13:00:00Z"
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch active.Immediate HARD_REJECT.Trading is paused. Please try again later.
FEE_GUARD_COST_EXCEEDS_EDGEHARD_REJECTTotal cost-to-edge ratio exceeds the hard ceiling.HARD_REJECT; log total_cost_usd, edge_usd, and ratio.This trade's fees would consume too much of the expected gain.
FEE_GUARD_RATE_ANOMALYHARD_REJECTEffective fee rate exceeds max_fee_bps (V2 protocol ceiling).HARD_REJECT; alert on-call — anomalous fee rate detected.The fee rate for this market is unusually high.
FEE_GUARD_ORDER_TOO_SMALLHARD_REJECTOrder size is below min_order_usd; gas costs dominate.HARD_REJECT.The order size is too small to be economical after fees.
FEE_GUARD_COST_APPROACHINGWARNCost-to-edge ratio between warning and hard threshold.Attach WARN annotation; APPROVE.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_risk_feeandgasguard_decisions_totalcountercountdecision, reason_codeTotal decisions by type and reason.
polytraders_risk_feeandgasguard_cost_to_edge_ratiogaugeratiomarket_idCurrent cost-to-edge ratio at last evaluation per market.
polytraders_risk_feeandgasguard_gas_cost_usdgaugeusdEstimated Polygon gas cost in pUSD at last evaluation.
polytraders_risk_feeandgasguard_eval_latency_mshistogrammillisecondsLatency from intent to RiskVote emit.

Alerts

AlertConditionSeverityRunbook
FeeAndGasGuardGasSpikepolytraders_risk_feeandgasguard_gas_cost_usd > 2.0P2#runbook-feeandgas-gas
FeeAndGasGuardRateAnomalyrate(polytraders_risk_feeandgasguard_decisions_total{reason_code='FEE_GUARD_RATE_ANOMALY'}[5m]) > 0P1#runbook-feeandgas-anomaly

16. Developer Reporting

{
  "bot_id": "risk.fee_and_gas_guard",
  "decision": "HARD_REJECT",
  "reason_code": "FEE_GUARD_COST_EXCEEDS_EDGE",
  "inputs_used": [
    "clob_public.fee_rate",
    "clob_public.prices",
    "onchain.gas_price"
  ],
  "metrics": {
    "fee_usd": 3.8,
    "gas_usd": 0.4,
    "total_cost_usd": 4.2,
    "edge_usd": 6.0,
    "cost_to_edge_ratio": 0.7,
    "fee_rate_bps": 60,
    "prob": 0.5
  },
  "checked_at": "2026-05-10T13:00:00Z"
}

17. Plain-English Reporting

SituationUser-facing explanation
Order blocked — fees exceed edgeThe estimated fees for this trade would consume more than half of the expected gain. The order was blocked to protect your returns.
Order blocked — fee rate anomalyThe fee rate for this market is unusually high. The order was blocked to protect against unexpected costs.
Order blocked — order too smallOrders below the minimum size are not economical after fees. Please increase the order size.

18. Failure-Mode Block

main_failure_modeApproving an order because the expected_edge estimate from the strategy is overstated, making the cost-to-edge ratio appear acceptable when it is not.
false_positive_riskRejecting a valid order because the Polygon gas price estimate is temporarily inflated during congestion.
false_negative_riskApproving an order because the fee rate cache is stale and does not reflect a recent operator fee increase.
safe_fallbackIf fee rate or gas price data is unavailable, HARD_REJECT with FEE_GUARD_DATA_UNAVAILABLE. Never approve on missing fee data.
required_dependenciesCLOB fee rate endpoint, Polygon gas price oracle, CLOB market prices, Strategy expected_edge, KillSwitch active flag

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
GAS_PRICE_UNAVAILABLEBlock onchain gas oracleReturns to normal once gas oracle is reachable.
FEE_RATE_ANOMALYSet mock fee_rate.bps=150 in CLOB mockReturns to normal once fee rate is corrected.
HIGH_GAS_COSTSet gas price 10x normal in mockReturns to APPROVE once gas price normalises.

20. State & Persistence

Cold-start recovery

On cold start, both caches populated from CLOB and onchain oracle before first evaluation.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded event loop
Max in-flight300
Idempotency keyintent_id
Per-call timeout (ms)100
Backpressure strategydrop newest
Locking / mutual exclusionfee and gas caches are read-only at evaluation; writes use TTL-based replacement

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchGlobal brake checked first.HARD_REJECT(KILL_SWITCH_ACTIVE) short-circuits all fee evaluation.

Emits to (downstream consumers)

BotWhyContract
exec.smart_routerApproved RiskVote passes to SmartRouter.APPROVE passes; HARD_REJECT discards intent.

External services

ServiceEndpointSLA assumedOn failure
CLOB API (fee rates + prices)https://clob.polymarket.com99.95% / 200ms p99HARD_REJECT(FEE_GUARD_DATA_UNAVAILABLE) if fee data unavailable.
Polygon gas oracleonchain gas price feedbest-effortHARD_REJECT(FEE_GUARD_DATA_UNAVAILABLE) if gas price unavailable.

23. Security Surfaces

Abuse vectors considered

  • Inflating expected_edge_bps in the intent to make the cost-to-edge ratio appear acceptable
  • Submitting during low-gas windows to bypass gas-inclusive cost checks, then executing during high-gas periods

Mitigations

  • expected_edge_bps is validated against a per-strategy cap from the internal config; values above the cap are clipped
  • Gas price is fetched at evaluation time, not at intent generation time

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
NotesFee computation uses V2 formula: C * feeRate * p * (1-p). Fees are operator-set at match time (not on signed order). Taker max 100 bps; maker max 50 bps; 1 bp granularity.

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
Approve when cost-to-edge within thresholdtotal_cost=2.0, edge=6.0, ratio=0.33APPROVE
Reject when cost-to-edge exceeds ceilingtotal_cost=4.2, edge=6.0, ratio=0.70HARD_REJECT(FEE_GUARD_COST_EXCEEDS_EDGE)
Reject when fee_bps > max_fee_bpseffective_fee_bps=120, max=100HARD_REJECT(FEE_GUARD_RATE_ANOMALY)
Reject when order below min_order_usdsize_usd=5, min=10HARD_REJECT(FEE_GUARD_ORDER_TOO_SMALL)

Integration Tests

TestExpected result
Gas spike causes rejection in live environmentHARD_REJECT(FEE_GUARD_COST_EXCEEDS_EDGE) when Polygon gas price increases significantly during congestion
KillSwitch bypasses fee checkHARD_REJECT(KILL_SWITCH_ACTIVE) without fetching fee or gas data

Property Tests

PropertyRequired behaviour
Approved order always has cost-to-edge ratio <= max_fee_to_edge_ratioAlways true
Missing fee data never results in APPROVEAlways true — fail-closed on data unavailability

27. Operational Runbook

FeeAndGasGuard incidents typically involve a gas spike or an anomalous fee rate on a newly listed market. Verify gas price normality before adjusting parameters.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
FeeAndGasGuardGasSpikeCheck Polygon gas price on external tracker; confirm spike is network-wide, not a data feed error.Infra on-call if gas oracle is misbehaving.
FeeAndGasGuardRateAnomalyIdentify market_id from reason_code log; check CLOB fee rate endpoint for that market directly.Risk pod lead if fee rate is genuinely anomalous.

Manual overrides

  • polytraders risk set-gas-override --gas-usd <value> --duration 300s — During a gas oracle outage; sets a conservative fixed gas estimate to unblock orders.

Healthcheck

GET /internal/health/feeandgasguard → green: CLOB fee rate cache age < 60s, gas price cache age < 15s, no FEE_GUARD_RATE_ANOMALY in last 5m; red: Fee or gas data cache expired or unavailable

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 for fee calculation and anomaly detectionCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
Shadow cost-to-edge ratios match expected distribution over 48hGrafana cost_to_edge_ratio gauge histogramNo spurious rejections in shadow

Promote to General live

GateHow measuredThreshold
Fee formula verified against V2 protocol spec for 5 reference marketsManual verification against CLOB API docs100% match

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