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
- 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
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(¶ms)) {
Ok(p) => submit(p),
Err(e) => { log::error!("TP/SL params rejected: {e}"); reject_order_before_exchange() }
} Prevention
- 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
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
- invalid 'stop_loss' price: '{s}', expected a non-negative va
- invalid 'bbo_level': '{s}', expected 1, 2, 3, 4, or 5
- invalid price for '{key}': '{s}', expected a finite non-nega
- TP override fields require 'take_profit' to be set
- invalid Bybit bbo_side_type: '{s}', expected Queue or Counte
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5b1ed14b4c18fc65.
Report an issue: GitHub.