Skip to content

feat(trade): add DelayedNotReported status and time-condition orders (DelayedUntil) - #123

Merged
hogan-yuan merged 11 commits into
mainfrom
feat/order-status-delayed-not-reported
Oct 14, 2026
Merged

hogan-yuan merged 11 commits into
mainfrom
feat/order-status-delayed-not-reported

Conversation

@hogan-yuan

@hogan-yuan hogan-yuan commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Ports longbridge/openapi#595: the DelayedNotReported order status (longbridge/developers#1263) and time-condition orders (longbridge/developers#1289).

Changes

  • trade.OrderDelayedNotReported: a new OrderStatus constant for the wire value "DelayedNotReported", meaning a time-condition order that is held and not yet reported to the exchange. OrderStatus is a plain string, so the value was already passed through; the constant lets callers compare against it instead of a literal.
  • DelayedUntil int64 (Unix seconds; 0 is not sent) on SubmitOrder, SubmitMultiLegOrder and ReplaceOrder.
    • On submit, the order is held in OrderDelayedNotReported and submitted at that time (later than now, at most 90 days ahead; multi-leg supports only LO / MO).
    • On replace, 0 keeps the current time condition, the new ClearDelayedUntil constant (1) clears it, and any other value sets a new time.
  • DelayedUntil string on Order / OrderDetail: the raw Unix-seconds string from TodayOrders / HistoryOrders / OrderDetail. It is "0", or empty if the server omits the field, when the order is not a time-condition order. This mirrors the existing TriggerAt string. The wire field also accepts a JSON number (or null), so a server-side type change in this new field cannot fail the whole response.

Raw int64 seconds follow the SDK's existing request conventions (ExpireTime, GetCashFlow.StartAt). The mapping to the wire types goes through util.Copy, the same as the neighbouring fields.

Verification

  • go build ./..., go vet ./trade/... and go test ./trade/... pass.
  • trade/delayed_until_test.go exercises the real util.Copy + encoding/json path:
    • the wire values for submit / multi-leg / replace, including ClearDelayedUntil and a new time
    • the field is omitted at zero for all three request types
    • responses with "0", a timestamp, a JSON number, null, and no field

🤖 Generated with Claude Code

Port longbridge/openapi #595.

Add the OrderDelayedNotReported constant for the "DelayedNotReported"
wire value: monitoring, the state a time-condition order sits in before
it is reported to the exchange. OrderStatus is a plain string, so the
value was already passed through; the constant lets callers compare
against it instead of a string literal.
hogan-yuan added a commit that referenced this pull request Sep 28, 2026
Cut **v0.28.0** — closes out the `[Unreleased]` CHANGELOG section as
`[v0.28.0] - 2026-09-28`.

## Included since v0.27.0
- **Grid trading** (`grid.GridContext`); `SubmitStrategyQuestionnaire`
endpoint removed
- **Removed all symbol↔counter_id conversions** (port longbridge/openapi
#562) — endpoints send/receive the user symbol directly; `counter`
package removed
- **Multi-leg option orders** — `TradeContext.SubmitMultiLeg` +
`MultiLegInfo` on orders/push (#575, #589, #590)
- **Option chain onto the HTTP endpoint** (#588, **breaking**) —
`StrikePriceInfo` → `OptionChainContract`; see gateway note below
- **FinanceCalendar pagination** (#597, **breaking**) —
`count`/`offset`/`next` + `CalendarPageDirection`
- **HistoryExecutions pages through all `has_more`** (#591)
- **SignalContext** — `Signals` / `Signal` / `SecurityFacts` (#577,
#578)
- **Type refinements** — `Execution.Side`, typed `AlertValueMap`, typed
`RankCategories`, `CorpActionLive.Status` string,
`IndustryPeersResponse.Top` optional, `WarrantStatusUnknown`, Greek doc
refresh (#586)
- Ratings temporarily disabled; `TopMovers.NextParams` string; portfolio
flows timestamp

## ⚠️ Gateway dependency (#588)
`QuoteContext.OptionChainInfoByDate` now calls `GET
/v1/gemini/option/option_chain_list`. That endpoint must be deployed on
the target gateway. Verified live for this release: **available on
staging, not yet on production** (returns `404 api not found` there
until deployed). Called out prominently at the top of the v0.28.0
CHANGELOG entry.

## Verification
Read-only endpoints exercised live against staging + production (report
with per-endpoint request/response params): every change verified on at
least one environment — see internal report. `go build ./...` and
package tests pass.

## Not included
`#123` (DelayedNotReported) and `#124` (fund) remain open for a later
release.

## After merge
Tag `v0.28.0` on the merge commit and push (Go modules resolve by tag).
@hogan-yuan
hogan-yuan marked this pull request as ready for review October 13, 2026 10:28
Port longbridge/openapi #595 (longbridge/developers#1289).

- SubmitOrder, SubmitMultiLegOrder and ReplaceOrder gain DelayedUntil
  (unix seconds, omitted when 0). On replace, ClearDelayedUntil (1)
  clears the time condition.
- Order and OrderDetail gain DelayedUntil, read from the delayed_until
  field of the order query responses.
- CHANGELOG: move both entries from Fixed to Added and drop the
  incorrect claim that DelayedNotReported used to map to an unknown
  status (Go OrderStatus is a plain string passthrough).
- Document that Order/OrderDetail DelayedUntil can be empty as well as
  "0", and warn against a literal 1 / zero on ReplaceOrder.
- Replace the Chinese godoc text with English.
- Tests: zero omitted on submit and multi-leg too, a non-sentinel
  replace time, and a response without delayed_until.
@hogan-yuan
hogan-yuan force-pushed the feat/order-status-delayed-not-reported branch from f84b074 to fd0f764 Compare October 13, 2026 14:42
Mirrors the Rust core: the wire field is a FlexString that also takes a
number (or null), so a type change in this newly added field cannot
fail TodayOrders / HistoryOrders / OrderDetail.
@hogan-yuan hogan-yuan changed the title feat(trade): add DelayedNotReported order status feat(trade): add DelayedNotReported status and time-condition orders (DelayedUntil) Oct 14, 2026
hogan-yuan added a commit to longbridge/openapi that referenced this pull request Oct 14, 2026
…rders (#595)

Follows
[longbridge/developers#1263](longbridge/developers#1263)
(the new `DelayedNotReported` order status) and
[longbridge/developers#1289](longbridge/developers#1289)
(time-condition orders).

## 1. `OrderStatus::DelayedNotReported`

**Monitoring (time-condition order)**: the state a time-condition order
sits in before it is reported to the exchange.

No SDK enum variant matched that wire value. `OrderStatus` deserializes
through `impl_serde_for_enum_string!`, which falls back to
`unwrap_or_default()` → `OrderStatus::Unknown`, so nothing failed
loudly. Monitored time-condition orders simply became 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
grouped with the other `*NotReported` values. The C/C++ and Node.js
enums use implicit discriminants:

- `lb_order_status_t` has no explicit values, so inserting mid-list
would shift every following value. That is an ABI break for anything
linked against a prebuilt C/C++ library.
- `nodejs/index.d.ts` emits a `const enum`, which TypeScript inlines at
the consumer's compile time. Renumbering would silently change
already-compiled code. Appending keeps every existing discriminant
stable (`DelayedNotReported = 18`).

## 2. Time-condition orders (`delayed_until`)

| Endpoint | SDK |
|---|---|
| Submit order / submit multi-leg order | `delayed_until`: hold the
order in `DelayedNotReported` and submit it at that time (later than
now, at most 90 days ahead; multi-leg supports only `LO` / `MO`) |
| Replace order | `delayed_until` moves the time; `clear_delayed_until`
turns it back into a regular order (wire `1`); leaving both unset keeps
the current time condition |
| `today_orders` / `history_orders` / `order_detail` |
`Order.delayed_until` / `OrderDetail.delayed_until`, unset when the
server returns `"0"` or omits it |

Per layer:

- **Rust:** `delayed_until(OffsetDateTime)` builders and
`ReplaceOrderOptions::clear_delayed_until()` (last call wins);
`Option<OffsetDateTime>` response fields.
- **C:** `const int64_t *delayed_until` (Unix seconds) on the three
option structs, plus `bool clear_delayed_until` on replace. Also on
`lb_order_t` / `lb_order_detail_t`. **Every new field is appended at the
end of its struct**, so existing field offsets are unchanged.
- **C++:** `std::optional<int64_t> delayed_until`, plus `bool
clear_delayed_until = false`.
- **Java:** `setDelayedUntil(OffsetDateTime)`,
`ReplaceOrderOptions.clearDelayedUntil()`, `getDelayedUntil()`.
- **Node.js:** `delayedUntil?: Date`, `clearDelayedUntil?: boolean`,
`get delayedUntil(): Date | null`.
- **Python:** `delayed_until: Optional[datetime]` /
`clear_delayed_until: bool = False` keyword arguments (appended, so
positional callers are unaffected), and `Order.delayed_until` /
`OrderDetail.delayed_until`.

In the bindings, clear takes precedence over a time when both are set.

### Edge cases handled

- **Wire sentinels:** a time at or before `1970-01-01T00:00:01Z` would
map to `0` (no condition) or `1` (clear), so it is sent as `2` instead.
The server then rejects it as a past time rather than silently placing a
regular order or clearing the condition.
- **Out-of-range timestamps:**
- C reports the error through the callback instead of panicking across
the FFI boundary.
- Node.js returns an `InvalidArg` error instead of aborting the process.
- **Lenient response parsing:** the response field also accepts a JSON
integer. A malformed, negative or out-of-range value reads as unset
instead of failing the whole `today_orders` / `history_orders` /
`order_detail` call, so users who upgrade are not exposed to a field the
server never sent before.
- **Python time zones:** `delayed_until` uses a dedicated converter.
- A timezone-aware `datetime` (one whose `utcoffset()` is not `None`)
converts by its real offset; a naive one is read as UTC.
- Other `datetime` parameters keep their existing wall-clock semantics.
For example, `history_candlesticks_by_offset(time=...)` relies on that.

## Second commit: regenerated `nodejs/index.js`

`npm run build:debug` regenerates `index.js` alongside `index.d.ts`. The
committed copy predated the `@napi-rs/cli@3.8.6` that the release
workflow pins, so it was regenerated separately (`38184a7`); it is
loader churn unrelated to the API changes.

## Verification

- `cargo clippy --all --all-features`: no new warnings. `cargo +nightly
fmt --all` applied.
- `cargo test -p longbridge --lib`: 70 passed. New tests cover:
  - request serialization for submit / multi-leg / replace
  - the sentinel clamp, and last-call-wins on replace
  - `"0"` / absent / `null` / integer / malformed responses
  - the new status
- `cargo build -p longbridge-c` (header regenerated); `clang++
-fsyntax-only` on `cpp/src/trade_context.cpp`.
- `cargo build -p longbridge-java` and `mvn compile` for the Java
classes.
- `npm run build:debug`: `index.d.ts` regenerated. Checked with a script
that an out-of-range `delayedUntil` returns `InvalidArg` and the process
stays alive.
- Python: built the wheel and called `submit_order` / `replace_order`
against a local fake HTTP server.
  - Aware `+08:00`, aware UTC and naive inputs all send `1767225600`.
  - The epoch is sent as `2`.
  - Clear sends `1` and wins over a time.
  - The field is omitted when unset.
- Not verified: an end-to-end order against a real account.

Go SDK counterpart: longbridge/openapi-go#123.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: 袁章洪 <zhanghong.yuan@longbridge-inc.com>
@hogan-yuan
hogan-yuan merged commit 034c076 into main Oct 14, 2026
@hogan-yuan
hogan-yuan deleted the feat/order-status-delayed-not-reported branch October 14, 2026 03:44
hogan-yuan added a commit that referenced this pull request Oct 14, 2026
Cut **v0.29.1** — closes out the `[Unreleased]` CHANGELOG section as
`[v0.29.1] - 2026-10-14`.

## Included since v0.29.0
- **Added — time-condition orders** (#123): `DelayedUntil` on
`SubmitOrder` / `SubmitMultiLegOrder` / `ReplaceOrder`
(`ClearDelayedUntil` clears it on replace), `DelayedUntil` on `Order` /
`OrderDetail`, and the `OrderDelayedNotReported` status constant. Ports
longbridge/openapi#595.
- **Added — quote transport** (#130): `quote.Transport` to serve quote
pull APIs over HTTP, plus the related `Changed` / `Fixed` items
(`LONGBRIDGE_ENABLE_OVERNIGHT`, idempotent `QuoteContext.Close`,
`Subscribe` error propagation, WebSocket leak fixes).

Only `CHANGELOG.md` changes. The `v0.29.1` tag is created after merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant