{"record":{"id":"b1e8e7adc39c2212","repo":"nautechsystems/nautilus_trader","slug":"binance-futures-goodtilldate-requires-whole-second","errorCode":null,"errorMessage":"Binance Futures goodTillDate requires whole-second precision","messagePattern":"Binance Futures goodTillDate requires whole-second precision","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/binance/src/futures/execution.rs","lineNumber":1519,"sourceCode":"    anyhow::ensure!(!post_only, \"Binance Futures GTD cannot be post-only\");\n\n    anyhow::ensure!(\n        product_type == BinanceProductType::UsdM,\n        \"Binance {product_type:?} Futures does not support native GTD\"\n    );\n\n    let expire_time = expire_time.context(\"Binance Futures GTD requires an expire_time\")?;\n    let expire_ns = expire_time.as_u64();\n    anyhow::ensure!(\n        expire_ns.is_multiple_of(NANOSECONDS_IN_SECOND),\n        \"Binance Futures goodTillDate requires whole-second precision\"\n    );\n\n    let minimum_ns = ts_now\n        .as_u64()\n        .checked_add(BINANCE_GTD_MIN_LEAD_SECS * NANOSECONDS_IN_SECOND)\n        .context(\"Binance Futures GTD minimum timestamp overflow\")?;\n    anyhow::ensure!(\n        expire_ns > minimum_ns,\n        \"Binance Futures goodTillDate must be strictly greater than current time plus {BINANCE_GTD_MIN_LEAD_SECS} seconds\"\n    );\n\n    let good_till_date = expire_ns / NANOSECONDS_IN_MILLISECOND;\n    anyhow::ensure!(\n        good_till_date < BINANCE_GTD_MAX_MILLIS,\n        \"Binance Futures goodTillDate must be smaller than {BINANCE_GTD_MAX_MILLIS}\"\n    );\n\n    Ok(FuturesOrderLifetime {\n        time_in_force,\n        good_till_date: Some(i64::try_from(good_till_date)?),\n    })\n}\n\nfn is_grouped_order(order: &OrderAny) -> bool {\n    order.contingency_type().is_some()","sourceCodeStart":1501,"sourceCodeEnd":1537,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/binance/src/futures/execution.rs#L1501-L1537","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet expire = ts_now + 90_500_000_000; // 90.5s -> sub-second precision\n// after\nlet expire = (ts_now + 90_000_000_000) / 1_000_000_000 * 1_000_000_000; // whole seconds","handlingStrategy":"validation","validationCode":"fn second_aligned(ns: u64) -> bool { ns % 1_000_000_000 == 0 }\nlet expire_ns = expire_time.as_u64();\nif !second_aligned(expire_ns) { /* round to whole second before submit */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute expiries in whole seconds","Round expire_time to second precision after any duration math","Normalize external timestamps to whole seconds before use"],"tags":["gtd","timestamp-precision","expire-time","binance-futures"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}