nautechsystems/nautilus_trader · error · anyhow::Error
Binance Futures goodTillDate requires whole-second precision
Error message
Binance Futures goodTillDate requires whole-second precision
What it means
Binance Futures goodTillDate must be expressed in whole-second precision (millisecond field derived from a second-aligned nanosecond timestamp). If the expire_time nanosecond value is not an exact multiple of one second, the adapter rejects the order rather than silently rounding the expiry, which could expire the order at a different time than the strategy intended.
Solutions
- Round the expire_time down (or up) to a whole second before submitting: expire_ns - (expire_ns % 1_000_000_000).
- Construct expiries from whole-second Unix timestamps in the strategy.
- Ensure any lead-time/latency adjustments applied to expire_time are whole seconds.
- Verify timestamps crossing from external systems are normalized to second precision.
Example fix
// before let expire = ts_now + 90_500_000_000; // 90.5s -> sub-second precision // after let expire = (ts_now + 90_000_000_000) / 1_000_000_000 * 1_000_000_000; // whole seconds
Defensive patterns
Strategy: validation
Validate before calling
fn second_aligned(ns: u64) -> bool { ns % 1_000_000_000 == 0 }
let expire_ns = expire_time.as_u64();
if !second_aligned(expire_ns) { /* round to whole second before submit */ } Prevention
- Compute expiries in whole seconds
- Round expire_time to second precision after any duration math
- Normalize external timestamps to whole seconds before use
When it happens
Trigger: validate_order / build_futures_order_list_batch with GTD and use_gtd=true where expire_time.as_u64() % 1_000_000_000 != 0 — e.g. expiry computed from a data timestamp, latency-adjusted time, or arbitrary duration addition carrying sub-second nanoseconds.
Common situations: expire_time computed as now + duration (including fractional seconds); expiries derived from bar close timestamps with sub-second resolution; timestamps generated on a different clock resolution and passed unrounded.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Binance Futures goodTillDate must be smaller than
- Binance Futures does not support GTD for order type
- Binance Futures GTD cannot be post-only
- Binance Futures GTD requires an expire_time
- Binance Futures does not support native GTD
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b1e8e7adc39c2212.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/binance/src/futures/execution.rs:1519
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 {
order.contingency_type().is_some()View on GitHub (pinned to 18893faf8b)