nautechsystems/nautilus_trader · error · anyhow::Error

invalid 'stop_loss' price: {e}

Error message

invalid 'stop_loss' price: {e}

What it means

parse_bybit_tp_sl_params reads an optional 'stop_loss' param from the order params map and parses it into a Price via Price::from_str; unparseable values raise this error (negative values are rejected separately). It means the stop-loss value supplied is not a valid price string.

Source

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

    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>,
        ),
        ("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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure stop_loss is a valid decimal price string with decimals fitting the instrument's price precision.
  2. Omit the key entirely when no stop-loss is intended instead of passing empty/placeholder values.
  3. Validate the price is finite and positive before adding to params.
  4. Format floats explicitly (fixed-point, e.g. {:.8g} or precision-aware) rather than relying on default Display of f64.

Example fix

// before
params.insert("stop_loss".into(), sl.to_string()); // f64, may be 0.0 or NaN
// after
if let Some(sl) = sl.filter(|v| v.is_finite() && *v > 0.0) {
    params.insert("stop_loss".into(), format!("{:.2}", sl));
}
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate stop_loss param before building order
let sl = sl.filter(|v| v.is_finite() && *v > 0.0)
    .map(|v| format!("{v:.instrument_price_precision$}"));

Type guard

fn valid_sl_string(s: &str) -> bool {
    s.trim().parse::<f64>().map(|v| v.is_finite() && v > 0.0).unwrap_or(false)
}

Try / catch

let p = parse_bybit_tp_sl_params(&params)
    .map_err(|e| { log::error!("bad stop_loss/TP params: {e:#}"); e })?;

Prevention

When it happens

Trigger: Submitting/modifying an order with params['stop_loss'] set to an empty string, non-numeric text, NaN/inf, or a value violating Price parsing rules (e.g. too many decimals for precision).

Common situations: Hardcoded placeholder stop-losses in config; float formatting producing scientific notation for very small prices; strategy passing sentinel values like 0 or -1 meant to mean 'no stop'; locale-formatted numbers.

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