nautechsystems/nautilus_trader · error · anyhow::Error

Binance Futures goodTillDate must be strictly greater than c

Error message

Binance Futures goodTillDate must be strictly greater than current time plus {BINANCE_GTD_MIN_LEAD_SECS} seconds

What it means

Binance enforces a minimum lead time on goodTillDate: the expiry must be strictly greater than the current time plus 600 seconds (BINANCE_GTD_MIN_LEAD_SECS, execution.rs:139). The client computes now + 600s in nanoseconds (checked, so extreme clock values surface as an overflow context error instead) and rejects expiries at or below it, because the venue would reject them as too soon.

Source

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

    anyhow::ensure!(!post_only, "Binance Futures GTD cannot be post-only");

    anyhow::ensure!(
        product_type == BinanceProductType::UsdM,
        "Binance {product_type:?} Futures does not support native GTD"
    );

    let expire_time = expire_time.context("Binance Futures GTD requires an expire_time")?;
    let expire_ns = expire_time.as_u64();
    anyhow::ensure!(
        expire_ns.is_multiple_of(NANOSECONDS_IN_SECOND),
        "Binance Futures goodTillDate requires whole-second precision"
    );

    let minimum_ns = ts_now
        .as_u64()
        .checked_add(BINANCE_GTD_MIN_LEAD_SECS * NANOSECONDS_IN_SECOND)
        .context("Binance Futures GTD minimum timestamp overflow")?;
    anyhow::ensure!(
        expire_ns > minimum_ns,
        "Binance Futures goodTillDate must be strictly greater than current time plus {BINANCE_GTD_MIN_LEAD_SECS} seconds"
    );

    let good_till_date = expire_ns / NANOSECONDS_IN_MILLISECOND;
    anyhow::ensure!(
        good_till_date < BINANCE_GTD_MAX_MILLIS,
        "Binance Futures goodTillDate must be smaller than {BINANCE_GTD_MAX_MILLIS}"
    );

    Ok(FuturesOrderLifetime {
        time_in_force,
        good_till_date: Some(i64::try_from(good_till_date)?),
    })
}

fn is_grouped_order(order: &OrderAny) -> bool {
    matches!(

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Set the expiry strictly more than 600 seconds (10 minutes) in the future at submission time
  2. Recompute the expiry relative to the current clock right before (re)submission rather than reusing a stale timestamp
  3. If the desired expiry is closer than the lead time, submit GTC and cancel at the desired time instead

Example fix

// before
let expire = UnixNanos::now().checked_add(5 * 60 * 1_000_000_000)?; // only 5 min ahead

// after
let expire = UnixNanos::now().checked_add(15 * 60 * 1_000_000_000)?; // > now + 600s
Defensive patterns

Strategy: validation

Validate before calling

const MIN_LEAD_SECS: u64 = 600;
let now = clock.get_time_ns().as_u64();
anyhow::ensure!(
    expire.as_u64() > now + MIN_LEAD_SECS * 1_000_000_000,
    "expiry must be more than 10 minutes out"
);

Type guard

fn expiry_has_min_lead(expire_ns: u64, now_ns: u64) -> bool {
    expire_ns > now_ns.saturating_add(600 * 1_000_000_000)
}

Prevention

When it happens

Trigger: Submitting a GTD order whose expire_time is less than ~10 minutes in the future, exactly now+600s (not strictly greater), or in the past — e.g. a session-end expiry submitted late, or a fixed expiry timestamp whose submission was delayed.

Common situations: End-of-session expiries submitted within the final 10 minutes of the session; delayed order submission (queueing, retries) making a previously valid expiry too close; timezone math producing a past timestamp.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/f0189318926926af. Report an issue: GitHub.