nautechsystems/nautilus_trader · error

invalid 'take_profit' price: '{s}', expected a non-negative

Error message

invalid 'take_profit' price: '{s}', expected a non-negative value

What it means

When parsing TP/SL params, parse_bybit_tp_sl_params first converts the take_profit string to a Price, then rejects negative values with this anyhow error. Note that a string that is not a valid price at all fails earlier with 'invalid take_profit price: {e}'; this error fires only for well-formed but negative prices.

Source

Thrown at crates/adapters/bybit/src/common/parse.rs:1795

}

/// Parses Bybit TP/SL parameters from an optional params map.
pub fn parse_bybit_tp_sl_params(params: Option<&Params>) -> anyhow::Result<BybitTpSlParams> {
    let Some(params) = params else {
        return Ok(BybitTpSlParams::default());
    };

    let mut result = BybitTpSlParams {
        is_leverage: params.get_bool("is_leverage").unwrap_or(false),
        ..Default::default()
    };

    if let Some(s) = get_price_str(params, "take_profit") {
        let p =
            Price::from_str(&s).map_err(|e| anyhow::anyhow!("invalid 'take_profit' price: {e}"))?;

        if p.as_f64() < 0.0 {
            anyhow::bail!("invalid 'take_profit' price: '{s}', expected a non-negative value");
        }
        result.take_profit = Some(p);
    }

    if let Some(s) = get_price_str(params, "stop_loss") {
        let p =
            Price::from_str(&s).map_err(|e| anyhow::anyhow!("invalid 'stop_loss' price: {e}"))?;

        if p.as_f64() < 0.0 {
            anyhow::bail!("invalid 'stop_loss' price: '{s}', expected a non-negative value");
        }
        result.stop_loss = Some(p);
    }

    for (key, setter) in [
        (
            "tp_limit_price",
            &mut result.tp_limit_price as &mut Option<String>,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Take the absolute value or fix the price calculation so take_profit >= 0
  2. Validate the price sign in the strategy before attaching it to the order
  3. If you need direction, express it via order side (BUY/SELL) rather than a negative price

Example fix

// before
let tp = format!("{}", entry - offset); // can be negative
// after
let tp = (entry - offset).max(Decimal::ZERO);
Defensive patterns

Strategy: validation

Validate before calling

tp = float(params["take_profit"])
assert tp >= 0, f"take_profit must be non-negative, got {tp}"

Type guard

fn is_non_negative_price(s: &str) -> bool {
    Price::from_str(s).map(|p| p.as_f64() >= 0.0).unwrap_or(false)
}

Try / catch

match parse_bybit_tp_sl_params(Some(&params)) {
    Ok(p) => submit(p),
    Err(e) => { log::error!("TP/SL params rejected: {e}"); reject_order_before_exchange() }
}

Prevention

When it happens

Trigger: Submitting an order with params take_profit='-0.01' (or any negative numeric string) via py_submit_order / place_order with TP attached.

Common situations: Sign-flip bugs computing expected profit, exporting stop-side prices as negative offsets, or template configs where a minus sign leaked into the value.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/5b1ed14b4c18fc65. Report an issue: GitHub.