{"record":{"id":"fd5508bed899a1e0","repo":"nautechsystems/nautilus_trader","slug":"polymarket-fee-rate-must-be-greater-than-or-equal","errorCode":null,"errorMessage":"Polymarket fee rate must be greater than or equal to zero","messagePattern":"Polymarket fee rate must be greater than or equal to zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/models.rs","lineNumber":118,"sourceCode":"                .round_dp(5),\n            LiquiditySide::Taker => fee_equivalent.round_dp(5),\n            LiquiditySide::NoLiquiditySide => unreachable!(),\n        };\n\n        Money::from_decimal(commission, instrument.quote_currency()).map_err(Into::into)\n    }\n}\n\nfn validate_schedule(schedule: &FeeSchedule) -> anyhow::Result<()> {\n    if schedule.exponent != Decimal::ONE {\n        anyhow::bail!(\n            \"PolymarketFeeModel requires fee schedule exponent 1, was {}\",\n            schedule.exponent\n        );\n    }\n\n    if schedule.rate < Decimal::ZERO {\n        anyhow::bail!(\"Polymarket fee rate must be greater than or equal to zero\");\n    }\n\n    if !(Decimal::ZERO..=Decimal::ONE).contains(&schedule.rebate_rate) {\n        anyhow::bail!(\"Polymarket rebate rate must be in [0, 1]\");\n    }\n\n    if !schedule.taker_only {\n        anyhow::bail!(\"PolymarketFeeModel requires a taker-only fee schedule\");\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use nautilus_core::UnixNanos;\n    use nautilus_execution::models::fee::{FeeModel, FeeModelHandle};\n    use nautilus_model::{\n        enums::{LiquiditySide, OrderSide, OrderType},","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/models.rs#L100-L136","documentation":"validate_schedule rejects fee schedules with a negative rate, since a fee rate below zero is meaningless for this model (it would turn commission into a payout). The rate comes from the market's fee schedule in the binary option's instrument info.","triggerScenarios":"get_commission encounters a FeeSchedule whose rate < 0 — from a malformed CLOB API response, bad manual fixture, or sign error when constructing the schedule.","commonSituations":"Hand-written test fixtures with negative rates; API/schema changes altering how the rate is encoded; data pipelines mangling the sign.","solutions":["Inspect the fee schedule source (instrument info) and correct the negative rate value.","Validate fee schedule data at instrument-build time so bad schedules are rejected earlier.","Check whether the API response was misparsed (e.g. a rebate misread as a rate).","Fix test fixtures to use non-negative rates."],"exampleFix":"// before\nlet schedule = FeeSchedule { rate: Decimal::new(-5, 3), .. };\n// after\nlet schedule = FeeSchedule { rate: Decimal::new(5, 3), .. }; // 0.005","handlingStrategy":"validation","validationCode":"if schedule.rate < Decimal::ZERO {\n    return Err(\"negative fee rate in schedule\".into());\n}","typeGuard":"fn has_valid_rate(s: &FeeSchedule) -> bool {\n    s.rate >= Decimal::ZERO\n}","tryCatchPattern":"match fee_model.get_commission(&order, qty, px, &instrument) {\n    Ok(fee) => fee,\n    Err(e) if e.to_string().contains(\"greater than or equal to zero\") => {\n        log::error!(\"corrupt fee schedule (negative rate); refusing to trade\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate fee schedules when building instruments, not at trade time.","Verify API-to-Decimal parsing preserves signs and scales.","Review hand-written fixtures for plausible values."],"tags":["fees","validation","polymarket"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}