Conversation
The option chain moves off quote socket commands 20/21 onto the fundamental-data OpenAPI service, so the old pages come down and the contract list ships as its own HTTP page rather than inheriting their URLs. - drop quote/options/optionchain_date and optionchain-date-strike across en/zh-CN/zh-HK - add quote/options/option-chain, slug /quote/pull/option-chain, documenting standard_only and the standard_attr / option_type / days_to_expiry fields - response example is a real SIT payload (BABA.US 20261218) carrying old contracts - rework the quote/overview rows and rowspan; socket/biz_command loses both entries since the endpoint is no longer a business command
sunli829
added a commit
to longbridge/openapi
that referenced
this pull request
Sep 20, 2026
## Background longbridge/developers#1244 moves the option chain off the quote socket business commands onto the fundamental-data OpenAPI service (gemini `OpenAPIOptionChain`). The contract list ships as its own plain-HTTP endpoint: ``` GET /v1/gemini/option/option_chain_list symbol string required underlying, ticker.region, e.g. BABA.US expiry_date string required YYYYMMDD, e.g. 20261218 standard_only bool optional true/1 returns standard contracts only; omitted or false returns everything ``` This is not a URL swap — the response shape changes, so the SDK's return type had to be rebuilt. ## What changed `QuoteContext.option_chain_info_by_date` keeps its name (it still takes an expiry date, so the name is still accurate) but is now backed by the HTTP endpoint instead of socket command `21`. **Return type.** The old command paired contracts by strike price (`price` + `call_symbol` + `put_symbol` + `standard`). The new endpoint returns **one entry per contract**, so the paired `StrikePriceInfo` is removed in favour of a flat `OptionChainContract`: | field | type | note | |---|---|---| | `symbol` | string | option contract code, `ticker.region` | | `expiry_date` | date | US Eastern time | | `strike_price` | decimal | | | `direction` | `OptionDirection` | existing enum, reused | | `option_type` | `OptionExpiryCycleType` | **new enum** | | `standard_attr` | `OptionStandardAttr` | **new enum** | | `days_to_expiry` | int32 | `0` on the expiry day, negative once expired | A strike listed on one side only now yields a single entry instead of an entry with an empty `call_symbol`/`put_symbol`. **Callers that read those two fields must filter on `direction` instead.** **Two new enums.** `OptionExpiryCycleType` (`Unknown` / `Monthly` / `Weekly` / `Quarterly`) — the server's empty `option_type` means a standard monthly option. `OptionStandardAttr` (`Unknown` / `Normal` / `Old`) marks the legacy contracts left over from a corporate action. `Unknown` is the first variant in every layer and is the `Default`, i.e. the fallback for an unrecognized server value; the meaningful empty string maps explicitly to `Monthly` / `Normal`. **New `standard_only` parameter.** Positional `bool` in Rust (incl. blocking), C, C++ and Java; optional `standardOnly?: boolean` in Node.js; `standard_only: bool = False` in Python. It is omitted from the query string when false, which is the endpoint's documented "return everything" path. **Cache dropped.** The 30-minute client-side cache of the chain is gone, matching the other HTTP quote endpoints — and `days_to_expiry` changes daily, so caching it was wrong anyway. The separate `option_chain_expiry_date_list` cache is untouched. `cmd_code::GET_OPTION_CHAIN_INFO_BY_DATE` (21) is removed; command `20` stays. ## Deliberately out of scope `QuoteContext.option_chain_expiry_date_list` is **unchanged** and still uses socket command `20`. `expiry_date` is a hard requirement of the new endpoint, so it cannot enumerate expiry dates, and the gateway path for the replacement expiry-date-list endpoint is not settled upstream (the developers PR says as much). Probed on the canary to be sure: | request | result | |---|---| | `symbol` + `expiry_date=20261218` | 206 rows, 1 distinct `expiry_date` | | `expiry_date` omitted | `310010` invalid parameter | | `expiry_date=` (empty) | `310010` invalid parameter | | `expiry_date=0` | `310010` invalid parameter | Five guessed paths (`option_expiry_date_list`, `expiry_date_list`, `option_chain_date_list`, `option_dates`, `expiry_dates` under `/v1/gemini/option/`) all returned `404000 api not found`. ## Layers touched All six, per `CLAUDE.md`: - **Rust** — `option_chain_info_by_date` rewritten on `http_cli` (modelled on `security_list` / `option_volume`); `OptionChainContract` + the two enums in `quote/types.rs`; blocking wrapper; exports; `cmd_code`. - **C** — `lb_option_chain_contract_t`, `lb_option_expiry_cycle_type_t`, `lb_option_standard_attr_t` (`lb_strike_price_info_t` removed); `lb_quote_context_option_chain_info_by_date` gains `bool standard_only` before the callback; three `cbindgen.toml` renames + `include` entry so the header does not leak the raw Rust names. - **C++** — `quote::OptionChainContract` and the two `enum class`es, explicit `switch` converters (not `static_cast`), and the copy-pasted `option_chain_info_by_date` doc comment fixed (it read "Get option chain expiry date list"). - **Java** — new `OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr` classes, `StrikePriceInfo` deleted, `java/Makefile.toml` source list updated. The JNI parameter is `jboolean` (not Rust `bool`), and all three halves agree: `(JLjava/lang/String;Ljava/time/LocalDate;ZLcom/longbridge/AsyncCallback;)V`. - **Node.js** — `OptionChainContract` + the two napi enums; `index.d.ts`/`index.js` regenerated. - **Python** — sync + async, `#[pyo3(signature = (…, standard_only = false))]`, three new `add_class` registrations, and `openapi.pyi` updated by hand. ### Implementation note The wire values (`"C"`, `"W"`, `"old"`, `"20261218"`, `"50"`) are parsed in a private `RawOptionChainContract` inside the method, following the `option_volume` pattern. `OptionDirection`'s existing serde representation is deliberately left untouched, because `OptionQuote` embeds it. `option_type`, `standard_attr`, `symbol` and `days_to_expiry` tolerate an explicit JSON `null`. The now-dead `OptionChainDateStrikeInfoRequest`/`Response`/`StrikePriceInfo` protobuf messages are left in place: `openapi-protobufs` cannot be regenerated here, and as `pub` items of a separate crate they trigger no `dead_code` warning. They will drop out with the next submodule update. ## Verification `cargo clippy --all --all-features` — 0 errors. `cargo +nightly fmt --all`. `cargo test --doc -p longbridge` — 56 passed. `cargo build` clean for `longbridge-c` / `-java` / `-python`; all CMake targets clean; `npm run build:debug` clean; `openapi.pyi` parses. Probed live against the canary (`BABA.US` / `20261218` — the same data as the response example in the developers PR): - `standard_only=false` → 206 rows (103 calls / 103 puts, 112 of them `standard_attr = Old`, including `BABA2261218C10000.US`); `standard_only=true` → 94 rows, no `Old`. - All three `option_type` values occur in the wild, so the mapping is not just paper: `""` → `Monthly` (AAPL 2026-09-18), `"W"` → `Weekly` (six AAPL weekly expiries), `"Q"` → `Quarterly` (QQQ / IWM 2027-06-30). - `days_to_expiry` is negative for already-expired contracts (`-5` for AAPL 2026-09-02), as documented. - **Zero `Unknown` fallbacks** across every sample — no unmapped server value. Not run here: `javac` / `cargo make javah` and `maturin develop` — no JDK or maturin on this machine. `java/c/com_longbridge_SdkNative.h` was therefore synced by hand (a mechanical one-line change) and should be re-generated on a machine with a JDK. `cargo build -p longbridge-python` passing is the real compile check for the PyO3 side; `openapi.pyi` has no build step. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
Background
The option chain moves off quote socket commands 20/21 onto the fundamental-data OpenAPI service (gemini
OpenAPIOptionChain). The old pages come down and the contract list ships as its own HTTP page on a new URL.Changes
Removed —
quote/options/optionchain_dateandoptionchain-date-strike, all three locales. Both rows also drop out ofsocket/biz_command, since the endpoint is no longer a business command.Added —
quote/options/option-chain, slug/quote/pull/option-chain, all three locales. Follows the plain-HTTP page shape used byai/agents.mdx(http-basictable + Query Parameters + cURL tab + Schemas). NoSDKLinks/CliCommand/QuotePermission: the SDK method name is not settled, the CLI has no such command, and this endpoint carries no quote-permission gate.quote/overviewkeeps one row for the endpoint; the Pull group'srowspanis adjusted to match and was verified against the actual row count in every locale.Contract documented
standard_only(query)true/1returns standard contracts only; omitted orfalsereturns everything includingstandard_attr=olddirectionC/Poption_typeW/Q/ emptystandard_attrold/ emptydays_to_expiry0on expiry day, negative once expiredThe response example is a real SIT payload (
BABA.US/20261218) and includes genuine post-corporate-action contracts (BABA2261218C10000.US), not invented ones.Notes
/quote/pull/optionchain-date(-strike)now 404 — new slug was chosen deliberately over reusing the old one.directionis a literalC/Prather than the enum ordinal it used to serialize as, matchingoption_typeandstandard_attr. The gemini-side proto change lands alongside this.