nautechsystems/nautilus_trader · error · anyhow::Error

GTD time in force requires expire_time parameter

Error message

GTD time in force requires expire_time parameter

What it means

On Kraken WS v2, a GTD (Good-Til-Date) order requires an explicit expire_time; compute_ws_time_in_force bails with this error when TimeInForce::Gtd is requested without one. The library throws it because the Kraken AddOrder message cannot express GTD without an expiry timestamp.

Source

Thrown at crates/adapters/kraken/src/common/order_params.rs:230

pub(crate) fn compute_ws_time_in_force(
    is_limit_order: bool,
    time_in_force: TimeInForce,
    expire_time: Option<UnixNanos>,
) -> anyhow::Result<Option<KrakenTimeInForce>> {
    if !is_limit_order {
        return Ok(None);
    }

    match time_in_force {
        TimeInForce::Gtc => Ok(None),
        TimeInForce::Ioc => Ok(Some(KrakenTimeInForce::ImmediateOrCancel)),
        TimeInForce::Fok => {
            anyhow::bail!("FOK time in force is not supported on Kraken WS v2; use REST")
        }
        TimeInForce::Gtd => {
            expire_time.ok_or_else(|| {
                anyhow::anyhow!("GTD time in force requires expire_time parameter")
            })?;
            Ok(Some(KrakenTimeInForce::GoodTilDate))
        }
        _ => anyhow::bail!("Unsupported time in force: {time_in_force:?}"),
    }
}

#[cfg(test)]
mod tests {
    use nautilus_core::{UUID4, UnixNanos};
    use nautilus_model::identifiers::{
        ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId,
    };
    use rstest::rstest;
    use rust_decimal_macros::dec;

    use super::*;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Provide expire_time (as a Unix timestamp) when constructing the order with TimeInForce::Gtd.
  2. Fall back to Gtc if no expiry is meaningful for the strategy.
  3. Validate at the strategy/order-builder layer that Gtd orders always carry an expiry before submission.
  4. Use the REST API instead of WS v2 if the order router cannot supply expire_time.

Example fix

// before
let tif = TimeInForce::Gtd;
build_add_order_params(&order, None)?; // expire_time not passed
// after
let expire_time = order.expire_time.expect("GTD orders require expire_time");
build_add_order_params(&order, Some(expire_time))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: check before submitting
if order.time_in_force == TimeInForce::Gtd && order.expire_time.is_none() {
    return Err(anyhow::anyhow!("order {} is GTD but has no expire_time", order.client_order_id));
}

Try / catch

match provider.submit_order(order) {
    Err(e) if e.to_string().contains("GTD time in force requires expire_time") => {
        // resubmit as GTC or attach expire_time
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling build_add_order_params or build_batch_order with an order whose time_in_force is Gtd while the expire_time option is None.

Common situations: Building orders from a strategy that sets TIF=Gtd but never sets expiry; mapping an upstream order request that carries Gtd without an expiry field; tests like test_compute_ws_time_in_force_fok_bails exercising TIF handling.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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