{"record":{"id":"5b1ed14b4c18fc65","repo":"nautechsystems/nautilus_trader","slug":"invalid-take-profit-price-s-expected-a-non","errorCode":null,"errorMessage":"invalid 'take_profit' price: '{s}', expected a non-negative value","messagePattern":"invalid 'take_profit' price: '(.+?)', expected a non-negative value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bybit/src/common/parse.rs","lineNumber":1795,"sourceCode":"}\n\n/// Parses Bybit TP/SL parameters from an optional params map.\npub fn parse_bybit_tp_sl_params(params: Option<&Params>) -> anyhow::Result<BybitTpSlParams> {\n    let Some(params) = params else {\n        return Ok(BybitTpSlParams::default());\n    };\n\n    let mut result = BybitTpSlParams {\n        is_leverage: params.get_bool(\"is_leverage\").unwrap_or(false),\n        ..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>,","sourceCodeStart":1777,"sourceCodeEnd":1813,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bybit/src/common/parse.rs#L1777-L1813","documentation":"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.","triggerScenarios":"Submitting an order with params take_profit='-0.01' (or any negative numeric string) via py_submit_order / place_order with TP attached.","commonSituations":"Sign-flip bugs computing expected profit, exporting stop-side prices as negative offsets, or template configs where a minus sign leaked into the value.","solutions":["Take the absolute value or fix the price calculation so take_profit >= 0","Validate the price sign in the strategy before attaching it to the order","If you need direction, express it via order side (BUY/SELL) rather than a negative price"],"exampleFix":"// before\nlet tp = format!(\"{}\", entry - offset); // can be negative\n// after\nlet tp = (entry - offset).max(Decimal::ZERO);","handlingStrategy":"validation","validationCode":"tp = float(params[\"take_profit\"])\nassert tp >= 0, f\"take_profit must be non-negative, got {tp}\"","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":["Assert TP prices are non-negative in the strategy before attaching them","Fix sign conventions: direction is the order side, not the price","Unit-test price computations for negative outcomes"],"tags":["bybit","tpsl","take-profit","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-14T00:17:10.932Z"}