{"record":{"id":"1a25f32752322e82","repo":"nautechsystems/nautilus_trader","slug":"invalid-stop-loss-price-s-expected-a-non-n","errorCode":null,"errorMessage":"invalid 'stop_loss' price: '{s}', expected a non-negative value","messagePattern":"invalid 'stop_loss' price: '(.+?)', expected a non-negative value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bybit/src/common/parse.rs","lineNumber":1805,"sourceCode":"        ..Default::default()\n    };\n\n    if let Some(s) = get_price_str(params, \"take_profit\") {\n        let p =\n            Price::from_str(&s).map_err(|e| anyhow::anyhow!(\"invalid 'take_profit' price: {e}\"))?;\n\n        if p.as_f64() < 0.0 {\n            anyhow::bail!(\"invalid 'take_profit' price: '{s}', expected a non-negative value\");\n        }\n        result.take_profit = Some(p);\n    }\n\n    if let Some(s) = get_price_str(params, \"stop_loss\") {\n        let p =\n            Price::from_str(&s).map_err(|e| anyhow::anyhow!(\"invalid 'stop_loss' price: {e}\"))?;\n\n        if p.as_f64() < 0.0 {\n            anyhow::bail!(\"invalid 'stop_loss' price: '{s}', expected a non-negative value\");\n        }\n        result.stop_loss = Some(p);\n    }\n\n    for (key, setter) in [\n        (\n            \"tp_limit_price\",\n            &mut result.tp_limit_price as &mut Option<String>,\n        ),\n        (\"sl_limit_price\", &mut result.sl_limit_price),\n        (\"tp_trigger_price\", &mut result.tp_trigger_price),\n        (\"sl_trigger_price\", &mut result.sl_trigger_price),\n    ] {\n        if let Some(s) = get_price_str(params, key) {\n            let v: f64 = s\n                .parse()\n                .map_err(|_| anyhow::anyhow!(\"invalid price for '{key}': '{s}'\"))?;\n","sourceCodeStart":1787,"sourceCodeEnd":1823,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bybit/src/common/parse.rs#L1787-L1823","documentation":"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.","triggerScenarios":"Submitting an order with params stop_loss='-1.5' (or any negative numeric string) through py_submit_order / place_order with SL attached.","commonSituations":"Computing stop prices as negative deltas, sign errors in risk calculations, or copying formatted values that include a minus sign.","solutions":["Fix the stop-loss computation so the absolute price is non-negative","Validate the sign before attaching stop_loss to order params","Express direction via the order side, not a negative price"],"exampleFix":"// before\nparams.insert(\"stop_loss\", \"-2500.5\");\n// after\nparams.insert(\"stop_loss\", \"2500.5\");","handlingStrategy":"validation","validationCode":"sl = float(params[\"stop_loss\"])\nassert sl >= 0, f\"stop_loss must be non-negative, got {sl}\"","typeGuard":"fn is_non_negative_price(s: &str) -> bool {\n    Price::from_str(s).map(|p| p.as_f64() >= 0.0).unwrap_or(false)\n}","tryCatchPattern":"match parse_bybit_tp_sl_params(Some(&params)) {\n    Ok(p) => submit(p),\n    Err(e) => { log::error!(\"TP/SL params rejected: {e}\"); reject_order_before_exchange() }\n}","preventionTips":["Validate stop-loss is a positive absolute price before submission","Avoid computing stops as negative deltas","Cover stop-price math with regression tests"],"tags":["bybit","tpsl","stop-loss","negative-price","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}