{"record":{"id":"989b73302e423fca","repo":"nautechsystems/nautilus_trader","slug":"fee-exponent-must-be-non-negative","errorCode":null,"errorMessage":"fee exponent must be non-negative","messagePattern":"fee exponent must be non-negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/execution/parse.rs","lineNumber":480,"sourceCode":"pub fn instrument_fee_exponent(instrument: &InstrumentAny) -> anyhow::Result<Decimal> {\n    let value = match instrument {\n        InstrumentAny::BinaryOption(bo) => {\n            bo.info.as_ref().and_then(|info| info.get(\"fee_schedule\"))\n        }\n        _ => None,\n    };\n    let Some(schedule) = value else {\n        return Ok(Decimal::ONE);\n    };\n    let value = schedule\n        .get(\"exponent\")\n        .context(\"fee schedule is missing exponent\")?;\n    let exponent = match value {\n        serde_json::Value::String(value) => parse_decimal_exact(value)?,\n        serde_json::Value::Number(value) => parse_decimal_exact(&value.to_string())?,\n        _ => anyhow::bail!(\"fee exponent must be a decimal number or numeric string\"),\n    };\n    anyhow::ensure!(\n        exponent >= Decimal::ZERO,\n        \"fee exponent must be non-negative\"\n    );\n    Ok(exponent)\n}\n\n/// Adjusts a market-BUY pUSD amount to fit within the user's pUSD balance once\n/// platform and builder taker fees are deducted. Mirrors `adjust_market_buy_amount`\n/// in `polymarket-rs-clob-client-v2`'s `clob/utilities.rs`.\n///\n/// Returns `amount` unchanged when the balance already covers `amount + fees`.\n/// Otherwise solves for the principal that, with fees, exactly consumes the\n/// balance, then truncates to `USDC_DECIMALS` (the on-chain pUSD scale).\n///\n/// The fee-curve step `(p * (1 - p))^exponent` is the only computation that\n/// crosses into `f64`, matching the reference SDK so we agree with the\n/// venue's authoritative match-time fee calculation regardless of whether\n/// Polymarket ships a fractional exponent in the future.","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/execution/parse.rs#L462-L498","documentation":"instrument_fee_exponent extracts the fee exponent from the venue fee schedule JSON and requires it to be a numeric value >= 0. The exponent drives the fee curve rate; a negative exponent would produce nonsensical fee scaling, so the parser bails with \"fee exponent must be non-negative\".","triggerScenarios":"Parsing a Polymarket fee schedule whose exponent field is a negative number (string or JSON number), via calculate_commission, build_admitted_target_fill, build_fill_reports_from_trades, build_ws_taker_fill_report, or submit_market_order.","commonSituations":"Stale or hand-edited cached fee-schedule JSON; a venue fee-model change emitting a different exponent convention; test fixtures with negative exponent values.","solutions":["Check the fee schedule payload's exponent field and correct it to a non-negative value.","Refresh the fee schedule from the venue (delete stale cached fee config and re-fetch).","Confirm exponent conventions: if the schedule is meant to express 10^-n rates, store n as a non-negative integer."],"exampleFix":"// before\n{\"exponent\": \"-2\"}  // negative -> rejected\n// after\n{\"exponent\": \"2\"}   // non-negative fee-curve exponent","handlingStrategy":"validation","validationCode":"let exponent: Decimal = parse_decimal_exact(schedule[\"exponent\"].as_str().unwrap_or_default())?;\nif exponent < Decimal::ZERO { return Err(\"fee schedule exponent must be >= 0\".into()); }","typeGuard":"fn has_valid_fee_exponent(schedule: &serde_json::Value) -> bool {\n    schedule.get(\"exponent\")\n        .and_then(|v| v.as_str().or_else(|| v.as_number().map(|n| n.to_string().into())).map(|s| s.parse::<Decimal>().map(|d| d >= Decimal::ZERO).unwrap_or(false)))\n        .unwrap_or(false)\n}","tryCatchPattern":"let exponent = match instrument_fee_exponent(&schedule) {\n    Ok(e) => e,\n    Err(e) => { tracing::warn!(\"bad fee schedule: {e:#}; refreshing from venue\"); refresh_fee_schedule().await?; }\n};","preventionTips":["Re-fetch the fee schedule from the venue instead of trusting long-lived caches.","Validate the whole fee schedule JSON schema once at load.","Never hand-edit cached fee schedule files."],"tags":["polymarket","fee-schedule","validation","parsing"],"backgroundTag":"invalid-config-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"}