{"record":{"id":"feb00427dab946b0","repo":"nautechsystems/nautilus_trader","slug":"unsupported-time-in-force-tif","errorCode":null,"errorMessage":"Unsupported time in force: {tif:?}","messagePattern":"Unsupported time in force: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bybit/src/http/client.rs","lineNumber":2536,"sourceCode":"        let instrument = self.instrument_from_cache(&instrument_id.symbol)?;\n        let bybit_symbol = BybitSymbol::new(instrument_id.symbol.as_str())?;\n\n        let bybit_side = match order_side {\n            OrderSide::Buy => BybitOrderSide::Buy,\n            OrderSide::Sell => BybitOrderSide::Sell,\n        };\n\n        // For stop/conditional orders, Bybit uses Market/Limit with trigger parameters\n        let (bybit_order_type, is_stop_order) = match order_type {\n            OrderType::Market => (BybitOrderType::Market, false),\n            OrderType::Limit => (BybitOrderType::Limit, false),\n            OrderType::StopMarket | OrderType::MarketIfTouched => (BybitOrderType::Market, true),\n            OrderType::StopLimit | OrderType::LimitIfTouched => (BybitOrderType::Limit, true),\n            _ => anyhow::bail!(\"Unsupported order type: {order_type:?}\"),\n        };\n\n        let bybit_tif = map_time_in_force(bybit_order_type, time_in_force, post_only)\n            .map_err(|tif| anyhow::anyhow!(\"Unsupported time in force: {tif:?}\"))?;\n        let market_unit = spot_market_unit(product_type, bybit_order_type, is_quote_quantity);\n        let trigger_dir = trigger_direction(order_type, order_side, is_stop_order);\n\n        let mut order_entry = BybitBatchPlaceOrderEntryBuilder::default();\n        order_entry.symbol(bybit_symbol.raw_symbol().to_string());\n        order_entry.side(bybit_side);\n        order_entry.order_type(bybit_order_type);\n        order_entry.qty(quantity.to_string());\n        order_entry.time_in_force(bybit_tif);\n        order_entry.order_link_id(client_order_id.to_string());\n        order_entry.market_unit(market_unit);\n        order_entry.trigger_direction(trigger_dir);\n\n        if bbo_side_type.is_none()\n            && let Some(price) = price\n        {\n            order_entry.price(Some(price.to_string()));\n        }","sourceCodeStart":2518,"sourceCodeEnd":2554,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bybit/src/http/client.rs#L2518-L2554","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a supported TimeInForce for Bybit: Gtc, Ioc, Fok, or post_only Gtc (PostOnly)","Remove post_only when using IOC/FOK, or set TIF to Gtc for post-only limit orders","Validate time_in_force at the strategy/config layer before submitting to the client","For order types that only support GTC (some conditional orders), drop the TIF field"],"exampleFix":"// before\nlet order = OrderRequestBuilder::new().order_type(Limit).time_in_force(Gtd, Some(expiry)).post_only(true).build();\n// after\nlet order = OrderRequestBuilder::new().order_type(Limit).time_in_force(Gtc, None).post_only(true).build();","handlingStrategy":"validation","validationCode":"fn tif_supported_bybit(tif: TimeInForce, post_only: bool) -> bool {\n    match tif {\n        TimeInForce::Gtc | TimeInForce::Ioc | TimeInForce::Fok => !post_only || tif == TimeInForce::Gtc,\n        _ => false,\n    }\n}","typeGuard":"fn is_bybit_tif(tif: TimeInForce) -> Option<&'static str> {\n    match tif {\n        TimeInForce::Gtc => Some(\"GTC\"),\n        TimeInForce::Ioc => Some(\"IOC\"),\n        TimeInForce::Fok => Some(\"FOK\"),\n        _ => None,\n    }\n}","tryCatchPattern":"let resp = client.batch_place_orders(orders).await\n    .map_err(|e| if e.to_string().contains(\"Unsupported time in force\") {\n        anyhow::anyhow!(\"strategy emitted a TIF Bybit does not accept: {e}\")\n    } else { e })?;","preventionTips":["Restrict strategy configs to GTC/IOC/FOK for Bybit","Never combine post_only with IOC/FOK","Validate order params (TIF, type, post_only) before submission"],"tags":["rust","bybit","orders","time-in-force"],"backgroundTag":"unsupported-enum-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}