feat!: move the option chain onto the OpenAPI endpoint - #588
Draft
sunli829 wants to merge 1 commit into
Draft
Conversation
`QuoteContext.option_chain_info_by_date` moves off quote socket business command `21` onto the plain HTTP endpoint `GET /v1/gemini/option/option_chain_list` (longbridge/developers#1244). The wire shape changes with it. 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` carrying `symbol`, `expiry_date`, `strike_price`, `direction`, `option_type`, `standard_attr` and `days_to_expiry`. A strike listed on one side only now yields a single entry instead of an entry with an empty `call_symbol`/`put_symbol`, and callers that read those two fields must filter on `direction` instead. Two new enums come with the new fields: `OptionExpiryCycleType` (`Unknown` / `Monthly` / `Weekly` / `Quarterly`) for the expiration cycle — the server's empty `option_type` means a standard monthly option — and `OptionStandardAttr` (`Unknown` / `Normal` / `Old`) marking the legacy contracts left over from a corporate action. Both fall back to `Unknown` for an unrecognized server value. The method also gains a `standard_only` parameter that filters those legacy contracts out server-side. It is omitted from the query string when false, which is the endpoint's documented "return everything" default. The 30-minute client-side cache of the chain is dropped, matching the other HTTP quote endpoints — and `days_to_expiry` changes daily, so caching it was wrong anyway. `QuoteContext.option_chain_expiry_date_list` is deliberately left alone and still uses socket command `20`: the new endpoint requires `expiry_date`, so it cannot enumerate expiry dates, and the gateway path for the replacement expiry-date-list endpoint is not settled upstream yet. Verified live against the canary environment.
sunli829
marked this pull request as draft
September 9, 2026 01:48
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
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:
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_datekeeps its name (it still takes anexpiry 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 returnsone entry per contract, so the paired
StrikePriceInfois removed in favourof a flat
OptionChainContract:symbolticker.regionexpiry_datestrike_pricedirectionOptionDirectionoption_typeOptionExpiryCycleTypestandard_attrOptionStandardAttrdays_to_expiry0on the expiry day, negative once expiredA 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 fieldsmust filter on
directioninstead.Two new enums.
OptionExpiryCycleType(Unknown/Monthly/Weekly/Quarterly) — the server's emptyoption_typemeans a standard monthly option.OptionStandardAttr(Unknown/Normal/Old) marks the legacy contractsleft over from a corporate action.
Unknownis the first variant in every layerand is the
Default, i.e. the fallback for an unrecognized server value; themeaningful empty string maps explicitly to
Monthly/Normal.New
standard_onlyparameter. Positionalboolin Rust (incl. blocking),C, C++ and Java; optional
standardOnly?: booleanin Node.js;standard_only: bool = Falsein Python. It is omitted from the query stringwhen 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_expirychanges daily, socaching it was wrong anyway. The separate
option_chain_expiry_date_listcacheis untouched.
cmd_code::GET_OPTION_CHAIN_INFO_BY_DATE(21) is removed; command20stays.Deliberately out of scope
QuoteContext.option_chain_expiry_date_listis unchanged and still usessocket command
20.expiry_dateis a hard requirement of the new endpoint, soit 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:
symbol+expiry_date=20261218expiry_dateexpiry_dateomitted310010invalid parameterexpiry_date=(empty)310010invalid parameterexpiry_date=0310010invalid parameterFive guessed paths (
option_expiry_date_list,expiry_date_list,option_chain_date_list,option_dates,expiry_datesunder/v1/gemini/option/) all returned404000 api not found.Layers touched
All six, per
CLAUDE.md:option_chain_info_by_daterewritten onhttp_cli(modelled onsecurity_list/option_volume);OptionChainContract+ the two enums inquote/types.rs; blocking wrapper; exports;cmd_code.lb_option_chain_contract_t,lb_option_expiry_cycle_type_t,lb_option_standard_attr_t(lb_strike_price_info_tremoved);lb_quote_context_option_chain_info_by_dategainsbool standard_onlybefore the callback; three
cbindgen.tomlrenames +includeentry so theheader does not leak the raw Rust names.
quote::OptionChainContractand the twoenum classes, explicitswitchconverters (notstatic_cast), and the copy-pastedoption_chain_info_by_datedoc comment fixed (it read "Get option chainexpiry date list").
OptionChainContract/OptionExpiryCycleType/OptionStandardAttrclasses,StrikePriceInfodeleted,java/Makefile.tomlsource list updated. The JNI parameter is
jboolean(not Rustbool), andall three halves agree:
(JLjava/lang/String;Ljava/time/LocalDate;ZLcom/longbridge/AsyncCallback;)V.OptionChainContract+ the two napi enums;index.d.ts/index.jsregenerated.#[pyo3(signature = (…, standard_only = false))],three new
add_classregistrations, andopenapi.pyiupdated by hand.Implementation note
The wire values (
"C","W","old","20261218","50") are parsed in aprivate
RawOptionChainContractinside the method, following theoption_volumepattern.
OptionDirection's existing serde representation is deliberately leftuntouched, because
OptionQuoteembeds it.option_type,standard_attr,symbolanddays_to_expirytolerate an explicit JSONnull.The now-dead
OptionChainDateStrikeInfoRequest/Response/StrikePriceInfoprotobuf messages are left in place:
openapi-protobufscannot be regeneratedhere, and as
pubitems of a separate crate they trigger nodead_codewarning. 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 buildclean forlongbridge-c/-java/-python; all CMake targets clean;npm run build:debugclean;openapi.pyiparses.Probed live against the canary (
BABA.US/20261218— the same data as theresponse example in the developers PR):
standard_only=false→ 206 rows (103 calls / 103 puts, 112 of themstandard_attr = Old, includingBABA2261218C10000.US);standard_only=true→ 94 rows, noOld.option_typevalues occur in the wild, so the mapping is not justpaper:
""→Monthly(AAPL 2026-09-18),"W"→Weekly(six AAPL weeklyexpiries),
"Q"→Quarterly(QQQ / IWM 2027-06-30).days_to_expiryis negative for already-expired contracts (-5for AAPL2026-09-02), as documented.
Unknownfallbacks across every sample — no unmapped server value.Not run here:
javac/cargo make javahandmaturin develop— no JDK ormaturin on this machine.
java/c/com_longbridge_SdkNative.hwas thereforesynced by hand (a mechanical one-line change) and should be re-generated on a
machine with a JDK.
cargo build -p longbridge-pythonpassing is the realcompile check for the PyO3 side;
openapi.pyihas no build step.🤖 Generated with Claude Code