nautechsystems/nautilus_trader · error

Unsupported time in force: {tif:?}

Error message

Unsupported time in force: {tif:?}

What it means

When building a Bybit batch order entry, map_time_in_force() translates the Nautilus TimeInForce into Bybit's timeInForce enum for the resolved order type. If the combination is not supported, the mapper returns Err(tif) and the caller wraps it as 'Unsupported time in force: {tif:?}'. Bybit only accepts a subset (GTC, IOC, FOK, PostOnly depending on order type/market), so combos like FOK with post_only or unusual TIFs on conditional orders fail here before any request is sent.

Source

Thrown at crates/adapters/bybit/src/http/client.rs:2536

        let instrument = self.instrument_from_cache(&instrument_id.symbol)?;
        let bybit_symbol = BybitSymbol::new(instrument_id.symbol.as_str())?;

        let bybit_side = match order_side {
            OrderSide::Buy => BybitOrderSide::Buy,
            OrderSide::Sell => BybitOrderSide::Sell,
        };

        // For stop/conditional orders, Bybit uses Market/Limit with trigger parameters
        let (bybit_order_type, is_stop_order) = match order_type {
            OrderType::Market => (BybitOrderType::Market, false),
            OrderType::Limit => (BybitOrderType::Limit, false),
            OrderType::StopMarket | OrderType::MarketIfTouched => (BybitOrderType::Market, true),
            OrderType::StopLimit | OrderType::LimitIfTouched => (BybitOrderType::Limit, true),
            _ => anyhow::bail!("Unsupported order type: {order_type:?}"),
        };

        let bybit_tif = map_time_in_force(bybit_order_type, time_in_force, post_only)
            .map_err(|tif| anyhow::anyhow!("Unsupported time in force: {tif:?}"))?;
        let market_unit = spot_market_unit(product_type, bybit_order_type, is_quote_quantity);
        let trigger_dir = trigger_direction(order_type, order_side, is_stop_order);

        let mut order_entry = BybitBatchPlaceOrderEntryBuilder::default();
        order_entry.symbol(bybit_symbol.raw_symbol().to_string());
        order_entry.side(bybit_side);
        order_entry.order_type(bybit_order_type);
        order_entry.qty(quantity.to_string());
        order_entry.time_in_force(bybit_tif);
        order_entry.order_link_id(client_order_id.to_string());
        order_entry.market_unit(market_unit);
        order_entry.trigger_direction(trigger_dir);

        if bbo_side_type.is_none()
            && let Some(price) = price
        {
            order_entry.price(Some(price.to_string()));
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use a supported TimeInForce for Bybit: Gtc, Ioc, Fok, or post_only Gtc (PostOnly)
  2. Remove post_only when using IOC/FOK, or set TIF to Gtc for post-only limit orders
  3. Validate time_in_force at the strategy/config layer before submitting to the client
  4. For order types that only support GTC (some conditional orders), drop the TIF field

Example fix

// before
let order = OrderRequestBuilder::new().order_type(Limit).time_in_force(Gtd, Some(expiry)).post_only(true).build();
// after
let order = OrderRequestBuilder::new().order_type(Limit).time_in_force(Gtc, None).post_only(true).build();
Defensive patterns

Strategy: validation

Validate before calling

fn tif_supported_bybit(tif: TimeInForce, post_only: bool) -> bool {
    match tif {
        TimeInForce::Gtc | TimeInForce::Ioc | TimeInForce::Fok => !post_only || tif == TimeInForce::Gtc,
        _ => false,
    }
}

Type guard

fn is_bybit_tif(tif: TimeInForce) -> Option<&'static str> {
    match tif {
        TimeInForce::Gtc => Some("GTC"),
        TimeInForce::Ioc => Some("IOC"),
        TimeInForce::Fok => Some("FOK"),
        _ => None,
    }
}

Try / catch

let resp = client.batch_place_orders(orders).await
    .map_err(|e| if e.to_string().contains("Unsupported time in force") {
        anyhow::anyhow!("strategy emitted a TIF Bybit does not accept: {e}")
    } else { e })?;

Prevention

When it happens

Trigger: Placing a batch order with a TimeInForce that doesn't map for the chosen BybitOrderType (e.g. Gtd/Day TIFs, FOK+post_only, unsupported TIF on Market orders); strategies setting time_in_force fields copied from another venue's semantics.

Common situations: Porting strategies from exchanges that support DAY or more granular TIFs to Bybit; misconfigured post_only orders where post_only conflicts with the requested TIF; batch order builders that pass TIF through unvalidated.

Related errors


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