nautechsystems/nautilus_trader · error
`{name}` must be greater than or equal to zero
Error message
`{name}` must be greater than or equal to zero What it means
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.
Source
Thrown at crates/execution/src/models/fee.rs:648
fn option_fee_rate(
order: &OrderAny,
instrument: &InstrumentAny,
maker_rate: Option<Decimal>,
taker_rate: Option<Decimal>,
) -> anyhow::Result<Decimal> {
let rate = match order.liquidity_side() {
Some(LiquiditySide::Maker) => maker_rate.unwrap_or_else(|| instrument.maker_fee()),
Some(LiquiditySide::Taker) => taker_rate.unwrap_or_else(|| instrument.taker_fee()),
Some(LiquiditySide::NoLiquiditySide) | None => anyhow::bail!("Liquidity side not set"),
};
check_fee_rate(Some(rate), "fee_rate")?;
Ok(rate)
}
fn check_fee_rate(rate: Option<Decimal>, name: &str) -> anyhow::Result<()> {
if rate.is_some_and(|rate| rate < Decimal::ZERO) {
anyhow::bail!("`{name}` must be greater than or equal to zero");
}
Ok(())
}
fn check_option_instrument(instrument: &InstrumentAny, model_name: &str) -> anyhow::Result<()> {
if !matches!(
instrument,
InstrumentAny::CryptoOption(_) | InstrumentAny::OptionContract(_)
) {
anyhow::bail!("{model_name} requires an option instrument");
}
Ok(())
}
fn commission_currency(instrument: &InstrumentAny) -> Currency {
if instrument.is_inverse() {
instrument.settlement_currency()
} else {View on GitHub (pinned to 18893faf8b)
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
Example fix
// before let model = OptionFeeModel::new(Some(Decimal::new(-2, 4)), None)?; // -0.0002 // after let maker_rate = Decimal::new(2, 4); // 0.0002 assert!(maker_rate >= Decimal::ZERO); let model = OptionFeeModel::new(Some(maker_rate), None)?;
Defensive patterns
Strategy: validation
Validate before calling
for (name, rate) in [("maker_fee", maker_fee), ("taker_fee", taker_fee)] {
if let Some(r) = rate {
if r < Decimal::ZERO { return Err(anyhow::anyhow!("{name} must be >= 0")); }
}
} Type guard
fn valid_rate(r: Option<Decimal>) -> bool { r.map_or(true, |v| v >= Decimal::ZERO) } Try / catch
let model = OptionFeeModel::new(maker_fee, taker_fee)
.context("invalid option fee rates")?; Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/72aeed0724c25fa5.
Report an issue: GitHub.