Skip to content

docs(quote): replace the option chain pages with the OpenAPI endpoint - #1244

Open
nlimpid wants to merge 1 commit into
longbridge:mainfrom
nlimpid:docs/openapi-option-chain
Open

nlimpid wants to merge 1 commit into
longbridge:mainfrom
nlimpid:docs/openapi-option-chain

Conversation

@nlimpid

@nlimpid nlimpid commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

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

Removedquote/options/optionchain_date and optionchain-date-strike, all three locales. Both rows also drop out of socket/biz_command, since the endpoint is no longer a business command.

Addedquote/options/option-chain, slug /quote/pull/option-chain, all three locales. Follows the plain-HTTP page shape used by ai/agents.mdx (http-basic table + Query Parameters + cURL tab + Schemas). No SDKLinks / CliCommand / QuotePermission: the SDK method name is not settled, the CLI has no such command, and this endpoint carries no quote-permission gate.

quote/overview keeps one row for the endpoint; the Pull group's rowspan is adjusted to match and was verified against the actual row count in every locale.

Contract documented

field value
standard_only (query) true / 1 returns standard contracts only; omitted or false returns everything including standard_attr = old
direction C / P
option_type W / Q / empty
standard_attr old / empty
days_to_expiry ET-midnight based, 0 on expiry day, negative once expired

The response example is a real SIT payload (BABA.US / 20261218) and includes genuine post-corporate-action contracts (BABA2261218C10000.US), not invented ones.

Notes

  • Old URLs /quote/pull/optionchain-date(-strike) now 404 — new slug was chosen deliberately over reusing the old one.
  • The expiry-date list endpoint is not documented yet; its gateway path is still undecided, so that page is deliberately out of scope here.
  • direction is a literal C/P rather than the enum ordinal it used to serialize as, matching option_type and standard_attr. The gemini-side proto change lands alongside this.

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)
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