Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [5.0.0] - 2026-09-14
## [5.1.0] - 2026-09-20

### Breaking changes

- **All SDKs:** `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), and its return type changes shape. The paired `StrikePriceInfo` (`price` + `call_symbol` + `put_symbol` + `standard`) is **removed** in favour of a flat, one-entry-per-contract `OptionChainContract` (`symbol`, `expiry_date`, `strike_price`, `direction`, `option_type`, `standard_attr`, `days_to_expiry`). Calls and puts are no longer paired by strike, so 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 must filter on `direction` where they used to read the two symbol fields. Two new enums come with it: `OptionExpiryCycleType` (`Unknown` / `Monthly` / `Weekly` / `Quarterly`) for the special 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 (e.g. `BABA2261218C10000.US`); 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 (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 the endpoint treats as "return everything". The 30-minute client-side cache of the chain is gone, matching the other HTTP quote endpoints. New types per layer: C `lb_option_chain_contract_t` / `lb_option_expiry_cycle_type_t` / `lb_option_standard_attr_t` (`lb_strike_price_info_t` removed, and `lb_quote_context_option_chain_info_by_date` gains a `bool standard_only` argument before the callback), C++ `quote::OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr`, Java `com.longbridge.quote.OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr` (`StrikePriceInfo` deleted, and `getOptionChainInfoByDate` becomes `getOptionChainInfoByDate(String, LocalDate, boolean)`), Node.js `OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr`, and Python the same three (incl. the `openapi.pyi` stub). `QuoteContext.option_chain_expiry_date_list` is **unchanged** 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

### Added

- **All languages:** `MultiLegStrategy` gains `CalendarCallSpread` (`7`) and `CalendarPutSpread` (`8`) — calendar (horizontal) spreads, accepted by `TradeContext.submit_multileg` and reported back on the `multi_leg` field of order queries and the order-changed push. Both are appended after `Strangle`, so the discriminants of the existing variants are unchanged. Added across Rust, C, C++, Java, Node.js, and Python. Documented in longbridge/developers#1251

### Changed

- **All SDKs:** refreshed the option Greek field descriptions on `SecurityCalcIndex` (`calc_indexes`) — `delta` / `gamma` / `theta` / `vega` / `rho` — to match the Calc Index interface documentation. Notably, `theta` is now documented as a per-day value: the server divides the raw annualized value by 365, so the returned `theta` is already daily (previously the doc said the raw value was annualized by 252 and had to be divided by 252). Doc comments only — no field or behaviour changes

## [5.0.0] - 2026-09-14

### Breaking changes

- **All SDKs:** `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), and its return type changes shape. The paired `StrikePriceInfo` (`price` + `call_symbol` + `put_symbol` + `standard`) is **removed** in favour of a flat, one-entry-per-contract `OptionChainContract` (`symbol`, `expiry_date`, `strike_price`, `direction`, `option_type`, `standard_attr`, `days_to_expiry`). Calls and puts are no longer paired by strike, so 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 must filter on `direction` where they used to read the two symbol fields. Two new enums come with it: `OptionExpiryCycleType` (`Unknown` / `Monthly` / `Weekly` / `Quarterly`) for the special 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 (e.g. `BABA2261218C10000.US`); 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 (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 the endpoint treats as "return everything". The 30-minute client-side cache of the chain is gone, matching the other HTTP quote endpoints. New types per layer: C `lb_option_chain_contract_t` / `lb_option_expiry_cycle_type_t` / `lb_option_standard_attr_t` (`lb_strike_price_info_t` removed, and `lb_quote_context_option_chain_info_by_date` gains a `bool standard_only` argument before the callback), C++ `quote::OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr`, Java `com.longbridge.quote.OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr` (`StrikePriceInfo` deleted, and `getOptionChainInfoByDate` becomes `getOptionChainInfoByDate(String, LocalDate, boolean)`), Node.js `OptionChainContract` / `OptionExpiryCycleType` / `OptionStandardAttr`, and Python the same three (incl. the `openapi.pyi` stub). `QuoteContext.option_chain_expiry_date_list` is **unchanged** 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
- **All SDKs:** removed `GridContext.submit_strategy_questionnaire` (`POST /v1/record/questionnaire`) and its `SubmitStrategyQuestionnaireOptions` type. The endpoint has been retired; the strategy risk-disclosure record is no longer submitted through the OpenAPI SDK. Removed across Rust (incl. blocking), C, C++, Java, Node.js, and Python bindings
- **All SDKs:** `WarrantStatus` gains an `Unknown` variant (first variant, matching `WarrantType`), and `QuoteContext.warrant_list` now maps an unrecognized status discriminant to it instead of failing. The same placeholder rows that carry an empty `expiry_date` also carry `status: 0`, which no `WarrantStatus` variant matched (`Suspend = 2` / `PrepareList = 3` / `Normal = 4`), so they failed the whole list with `parse field: state: No discriminant in enum WarrantStatus matches the value 0`. Adding a variant shifts the ordinal of the existing ones in the C/C++/Java/Node.js/Python bindings
- **All SDKs:** `WarrantInfo.expiry_date` (`QuoteContext.warrant_list`) is now optional. The server returns an empty `expiry_date` for some warrants, and the strict `[year][month][day]` parse turned that single bad row into a `parse field: expiry_date: the 'year' component could not be parsed` error that failed the **entire** list — observed live on `700.HK`, where one warrant out of 126 (`68463.HK`) discarded the other 125. An unparseable date now yields an absent value instead (Rust `Option<Date>`, Python `Optional[date]`, Node.js `NaiveDate | null`, Java nullable `LocalDate`, C `const lb_date_t*` NULL-when-absent, C++ `std::optional<Date>`)
Expand All @@ -23,8 +32,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **All languages:** add `AgentContext.public_agents` (`GET /v1/ai/agents`) — list all publicly available Agents on the platform (the Explore catalog). Unlike `agents`, it is not scoped to a Workspace and returns every published, publicly-shared Agent. Takes the same optional `page` / `limit` / `name` parameters and returns the existing `AgentsResponse`
- **All languages:** add optional `parent_message_id` parameter to the AI Agent `conversation` and `conversation_streamed` methods — pass the `message_id` from a previous response to attach a follow-up message after the specified one, keeping the message stream in order. Only valid together with `chat_uid`; must not be set for a new conversation
- **Rust:** `Signal.status` is now a `SignalStatus` enum (pending / active / deleted / ai-failed / filtered-by-manual / ai-submit-failed), `SignalsResponse.total` is `i32` to match the wire contract, and the `risk_level` / `display_control` fields were dropped — neither is part of the API contract nor served in production
- **Rust:** `SignalContext.security_facts` now returns a typed `SecurityFact` instead of raw JSON — fact id / type / direction, the securities it is about, the factors behind it (with their anomaly test and groups), the data sources, and the natural-language `nl_info`. Adds the `FactType` and `FactDirection` enums, and `FactNlInfo::summary_tags()` / `invest_anal_tags()` / `eli_explain_tags()` for the `{tag, value}` entries the API carries as JSON inside a string
- **Rust:** add `SignalContext` — strategy signals and the catalyst facts behind them. `signals` (`GET /v1/signals`) queries signals with symbol / strategy / catalyst / time-range filters and paging; `signal` (`GET /v1/signals/{signal_id}`) returns one signal including the full strategy analysis in `json_data`; `security_facts` (`GET /v1/facts/security_facts`) lists a security's fact (catalyst) events. Bindings for the other languages are not wired up yet
Expand Down Expand Up @@ -61,20 +68,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **All SDKs:** document that `SubmitMultiLegOrderLeg.ratio_quantity` must be a positive number. A leg's direction comes from the `strategy` plus the order `side`, not from the sign of the ratio; the server rejects a negative or zero ratio with `602001` (`value does not match regex pattern "^([1-9]\\d*(\\.\\d+)?)$"`). Doc comments only — no behaviour or type changes
- **All SDKs:** `FundamentalContext.executive` (`GET /v1/quote/company-professionals`) sent its security in a `symbol` query parameter, but the endpoint expects the plural `symbols` (it takes a comma-separated list). The server silently ignored the unknown parameter and answered with an empty group (`total: 0`, no `symbol`, a `forward_url` missing the security id), so the call appeared to succeed while returning nothing. Verified live against `700.HK` and `AAPL.US`
- **All SDKs:** every optional response field now tolerates an explicit JSON `null`. `#[serde(default)]` alone only covers a *missing* key, so a server that sent `null` for any of ~700 optional fields aborted the whole response with `deserialize response body error: invalid type: null`. All `#[serde(default)]` response fields across every module (quote, trade, fundamental, market, dca, alert, sharelist, portfolio, calendar, content, screener, agent) now map `null` to the field's default value. First observed live as `institution_rating_detail` `"target"/"evaluate": null` (symbols without analyst coverage) and `us_company_dividends` `"recent_dividends": null` (no trailing dividends). No field types changed, so the language bindings are unaffected

### Added

- **All languages:** `MultiLegStrategy` gains `CalendarCallSpread` (`7`) and `CalendarPutSpread` (`8`) — calendar (horizontal) spreads, accepted by `TradeContext.submit_multileg` and reported back on the `multi_leg` field of order queries and the order-changed push. Both are appended after `Strangle`, so the discriminants of the existing variants are unchanged. Added across Rust, C, C++, Java, Node.js, and Python. Documented in longbridge/developers#1251
- **Rust:** `Signal.status` is now a `SignalStatus` enum (pending / active / deleted / ai-failed / filtered-by-manual / ai-submit-failed), `SignalsResponse.total` is `i32` to match the wire contract, and the `risk_level` / `display_control` fields were dropped — neither is part of the API contract nor served in production
- **Rust:** `SignalContext.security_facts` now returns a typed `SecurityFact` instead of raw JSON — fact id / type / direction, the securities it is about, the factors behind it (with their anomaly test and groups), the data sources, and the natural-language `nl_info`. Adds the `FactType` and `FactDirection` enums, and `FactNlInfo::summary_tags()` / `invest_anal_tags()` / `eli_explain_tags()` for the `{tag, value}` entries the API carries as JSON inside a string
- **Rust:** add `SignalContext` — strategy signals and the catalyst facts behind them. `signals` (`GET /v1/signals`) queries signals with symbol / strategy / catalyst / time-range filters and paging; `signal` (`GET /v1/signals/{signal_id}`) returns one signal including the full strategy analysis in `json_data`; `security_facts` (`GET /v1/facts/security_facts`) lists a security's fact (catalyst) events. Bindings for the other languages are not wired up yet
- **All languages:** add `TradeContext.submit_multileg` (`POST /v1/trade/order/multileg`) — submit a multi-leg option combination order (vertical spreads, straddles, strangles, collars, etc.) whose legs are placed together as a single strategy order. Takes `side`, `order_type`, `submitted_quantity`, `strategy` (`MultiLegStrategy`), a list of legs (`symbol` + `ratio_quantity`), and optional `submitted_price` / `remark` / `client_request_id`; returns the existing `SubmitOrderResponse`
- **All languages:** order queries and the order push now expose multi-leg strategy information. `Order` (from `today_orders` / `history_orders`), `OrderDetail` (from `order_detail`), and the `PushOrderChanged` order-changed event gain an optional `multi_leg` field (`MultiLegInfo`) — present only for multi-leg option combination orders — carrying the `strategy`, `strategy_name`, `multileg_id`, `code`, and the combination `legs` (each with `symbol`, `side`, `position`, `ratio_quantity`, `strike_price`, `expire_date`, and `contract_direction`). Adds the `MultiLegStrategy`, `MultiLegPosition`, and `ContractDirection` enums
- **All languages:** add grid-trading support via a standalone `GridContext` — submit / replace / cancel / suspend / restart grid orders, list orders (paged and by IDs), fetch order detail and trigger history, submit the strategy risk-disclosure questionnaire, and query the security (symbol) info (`symbol_info` → `GridSymbolInfo`: name, last price, lot sizes, price-step rules, channel/authorization) needed to build a grid order. Available in the Rust, Python, Node.js, Java, and C/C++ bindings
- **All languages:** `Execution` gains a `side` field (`OrderSide`) — the buy/sell direction of the fill, now returned by the `today_executions`, `history_executions`, and `all_executions` responses

### Fixed

- **All languages:** the AI Agent streamed conversation no longer errors mid-run when the server sends an explicit `"outputs": null`. `WorkflowFinishedPayload.outputs`, `NodeToolUseFinishedPayload.outputs`, and `SubagentFinishedPayload.outputs` were annotated `#[serde(default)]`, which only covers a *missing* key, not an explicit `null` — so a `workflow_finished` / `node_tool_use_finished` / `subagent_finished` event carrying `null` outputs failed to deserialize and aborted the whole event stream (`invalid type: null, expected struct WorkflowOutputs`). These fields now map `null` to the type's default
- **All languages:** likewise, list-typed fields on the streamed AI Agent event payloads no longer error on an explicit `null` (`invalid type: null, expected a sequence`). `tip_chips` (on the node / subagent / agent-tool `*_started` payloads), `WorkflowFinishedPayload.process_data`, and `SubagentStartedPayload.tools` were `#[serde(default)]`, which does not accept an explicit `null`; they now deserialize `null` to an empty list
- **All languages:** clarified that `CompanyOverview.employees` is typed as a **string** (not an integer) across all SDK languages — the Longbridge API returns this field as a JSON string (e.g. `"10000"`). Doc comments have been updated to make this explicit and prevent downstream tools from incorrectly treating the value as an integer
Expand Down Expand Up @@ -507,3 +500,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# [1.0.32] 2024-08-28

- make Depth.price to optional type

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ resolver = "3"
members = ["rust", "python", "nodejs", "java", "c"]

[workspace.package]
version = "5.0.0"
version = "5.1.0"
edition = "2024"

[profile.release]
lto = true

[workspace.dependencies]
longbridge-geo = { path = "rust/crates/geo", version = "5.0.0" }
longbridge-wscli = { path = "rust/crates/wsclient", version = "5.0.0" }
longbridge-httpcli = { path = "rust/crates/httpclient", version = "5.0.0" }
longbridge-proto = { path = "rust/crates/proto", version = "5.0.0" }
longbridge-candlesticks = { path = "rust/crates/candlesticks", version = "5.0.0" }
longbridge-oauth = { path = "rust/crates/oauth", version = "5.0.0" }
longbridge = { path = "rust", version = "5.0.0" }
longbridge-geo = { path = "rust/crates/geo", version = "5.1.0" }
longbridge-wscli = { path = "rust/crates/wsclient", version = "5.1.0" }
longbridge-httpcli = { path = "rust/crates/httpclient", version = "5.1.0" }
longbridge-proto = { path = "rust/crates/proto", version = "5.1.0" }
longbridge-candlesticks = { path = "rust/crates/candlesticks", version = "5.1.0" }
longbridge-oauth = { path = "rust/crates/oauth", version = "5.1.0" }
longbridge = { path = "rust", version = "5.1.0" }

tokio = "1.47.1"
tokio-tungstenite = "0.27.0"
Expand Down
Loading