{"record":{"id":"72aeed0724c25fa5","repo":"nautechsystems/nautilus_trader","slug":"name-must-be-greater-than-or-equal-to-zero","errorCode":null,"errorMessage":"`{name}` must be greater than or equal to zero","messagePattern":"`(.+?)` must be greater than or equal to zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/models/fee.rs","lineNumber":648,"sourceCode":"\nfn option_fee_rate(\n    order: &OrderAny,\n    instrument: &InstrumentAny,\n    maker_rate: Option<Decimal>,\n    taker_rate: Option<Decimal>,\n) -> anyhow::Result<Decimal> {\n    let rate = match order.liquidity_side() {\n        Some(LiquiditySide::Maker) => maker_rate.unwrap_or_else(|| instrument.maker_fee()),\n        Some(LiquiditySide::Taker) => taker_rate.unwrap_or_else(|| instrument.taker_fee()),\n        Some(LiquiditySide::NoLiquiditySide) | None => anyhow::bail!(\"Liquidity side not set\"),\n    };\n    check_fee_rate(Some(rate), \"fee_rate\")?;\n    Ok(rate)\n}\n\nfn check_fee_rate(rate: Option<Decimal>, name: &str) -> anyhow::Result<()> {\n    if rate.is_some_and(|rate| rate < Decimal::ZERO) {\n        anyhow::bail!(\"`{name}` must be greater than or equal to zero\");\n    }\n    Ok(())\n}\n\nfn check_option_instrument(instrument: &InstrumentAny, model_name: &str) -> anyhow::Result<()> {\n    if !matches!(\n        instrument,\n        InstrumentAny::CryptoOption(_) | InstrumentAny::OptionContract(_)\n    ) {\n        anyhow::bail!(\"{model_name} requires an option instrument\");\n    }\n    Ok(())\n}\n\nfn commission_currency(instrument: &InstrumentAny) -> Currency {\n    if instrument.is_inverse() {\n        instrument.settlement_currency()\n    } else {","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/models/fee.rs#L630-L666","documentation":"The internal `check_fee_rate` helper validates that an optional fee rate is not negative, formatting the error with the parameter name (`{name}` must be >= 0). Fee rates (maker/taker percentages) must be non-negative; negative values are rejected at model construction or rate resolution.","triggerScenarios":"Constructing an option fee model via `new` with a negative maker_fee or taker_fee, or calling `option_fee_rate` with an override rate < 0.","commonSituations":"Configuring maker rebates as plain negative rates instead of using a rebate mechanism; sign typo in config (e.g. -0.0002 instead of 0.0002); converting a signed spread/basis into a rate.","solutions":["Correct the sign of the maker/taker rate passed to the model constructor or override","If a maker rebate is intended, verify the model/venue supports rebates and use the proper mechanism","Validate fee-rate config (reject negatives) at load time with a clear message"],"exampleFix":"// before\nlet model = OptionFeeModel::new(Some(Decimal::new(-2, 4)), None)?; // -0.0002\n// after\nlet maker_rate = Decimal::new(2, 4); // 0.0002\nassert!(maker_rate >= Decimal::ZERO);\nlet model = OptionFeeModel::new(Some(maker_rate), None)?;","handlingStrategy":"validation","validationCode":"for (name, rate) in [(\"maker_fee\", maker_fee), (\"taker_fee\", taker_fee)] {\n    if let Some(r) = rate {\n        if r < Decimal::ZERO { return Err(anyhow::anyhow!(\"{name} must be >= 0\")); }\n    }\n}","typeGuard":"fn valid_rate(r: Option<Decimal>) -> bool { r.map_or(true, |v| v >= Decimal::ZERO) }","tryCatchPattern":"let model = OptionFeeModel::new(maker_fee, taker_fee)\n    .context(\"invalid option fee rates\")?;","preventionTips":["Reject negative rates in fee config loaders with named-field messages","Handle maker rebates via dedicated mechanisms, not negative rates","Add property tests that fee rates are always non-negative"],"tags":["rust","fee-model","validation","negative-value"],"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-14T00:17:10.932Z"}