nautechsystems/nautilus_trader · error · anyhow::Error

FOK time in force is not supported by Hyperliquid

Error message

FOK time in force is not supported by Hyperliquid

What it means

Hyperliquid's exchange API does not offer a Fill-or-Kill time in force; its order types map to GTC, IOC, and ALO only. time_in_force_to_hyperliquid_tif rejects TIF::Fok with this message when translating a Nautilus order into a Hyperliquid exchange request, because silently converting FOK to something else would change execution semantics.

Source

Thrown at crates/adapters/hyperliquid/src/common/parse.rs:384

        Some(leading.to_string())
    }
}

/// Converts a Nautilus `TimeInForce` to Hyperliquid TIF.
///
/// # Errors
///
/// Returns an error if the time in force is not supported.
pub fn time_in_force_to_hyperliquid_tif(
    tif: TimeInForce,
    is_post_only: bool,
) -> anyhow::Result<HyperliquidExchangeTif> {
    match (tif, is_post_only) {
        (_, true) => Ok(HyperliquidExchangeTif::Alo), // Always use ALO for post-only orders
        (TimeInForce::Gtc, false) => Ok(HyperliquidExchangeTif::Gtc),
        (TimeInForce::Ioc, false) => Ok(HyperliquidExchangeTif::Ioc),
        (TimeInForce::Fok, false) => {
            anyhow::bail!("FOK time in force is not supported by Hyperliquid")
        }
        _ => anyhow::bail!("Unsupported time in force for Hyperliquid: {tif:?}"),
    }
}

fn determine_tpsl_type(
    order_type: OrderType,
    order_side: OrderSide,
    trigger_price: Decimal,
    current_price: Option<Decimal>,
) -> HyperliquidExchangeTpSl {
    match order_type {
        // Stop orders are protective - always SL
        OrderType::StopMarket | OrderType::StopLimit => HyperliquidExchangeTpSl::Sl,

        // If Touched orders are profit-taking or entry orders - always TP
        OrderType::MarketIfTouched | OrderType::LimitIfTouched => HyperliquidExchangeTpSl::Tp,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Change the order's time_in_force to Gtc or Ioc before submission (Ioc is the closest partial-fill-averse alternative).
  2. If true all-or-nothing execution is required, implement client-side size checks or use Hyperliquid's ALO post-only semantics where appropriate — the venue simply cannot guarantee FOK.
  3. Pre-validate order TIF in the strategy layer and route FOK orders to a venue that supports them.

Example fix

// before
let mut order = order_factory.limit(...);
order.time_in_force = TimeInForce::Fok;
submit_order(order)?;
// after
order.time_in_force = TimeInForce::Ioc; // FOK unsupported on Hyperliquid
submit_order(order)?;
Defensive patterns

Strategy: validation

Validate before calling

fn supports_tif_hyperliquid(tif: TimeInForce) -> bool {
    matches!(tif, TimeInForce::Gtc | TimeInForce::Ioc)
}
if !supports_tif_hyperliquid(order.time_in_force()) { /* convert to Ioc or reject at strategy layer */ }

Try / catch

match adapter.submit_order(order).await {
    Err(e) if e.to_string().contains("FOK time in force") => {
        let mut fixed = order.clone();
        fixed.set_time_in_force(TimeInForce::Ioc);
        adapter.submit_order(fixed).await?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: Submitting or modifying an order with TimeInForce::Fok and is_post_only=false (or unset) through order_to_hyperliquid_request_with_asset_and_cloid, modify_order, or when hyperliquid_order_kind derives the order kind — e.g. submit_order with a FOK limit/market order.

Common situations: Strategy code or config carrying over FOK defaults from another adapter (Binance/Bybit support FOK), a venue-agnostic strategy parameterization that sets FOK, or an order event reconstructed from a template with FOK TIF.

Related errors


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