nautechsystems/nautilus_trader · error · anyhow::Error

submitted position ID {submitted_position_id} conflicts with

Error message

submitted position ID {submitted_position_id} conflicts with canonical Binance Futures venue position ID {venue_position_id}; omit position_id while use_position_ids=true, or set use_position_ids=false for virtual hedging

What it means

When the venue has a canonical position ID (use_position_ids=true), any caller-supplied position_id on a submitted order must match it exactly. validate_submit_position_id rejects a submitted PositionId that differs from the venue's, because split identity would break position tracking; the fix is to omit position_id and let the adapter assign the venue ID, or disable venue position IDs entirely.

Source

Thrown at crates/adapters/binance/src/futures/execution.rs:1347

) -> anyhow::Result<(Option<BinancePositionSide>, Option<PositionId>)> {
    let position_side =
        determine_position_side(is_hedge_mode, order.order_side(), order.is_reduce_only());
    let venue_position_id = make_venue_position_id(
        use_position_ids,
        order.instrument_id(),
        Some(position_side.unwrap_or(BinancePositionSide::Both)),
    )?;
    Ok((position_side, venue_position_id))
}

fn validate_submit_position_id(
    submitted_position_id: Option<PositionId>,
    venue_position_id: Option<PositionId>,
) -> anyhow::Result<()> {
    if let (Some(submitted_position_id), Some(venue_position_id)) =
        (submitted_position_id, venue_position_id)
    {
        anyhow::ensure!(
            submitted_position_id == venue_position_id,
            "submitted position ID {submitted_position_id} conflicts with canonical Binance Futures venue position ID {venue_position_id}; omit position_id while use_position_ids=true, or set use_position_ids=false for virtual hedging",
        );
    }
    Ok(())
}

fn build_futures_order_list_batch(
    orders: &[OrderAny],
    is_hedge_mode: bool,
    close_position: bool,
    price_match: Option<BinancePriceMatch>,
    product_type: BinanceProductType,
    use_gtd: bool,
    ts_now: UnixNanos,
) -> Result<Vec<BatchOrderItem>, String> {
    if orders.len() > 5 {
        return Err(format!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Remove the explicit position_id from the SubmitOrder request so the adapter uses the canonical venue position ID.
  2. Set use_position_ids=false in the Binance Futures exec client config if you need strategy-managed (virtual) position IDs.
  3. Make the submitted position_id equal to the venue position ID if identity must be pinned deliberately.
  4. Audit strategy code for hardcoded PositionId values and route them through the exec client's identity scheme.

Example fix

// before
client.submit_order(order, Some(PositionId::new("MY-POS-1")), None);
// after
client.submit_order(order, None, None); // let venue position ID be assigned
Defensive patterns

Strategy: validation

Validate before calling

if cfg.use_position_ids {
    assert!(submit.position_id.is_none() || submit.position_id == Some(venue_position_id),
        "custom position_id not allowed with use_position_ids=true");
}

Prevention

When it happens

Trigger: submit_order or submit_order_list called (directly or via a strategy) with an explicit custom PositionId on an order while the Binance Futures exec client runs with use_position_ids=true and a venue position ID exists for the instrument/side.

Common situations: Strategies that hardcode position IDs ported from another adapter or from backtests with virtual hedging; copying code that sets position_id while the config later enabled use_position_ids=true.

Related errors


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