nautechsystems/nautilus_trader · error

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

Error message

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

What it means

parse_bybit_tp_sl_params converts the stop_loss string to a Price and rejects negative values with this anyhow error. It fires only for parseable but negative prices; malformed strings fail earlier with the 'invalid stop_loss price: {e}' message.

Source

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

        ..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>,
        ),
        ("sl_limit_price", &mut result.sl_limit_price),
        ("tp_trigger_price", &mut result.tp_trigger_price),
        ("sl_trigger_price", &mut result.sl_trigger_price),
    ] {
        if let Some(s) = get_price_str(params, key) {
            let v: f64 = s
                .parse()
                .map_err(|_| anyhow::anyhow!("invalid price for '{key}': '{s}'"))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fix the stop-loss computation so the absolute price is non-negative
  2. Validate the sign before attaching stop_loss to order params
  3. Express direction via the order side, not a negative price

Example fix

// before
params.insert("stop_loss", "-2500.5");
// after
params.insert("stop_loss", "2500.5");
Defensive patterns

Strategy: validation

Validate before calling

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

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 stop_loss='-1.5' (or any negative numeric string) through py_submit_order / place_order with SL attached.

Common situations: Computing stop prices as negative deltas, sign errors in risk calculations, or copying formatted values that include a minus sign.

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/1a25f32752322e82. Report an issue: GitHub.