nautechsystems/nautilus_trader · error
Conditional orders require a trigger price
Error message
Conditional orders require a trigger price
What it means
`build_new_order_params` validates the order before translating it to a Binance Spot new-order request: for StopMarket, StopLimit, MarketIfTouched and LimitIfTouched (conditional) order types, Binance requires a stop price, which maps from the Nautilus order's `trigger_price`. If the order has no trigger price the submission is rejected locally with this error — no request reaches Binance.
Source
Thrown at crates/adapters/binance/src/spot/execution.rs:2280
order: &impl Order,
client_order_id: ClientOrderId,
is_post_only: bool,
is_quote_quantity: bool,
use_gtd: bool,
) -> anyhow::Result<NewOrderParams> {
let binance_side = BinanceSide::try_from(order.order_side())?;
let binance_order_type = order_type_to_binance_spot(order.order_type(), is_post_only)?;
let requires_trigger = matches!(
order.order_type(),
OrderType::StopMarket
| OrderType::StopLimit
| OrderType::MarketIfTouched
| OrderType::LimitIfTouched
);
if requires_trigger && order.trigger_price().is_none() {
anyhow::bail!("Conditional orders require a trigger price");
}
let supports_tif = matches!(
binance_order_type,
BinanceSpotOrderType::Limit
| BinanceSpotOrderType::StopLossLimit
| BinanceSpotOrderType::TakeProfitLimit
);
let binance_tif = time_in_force_to_binance_spot(order.time_in_force(), use_gtd)?;
let binance_tif = if supports_tif {
Some(binance_tif)
} else {
None
};
let qty_str = order.quantity().to_string();
let (base_qty, quote_qty) = if is_quote_quantity {
(None, Some(qty_str))View on GitHub (pinned to a4b06ed870)
Solutions
- Pass `trigger_price=...` when submitting conditional orders (and for stop/limit variants also the limit `price`)
- If the trigger is genuinely unknown at submission time, submit a plain order and manage the trigger client-side instead of using a conditional order type
- Validate order type vs. trigger price in strategy code before submitting (see defense)
Example fix
# before
order = self.order_factory.submit(
strategy_id=self.id,
instrument_id=instrument_id,
order_side=OrderSide.SELL,
order_type=OrderType.STOP_LIMIT,
quantity=qty,
price=limit_price, # only limit price set
time_in_force=TimeInForce.GTC,
)
# after
order = self.order_factory.submit(
strategy_id=self.id,
instrument_id=instrument_id,
order_side=OrderSide.SELL,
order_type=OrderType.STOP_LIMIT,
quantity=qty,
price=limit_price,
trigger_price=stop_price, # Binance requires the stop price
time_in_force=TimeInForce.GTC,
) Defensive patterns
Strategy: validation
Validate before calling
# Python: validate before submitting
def assert_conditional_order_valid(order_type: OrderType, trigger_price) -> None:
conditional = order_type in (
OrderType.STOP_MARKET,
OrderType.STOP_LIMIT,
OrderType.MARKET_IF_TOUCHED,
OrderType.LIMIT_IF_TOUCHED,
)
if conditional and trigger_price is None:
raise ValueError(f"{order_type} requires a trigger_price for Binance Spot") Type guard
# Python
def is_submittable_on_binance_spot(order) -> bool:
"""True when conditional orders carry the trigger price Binance requires."""
conditional = order.order_type in {
OrderType.STOP_MARKET, OrderType.STOP_LIMIT,
OrderType.MARKET_IF_TOUCHED, OrderType.LIMIT_IF_TOUCHED,
}
return (not conditional) or (order.trigger_price is not None) Prevention
- Always pass trigger_price (and for stop-limits, also price) when submitting conditional orders
- Centralize order construction in one helper that enforces trigger price by order type
- Add a unit test per order type your strategy submits, asserting the built order has a trigger price for conditional types
- Remember the mapping: trigger_price becomes Binance's stopPrice — a limit price alone is never the trigger
When it happens
Trigger: Submitting via the order factory any of STOP_MARKET, STOP_LIMIT, MARKET_IF_TOUCHED or LIMIT_IF_TOUCHED without passing `trigger_price` — e.g. `self.order_factory.submit(...)` with only `price`/`quantity` set, or a generic order-building helper that never sets trigger prices.
Common situations: Forgetting the `trigger_price` argument; setting only the limit `price` on a StopLimit and assuming it doubles as the stop; porting strategies from venues where the trigger is optional or named differently; dynamically choosing order types where one branch omits the trigger.
Related errors
- venue_order_id required for modify
- Invalid venue order ID: {id}
- height must be positive, was {self.height}
- Unsupported `OrderSide` for Binance: {value:?}
- Unsupported `TimeInForce` for Binance: {value:?}
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/496b4939de29a44b.
Report an issue: GitHub.