{"record":{"id":"0dbf884e41c6f87e","repo":"nautechsystems/nautilus_trader","slug":"commission-must-be-greater-than-or-equal-to-zero","errorCode":null,"errorMessage":"Commission must be greater than or equal to zero","messagePattern":"Commission must be greater than or equal to zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/models/fee.rs","lineNumber":266,"sourceCode":"        extends = PyFeeModel,\n        skip_from_py_object\n    )\n)]\npub struct FixedFeeModel {\n    commission: Money,\n    zero_commission: Money,\n    charge_commission_once: bool,\n}\n\nimpl FixedFeeModel {\n    /// Creates a new [`FixedFeeModel`] instance.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if `commission` is negative.\n    pub fn new(commission: Money, charge_commission_once: Option<bool>) -> anyhow::Result<Self> {\n        if commission.raw < 0 {\n            anyhow::bail!(\"Commission must be greater than or equal to zero\")\n        }\n        let zero_commission = Money::zero(commission.currency);\n        Ok(Self {\n            commission,\n            zero_commission,\n            charge_commission_once: charge_commission_once.unwrap_or(true),\n        })\n    }\n}\n\nimpl FeeModel for FixedFeeModel {\n    fn get_commission(\n        &self,\n        order: &OrderAny,\n        _fill_quantity: Quantity,\n        _fill_px: Price,\n        _instrument: &InstrumentAny,\n    ) -> anyhow::Result<Money> {","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/models/fee.rs#L248-L284","documentation":"PerpetualFeeModel::new (or similar fee model constructor) validates that the commission amount passed in is non-negative. A negative Money value would produce nonsensical (negative) fees, so the constructor rejects it with anyhow::bail. This is a fail-fast guard at model construction time.","triggerScenarios":"Calling the fee model's `new(commission, charge_commission_once)` with a `Money` whose `raw` decimal is < 0, e.g. `Money::new(-1.5, Currency::USD())` or a commission computed from a negative rate.","commonSituations":"Loading commission from config where a sign was mistyped, computing commission as a signed delta instead of an absolute value, or a currency conversion/rounding pipeline that flipped the sign.","solutions":["Inspect the Money value passed as `commission` and correct its sign before constructing the model","Clamp or validate at the config boundary: reject negative commission inputs when parsing user config","If the rate is legitimately signed, apply the sign to the notional calculation instead of the commission"],"exampleFix":"// before\nlet model = PerpetualFeeModel::new(Money::new(-Decimal::new(15, 1), Currency::USD()), None)?;\n// after\nlet commission = Money::new(Decimal::new(15, 1), Currency::USD());\nassert!(commission.raw >= Decimal::ZERO);\nlet model = PerpetualFeeModel::new(commission, None)?;","handlingStrategy":"validation","validationCode":"if commission.raw < Decimal::ZERO {\n    return Err(anyhow::anyhow!(\"commission must be >= 0, got {}\", commission));\n}","typeGuard":"fn is_non_negative(m: &Money) -> bool { m.raw >= Decimal::ZERO }","tryCatchPattern":"match FeeModel::new(commission, once) {\n    Ok(model) => model,\n    Err(e) => { log::error!(\"fee model init failed: {e}\"); return Err(e); }\n}","preventionTips":["Validate fee amounts when loading strategy/venue config","Use unsigned or validated builders for Money fee inputs","Add unit tests covering negative-commission construction"],"tags":["rust","validation","fee-model","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"}