nautechsystems/nautilus_trader · error

TP override fields require 'take_profit' to be set

Error message

TP override fields require 'take_profit' to be set

What it means

After parsing, parse_bybit_tp_sl_params enforces that TP override fields (tp_trigger_by, tp_order_type, tp_limit_price, tp_trigger_price) may only be supplied when a take_profit price is also set. Supplying an override without the base take_profit raises this anyhow error (and symmetrically for SL overrides).

Source

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

        result.sl_order_type = Some(parse_tp_sl_order_type(s)?);
    }

    if let Some(s) = params.get_str("tpsl_mode") {
        result.tpsl_mode = Some(parse_tpsl_mode(s)?);
    }

    let has_tp_fields = result.tp_trigger_by.is_some()
        || result.tp_order_type.is_some()
        || result.tp_limit_price.is_some()
        || result.tp_trigger_price.is_some();

    let has_sl_fields = result.sl_trigger_by.is_some()
        || result.sl_order_type.is_some()
        || result.sl_limit_price.is_some()
        || result.sl_trigger_price.is_some();

    if result.take_profit.is_none() && has_tp_fields {
        anyhow::bail!("TP override fields require 'take_profit' to be set");
    }

    if result.stop_loss.is_none() && has_sl_fields {
        anyhow::bail!("SL override fields require 'stop_loss' to be set");
    }

    if result.tp_order_type == Some(BybitOrderType::Limit) && result.tp_limit_price.is_none() {
        anyhow::bail!("'tp_order_type' is 'Limit' but 'tp_limit_price' was not provided");
    }

    if result.sl_order_type == Some(BybitOrderType::Limit) && result.sl_limit_price.is_none() {
        anyhow::bail!("'sl_order_type' is 'Limit' but 'sl_limit_price' was not provided");
    }

    if result.tp_limit_price.is_some() && result.tp_order_type != Some(BybitOrderType::Limit) {
        anyhow::bail!("'tp_limit_price' requires 'tp_order_type' to be 'Limit'");
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Include take_profit in the params whenever you send any tp_* override field
  2. Remove the orphaned tp_* override fields if you did not intend to set a take profit
  3. Bundle the complete TP definition (take_profit plus overrides) together in one params map

Example fix

// before
params.insert("tp_order_type", "Limit");
// after
params.insert("take_profit", "30100.0");
params.insert("tp_order_type", "Limit");
Defensive patterns

Strategy: validation

Validate before calling

tp_overrides = [k for k in params if k.startswith("tp_")]
if tp_overrides:
    assert "take_profit" in params, "tp_* overrides require take_profit"
sl_overrides = [k for k in params if k.startswith("sl_")]
if sl_overrides:
    assert "stop_loss" in params, "sl_* overrides require stop_loss"

Type guard

fn tp_sl_params_complete(params: &Params) -> bool {
    let has_tp_fields = params.keys().any(|k| k.starts_with("tp_"));
    let has_sl_fields = params.keys().any(|k| k.starts_with("sl_"));
    (!has_tp_fields || params.contains_key("take_profit"))
        && (!has_sl_fields || params.contains_key("stop_loss"))
}

Try / catch

if let Err(e) = parse_bybit_tp_sl_params(Some(&params)) {
    log::error!("TP/SL params rejected: {e}");
    // resubmit with base price or drop the overrides
}

Prevention

When it happens

Trigger: Submitting an order with params containing e.g. tp_trigger_by='MarkPrice' or tp_order_type='Limit' but no take_profit key.

Common situations: Refining an existing TP order but forgetting to re-send the take_profit price; copy-pasting only the override block from an example; partial config merges dropping the take_profit key.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/c635bc5f2fcd4458. Report an issue: GitHub.