nautechsystems/nautilus_trader · error · anyhow::Error
Binance Futures goodTillDate must be strictly greater than…
Error message
Binance Futures goodTillDate must be strictly greater than current time plus {BINANCE_GTD_MIN_LEAD_SECS} seconds What it means
This error is raised by determine_futures_order_lifetime in the Binance Futures execution client when a GTD (Good-Till-Date) order's expire_time is not far enough in the future. Binance requires the native goodTillDate timestamp to be strictly greater than the current time plus BINANCE_GTD_MIN_LEAD_SECS, so NautilusTrader validates this locally before submitting and rejects orders that would be immediately rejected or misinterpreted by the exchange. It is a pre-submission sanity check to avoid a guaranteed API rejection.
Solutions
- Set the order's expire_time (TIF GTD) to strictly more than BINANCE_GTD_MIN_LEAD_SECS seconds in the future relative to submission time.
- Compute expire_time as close to submit as possible, e.g. using the live clock (clock.timestamp_ns()) rather than a cached value captured earlier in the strategy.
- If you do not need native exchange-side expiry, disable manage_gtd_expiry / set use_gtd=false so GTD orders are downgraded to GTC with a warning instead of an error.
- Check machine clock synchronization (NTP) if expire_time appears correct but still fails.
Example fix
// before let expire = self.clock.timestamp_ns() + 5_000_000_000; // 5s lead, too short order_factory.limit(..., time_in_force=TimeInForce::Gtd, expire_time=expire); // after let expire = self.clock.timestamp_ns() + 120_000_000_000; // >= BINANCE_GTD_MIN_LEAD_SECS + margin order_factory.limit(..., time_in_force=TimeInForce::Gtd, expire_time=expire);
Defensive patterns
Strategy: validation
Validate before calling
const MIN_LEAD_SECS: u64 = /* BINANCE_GTD_MIN_LEAD_SECS */;
let now_ns = clock.timestamp_ns().as_u64();
anyhow::ensure!(
expire_ns > now_ns + MIN_LEAD_SECS * 1_000_000_000,
"GTD expire_time must exceed now + {MIN_LEAD_SECS}s"
); Prevention
- Always compute GTD expire_time from the live clock immediately before submission
- Add generous margin (minutes, not seconds) above the minimum lead time
- Use manage_gtd_expiry=false if native exchange expiry is not required
When it happens
Trigger: Submitting a Limit/StopLimit/LimitIfTouched order with TimeInForce::Gtd and manage_gtd_expiry enabled (use_gtd=true) on USD-M Futures where expire_time is set to now, in the past, or within BINANCE_GTD_MIN_LEAD_SECS seconds of now. Also hit by validate_order paths and batch order building when a strategy computes expire_time from a stale clock value.
Common situations: A strategy sets a GTD expiry too close to 'now' (e.g. expire = now + a few seconds), backtest logic reuses a cached timestamp, clock skew between the machine and exchange time, or a config where expire_time was computed before a long preprocessing delay so it is stale by submission time.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Binance Spot does not support native GTD; set use_gtd=false…
- Lighter GTD expire_time must be at least 5 minutes from now…
- Binance Futures does not support GTD for order type
- Binance Futures fill report end time requires start time…
- Binance Futures trade limit must not exceed 1000
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f0189318926926af.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/binance/src/futures/execution.rs:1525
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 {
order.contingency_type().is_some()
|| order
.linked_order_ids()
.is_some_and(|linked_order_ids| !linked_order_ids.is_empty())
}
fn binance_side_wire(side: BinanceSide) -> &'static str {View on GitHub (pinned to 18893faf8b)