Repository navigation
Conversation
The gateway reports time-conditional orders that are being monitored, before they are reported to the exchange, with the order status `DelayedNotReported` (see longbridge/developers#1263). No SDK enum matched it, so `impl_serde_for_enum_string!`'s `unwrap_or_default()` turned it into `OrderStatus::Unknown` — monitoring orders were indistinguishable from a genuinely unrecognized status in `today_orders` / `history_orders` / `order_detail` and the order-changed push. Added across all six layers: Rust, C (header regenerated by cbindgen), C++ (enum plus both conversion directions), Java (JNI list plus the Java enum), Node.js (`index.d.ts` regenerated) and Python (plus the `openapi.pyi` stub). The variant is appended after `PartialWithdrawal` rather than placed next to the other `*NotReported` values: the C/C++ and Node.js enums use implicit discriminants, so inserting mid-list would renumber every following value — an ABI break for the C/C++ bindings, and a silent mismatch for TypeScript code that inlined the old `const enum` values.
The committed index.js was generated by an older CLI than the 3.8.6 the
release workflow pins, so regenerating it alongside index.d.ts produces
loader churn unrelated to the enum change. Committing it here keeps the
two generated files in step with each other and with CI.
What the regenerated loader changes:
- `require('node:fs')` → `require('fs')` and the optional chaining in the
musl / win32 probes is expanded, restoring older-Node compatibility
- NAPI_RS_FORCE_WASI is now tri-state ('true' / 'error' / unset) instead
of any-non-empty-string truthy, so NAPI_RS_FORCE_WASI=false or =0 no
longer takes the WASI path and fails with ENOENT
- adds NAPI_RS_WASI_FLAVOR for selecting one exact generated flavor
- WASI candidates are resolved before being required, and the load-error
chain is built without mutating the original errors; `error.cause` is
assigned rather than passed to the Error options form, which Node
< 16.9 ignores
sunli829
marked this pull request as draft
September 20, 2026 07:03
…ayed-not-reported # Conflicts: # CHANGELOG.md
hogan-yuan
marked this pull request as ready for review
October 13, 2026 10:24
…ayed-not-reported # Conflicts: # CHANGELOG.md
Documented in longbridge/developers#1289. A time-condition order is held in DelayedNotReported status and sent automatically at a set time (later than now, at most 90 days ahead). - Rust: SubmitOrderOptions / SubmitMultiLegOrderOptions gain delayed_until(OffsetDateTime); ReplaceOrderOptions gains delayed_until(OffsetDateTime) and clear_delayed_until() (sends 1). Order / OrderDetail gain delayed_until: Option<OffsetDateTime>, defaulting to None when the field is absent or "0". - C: delayed_until pointers (and clear_delayed_until on replace) appended to the option structs; delayed_until on lb_order_t / lb_order_detail_t. Header regenerated by cbindgen. - C++, Java, Node.js (index.d.ts regenerated), Python (+ openapi.pyi) mirror the Rust API.
There was a problem hiding this comment.
🟡 Changes recommended
The C API has binary-layout regressions and abort-prone timestamp conversion paths.
6 open findings
Handle invalid C timestamps without panicking · New Move delayed_until to preserve order struct ABI layout · New Prevent out-of-bounds reads from legacy replace options structs · New Make submit options struct extension ABI-safe · New Make multileg submit context extension ABI-safe · New Update PR metadata to reflect the full feature scope · New
What changed in this PR
Adds time-conditional order support across all SDK layers, including the DelayedNotReported status and scheduled submission fields.
Changes:
- Adds
DelayedNotReportedacross Rust and language bindings. - Adds
delayed_untilsubmission, replacement, and query support. - Regenerates C and Node.js artifacts and updates the changelog.
| File | Description |
|---|---|
rust/src/trade/types.rs |
Adds status and delayed-time response fields. |
rust/src/trade/requests/submit_order.rs |
Adds delayed regular-order submission. |
rust/src/trade/requests/submit_multileg.rs |
Adds delayed multi-leg submission. |
rust/src/trade/requests/replace_order.rs |
Adds updating and clearing delayed submission. |
python/src/trade/types.rs |
Exposes status and response fields to Python. |
python/src/trade/context.rs |
Adds synchronous Python parameters. |
python/src/trade/context_async.rs |
Adds asynchronous Python parameters. |
python/pysrc/longbridge/openapi.pyi |
Updates Python type declarations. |
nodejs/src/trade/types.rs |
Exposes status and response fields to Node.js. |
nodejs/src/trade/requests/submit_order.rs |
Adds delayed regular-order submission. |
nodejs/src/trade/requests/submit_multileg.rs |
Adds delayed multi-leg submission. |
nodejs/src/trade/requests/replace_order.rs |
Adds delayed-time replacement controls. |
nodejs/index.js |
Regenerates the native/WASI loader. |
nodejs/index.d.ts |
Regenerates Node.js declarations. |
java/src/types/enum_types.rs |
Maps the new Java status. |
java/src/types/classes.rs |
Maps delayed-time response fields. |
java/src/trade_context.rs |
Forwards Java delayed-order options. |
java/javasrc/src/main/java/com/longbridge/trade/SubmitOrderOptions.java |
Adds regular-order scheduling. |
java/javasrc/src/main/java/com/longbridge/trade/SubmitMultiLegOrderOptions.java |
Adds multi-leg scheduling. |
java/javasrc/src/main/java/com/longbridge/trade/ReplaceOrderOptions.java |
Adds delayed-time replacement controls. |
java/javasrc/src/main/java/com/longbridge/trade/OrderStatus.java |
Adds the new status constant. |
java/javasrc/src/main/java/com/longbridge/trade/OrderDetail.java |
Exposes delayed submission time. |
java/javasrc/src/main/java/com/longbridge/trade/Order.java |
Exposes delayed submission time. |
cpp/src/trade_context.cpp |
Forwards C++ scheduling options. |
cpp/src/convert.hpp |
Converts status and response fields. |
cpp/include/types.hpp |
Extends public C++ types. |
CHANGELOG.md |
Documents both trade features. |
c/src/trade_context/types.rs |
Extends public C FFI structures. |
c/src/trade_context/enum_types.rs |
Adds the C status value. |
c/src/trade_context/context.rs |
Converts delayed timestamps for requests. |
c/csrc/include/longbridge.h |
Regenerates the public C header. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+478
to
+480
| opts2 = opts2.delayed_until( | ||
| OffsetDateTime::from_unix_timestamp(*(*opts).delayed_until) | ||
| .expect("invalid delayed until"), |
Comment on lines
+943
to
+944
| /// Time-condition submit time (maybe null) | ||
| pub delayed_until: *const i64, |
Comment on lines
+1261
to
+1266
| /// New time-condition submit time, Unix timestamp in seconds (can be | ||
| /// null to keep the current time condition) | ||
| pub delayed_until: *const i64, | ||
| /// Clear the time condition, turning the order into a regular order | ||
| /// (takes precedence over `delayed_until`) | ||
| pub clear_delayed_until: bool, |
Comment on lines
+1312
to
+1315
| /// Time-condition submit time, Unix timestamp in seconds (can be null). | ||
| /// The order is held in `DelayedNotReported` status and only submitted at | ||
| /// this time. | ||
| pub delayed_until: *const i64, |
Comment on lines
+1384
to
+1386
| /// Time-condition submit time, Unix timestamp in seconds (can be null). | ||
| /// Only `LO` / `MO` support time conditions. | ||
| pub delayed_until: *const i64, |
| - **Known differences:** HTTP errors are `Error::HttpClient(OpenApi { .. })` rather than `Error::WsClient(ResponseError { .. })` (`Error::openapi_error_code()` / `into_simple_error()` cover both, but a non-JSON gateway error surfaces as `HttpClientError::UnexpectedHttpResponse` with no code); the REST gateway currently reports business errors (e.g. `301600`, `301607`) as `500`; the per-command rate limits the WebSocket applies from the quote profile are not applied on the HTTP path (the HTTP client retries `429` with back-off instead), and a candlestick top-up turns one call into up to 9 requests against `/quote/history-candlesticks`; during the US overnight session (20:00–04:00 New York) the REST gateway has already rolled over to the next trading day, so `intraday` for a US equity can be empty over HTTP (its lines are all overnight and dropped) and snapshot values of `quote`, `depth`, `calc_indexes` and `capital_flow` may reflect overnight trading that the WebSocket (without `enable_overnight`) does not report; `trades` may include overnight ticks during and shortly after the overnight session. The HTTP path has no client-side rate limiting. | ||
| - **C / C++ / Java / Node.js / Python:** the `QuoteTransport` enum and the corresponding config option are exposed in every binding layer. | ||
| - **All SDKs:** `OrderStatus` gains a `DelayedNotReported` variant (wire value `DelayedNotReported`) — "monitoring", the state a time-conditional order sits in before it is reported to the exchange. Previously this status deserialized to `OrderStatus::Unknown`, so time-conditional orders being monitored were indistinguishable from a genuinely unrecognized status in `today_orders` / `history_orders` / `order_detail` and the order-changed push. The variant is appended **after** `PartialWithdrawal` rather than inserted next to the other `*NotReported` values on purpose: the C/C++/Node.js enums use implicit discriminants, and inserting mid-list would renumber every following value (an ABI break for the C/C++ bindings, and a silent mismatch for TypeScript code that inlined the old `const enum` values) | ||
| - **All SDKs:** time-condition orders (longbridge/developers#1289). `SubmitOrderOptions` and `SubmitMultiLegOrderOptions` gain `delayed_until`: the order is held in `DelayedNotReported` status and sent automatically at that time (later than now, at most 90 days ahead; multi-leg orders support only `LO` / `MO`). `ReplaceOrderOptions` gains `delayed_until` to move the time and `clear_delayed_until` to turn the order back into a regular one; leaving both unset keeps the current time condition. `Order` and `OrderDetail` gain `delayed_until` (unset when the order is not a time-condition order), read from `today_orders` / `history_orders` / `order_detail`. Per layer: Rust `delayed_until(OffsetDateTime)` / `clear_delayed_until()` builder methods and `Option<OffsetDateTime>` fields; C `const int64_t *delayed_until` (Unix seconds) on `lb_submit_order_options_t` / `CSubmitMultiLegOrderOptions` / `lb_replace_order_options_t` (plus `bool clear_delayed_until`; all appended at the end of the structs) and on `lb_order_t` / `lb_order_detail_t`; C++ `std::optional<int64_t> delayed_until` (+ `bool clear_delayed_until` on `ReplaceOrderOptions`); Java `setDelayedUntil(OffsetDateTime)` / `ReplaceOrderOptions.clearDelayedUntil()` and `getDelayedUntil()`; Node.js `delayedUntil?: Date` / `clearDelayedUntil?: boolean` and `get delayedUntil(): Date | null`; Python `delayed_until: Optional[datetime]` / `clear_delayed_until: bool = False` keyword arguments and `Order.delayed_until` / `OrderDetail.delayed_until` |
- Rust: send a delayed_until that maps to 0/1 (the wire sentinels) as 2, so the server rejects it as a past time instead of silently placing a regular order or clearing the condition. Document last-call-wins for delayed_until / clear_delayed_until on replace. - C: report an out-of-range delayed_until through the callback instead of panicking across the FFI boundary; append delayed_until at the end of lb_order_t / lb_order_detail_t so existing offsets are unchanged. - Python: honour tzinfo on aware datetimes (previously their wall-clock time was read as UTC); document the naive/aware rules in the stub. - Node: doc comment refers to delayedUntil. - Tests for submit / multi-leg serialization, the sentinel clamp, OrderDetail delayed_until and the DelayedNotReported status.
- Python: revert the SDK-wide tzinfo change to PyOffsetDateTimeWrapper, which shifted wall-clock parameters such as history_candlesticks_by_offset(time=...). delayed_until now uses a dedicated PyInstantWrapper: aware datetimes (utcoffset() not None) convert by their offset, naive ones are read as UTC. - Node: the submit / replace / multi-leg option conversions are now TryFrom, so an out-of-range delayedUntil Date is an InvalidArg error instead of a panic. - Rust: test that the last of delayed_until / clear_delayed_until wins. Verified end to end against a local fake HTTP server with the built Python wheel: aware +08:00, aware UTC and naive inputs all send 1767225600; epoch is clamped to 2; clear sends 1 and wins over a time.
- Document the later-than-now / 90-day limit on delayed_until in the C, C++, Node multi-leg and Python multi-leg docs (header and index.d.ts regenerated). - C: parse_delayed_until returns the small ComponentRange error and converts it to longbridge::Error at the call site, avoiding a new clippy::result_large_err warning.
hogan-yuan
force-pushed
the
feat/order-status-delayed-not-reported
branch
from
October 13, 2026 14:42
d5a71db to
fa2d455
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Follows longbridge/developers#1263, which documents the new
DelayedNotReportedorder status — 监控中 (时间条件单), the state a time-conditional order sits in before it is reported to the exchange.Problem
No SDK enum variant matched that wire value.
OrderStatusdeserializes throughimpl_serde_for_enum_string!, which falls back tounwrap_or_default()→OrderStatus::Unknown, so nothing failed loudly — monitoring time-conditional orders simply became indistinguishable from a genuinely unrecognized status intoday_orders/history_orders/order_detailand the order-changed push.Changes
All six layers, per the checklist in
CLAUDE.md:rust/src/trade/types.rs(#[strum(serialize = "DelayedNotReported")])c/src/trade_context/enum_types.rs;longbridge.hregenerated by cbindgencpp/include/types.hpp+ both conversion directions incpp/src/convert.hppjava/src/types/enum_types.rs+java/javasrc/.../trade/OrderStatus.javanodejs/src/trade/types.rs;index.d.tsregenerated bynpm run build:debugpython/src/trade/types.rs+ theopenapi.pyistubWhy the variant is appended instead of grouped
The natural place would be next to the other
*NotReportedvalues, matching the docs table. It is appended afterPartialWithdrawalinstead, because the C/C++ and Node.js enums use implicit discriminants:lb_order_status_tinlongbridge.hhas no explicit values — inserting mid-list shifts every following one, an ABI break for anything linked against a prebuilt C/C++ library.nodejs/index.d.tsemitsexport declare const enum OrderStatus { … Filled = 5 … }, and TypeScript inlinesconst enummembers at the consumer's compile time. A renumbering would make already-compiled application code read its hard-coded5as a different status — a silent mismatch rather than a build error.Appending keeps every existing discriminant stable (
DelayedNotReported = 18in TS). The declaration order does not have to match the docs table.Second commit: regenerated
nodejs/index.jsnpm run build:debugregeneratesindex.jsalongsideindex.d.ts, and the committed copy turns out to predate the@napi-rs/cli@3.8.6the release workflow pins — so it comes back with ~160 lines of loader churn that has nothing to do with the enum. It is committed separately (38184a7) to bring the two generated files back in step with each other and with CI. Review it as a standalone change; what the newer generator emits:require('node:fs')→require('fs'), and the optional chaining in the musl / win32 probes is expanded — older-Node compatibilityNAPI_RS_FORCE_WASIbecomes tri-state ('true'/'error'/ unset) instead of any-non-empty-string truthy, soNAPI_RS_FORCE_WASI=falseor=0no longer takes the WASI path and fails with ENOENTNAPI_RS_WASI_FLAVORfor pinning one exact generated flavorrequire.resolve'd before being required, and the load-error chain is built without mutating the original errors;error.causeis assigned rather than passed via theErroroptions form, which Node < 16.9 ignoresVerification
cargo clippy --all --all-features— no new warningscargo +nightly fmt --allcargo build -p longbridge-c— header regeneratedg++ -fsyntax-only -std=c++17overcpp/src/convert.hpp— bothconvert()overloads compilenpm run build:debug—index.d.ts/index.jsregenerated🤖 Generated with Claude Code