diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ddb482ce..436360a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,20 @@ 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 diff --git a/c/csrc/include/longbridge.h b/c/csrc/include/longbridge.h index bea760dd1..94866ae6e 100644 --- a/c/csrc/include/longbridge.h +++ b/c/csrc/include/longbridge.h @@ -1343,6 +1343,14 @@ typedef enum CMultiLegStrategy { * Strangle */ MultiLegStrategyStrangle, + /** + * Calendar call spread + */ + MultiLegStrategyCalendarCallSpread, + /** + * Calendar put spread + */ + MultiLegStrategyCalendarPutSpread, } CMultiLegStrategy; /** diff --git a/c/src/trade_context/enum_types.rs b/c/src/trade_context/enum_types.rs index dc7260af3..8ab96821a 100644 --- a/c/src/trade_context/enum_types.rs +++ b/c/src/trade_context/enum_types.rs @@ -353,6 +353,12 @@ pub enum CMultiLegStrategy { /// Strangle #[c(remote = "Strangle")] MultiLegStrategyStrangle, + /// Calendar call spread + #[c(remote = "CalendarCallSpread")] + MultiLegStrategyCalendarCallSpread, + /// Calendar put spread + #[c(remote = "CalendarPutSpread")] + MultiLegStrategyCalendarPutSpread, } /// Multi-leg position direction diff --git a/cpp/include/types.hpp b/cpp/include/types.hpp index 187d88c93..9fb71b237 100644 --- a/cpp/include/types.hpp +++ b/cpp/include/types.hpp @@ -1697,6 +1697,10 @@ enum class MultiLegStrategy Straddle, /// Strangle Strangle, + /// Calendar call spread + CalendarCallSpread, + /// Calendar put spread + CalendarPutSpread, }; /// Multi-leg position direction diff --git a/cpp/src/convert.hpp b/cpp/src/convert.hpp index fa9ccaa59..f87c84087 100644 --- a/cpp/src/convert.hpp +++ b/cpp/src/convert.hpp @@ -1532,6 +1532,10 @@ convert(CMultiLegStrategy strategy) return MultiLegStrategy::Straddle; case MultiLegStrategyStrangle: return MultiLegStrategy::Strangle; + case MultiLegStrategyCalendarCallSpread: + return MultiLegStrategy::CalendarCallSpread; + case MultiLegStrategyCalendarPutSpread: + return MultiLegStrategy::CalendarPutSpread; default: return MultiLegStrategy::Unknown; } @@ -1557,6 +1561,10 @@ convert(MultiLegStrategy strategy) return MultiLegStrategyStraddle; case MultiLegStrategy::Strangle: return MultiLegStrategyStrangle; + case MultiLegStrategy::CalendarCallSpread: + return MultiLegStrategyCalendarCallSpread; + case MultiLegStrategy::CalendarPutSpread: + return MultiLegStrategyCalendarPutSpread; default: return MultiLegStrategyUnknown; } diff --git a/java/javasrc/src/main/java/com/longbridge/trade/MultiLegStrategy.java b/java/javasrc/src/main/java/com/longbridge/trade/MultiLegStrategy.java index 7e58e4b8b..7997455e5 100644 --- a/java/javasrc/src/main/java/com/longbridge/trade/MultiLegStrategy.java +++ b/java/javasrc/src/main/java/com/longbridge/trade/MultiLegStrategy.java @@ -20,4 +20,8 @@ public enum MultiLegStrategy { Straddle, /** Strangle */ Strangle, + /** Calendar call spread */ + CalendarCallSpread, + /** Calendar put spread */ + CalendarPutSpread, } diff --git a/java/src/types/enum_types.rs b/java/src/types/enum_types.rs index 7e0ac7a88..0b3bb2443 100644 --- a/java/src/types/enum_types.rs +++ b/java/src/types/enum_types.rs @@ -482,7 +482,9 @@ impl_java_enum!( VerticalPutSpread, Collar, Straddle, - Strangle + Strangle, + CalendarCallSpread, + CalendarPutSpread ] ); diff --git a/nodejs/index.d.ts b/nodejs/index.d.ts index b9e05844f..9b1815da5 100644 --- a/nodejs/index.d.ts +++ b/nodejs/index.d.ts @@ -6026,7 +6026,11 @@ export declare const enum MultiLegStrategy { /** Straddle */ Straddle = 6, /** Strangle */ - Strangle = 7 + Strangle = 7, + /** Calendar call spread */ + CalendarCallSpread = 8, + /** Calendar put spread */ + CalendarPutSpread = 9 } /** Options for listing topics created by the current authenticated user */ diff --git a/nodejs/src/trade/types.rs b/nodejs/src/trade/types.rs index b251bc9c7..ccaf76ac8 100644 --- a/nodejs/src/trade/types.rs +++ b/nodejs/src/trade/types.rs @@ -291,6 +291,10 @@ pub enum MultiLegStrategy { Straddle, /// Strangle Strangle, + /// Calendar call spread + CalendarCallSpread, + /// Calendar put spread + CalendarPutSpread, } /// Multi-leg position direction diff --git a/python/pysrc/longbridge/openapi.pyi b/python/pysrc/longbridge/openapi.pyi index 2897e3e73..94f5bf151 100644 --- a/python/pysrc/longbridge/openapi.pyi +++ b/python/pysrc/longbridge/openapi.pyi @@ -6358,6 +6358,16 @@ class MultiLegStrategy: Strangle """ + class CalendarCallSpread(MultiLegStrategy): + """ + Calendar call spread + """ + + class CalendarPutSpread(MultiLegStrategy): + """ + Calendar put spread + """ + class MultiLegPosition: """ Multi-leg position direction diff --git a/python/src/trade/types.rs b/python/src/trade/types.rs index 0baefd345..0cdd0ef39 100644 --- a/python/src/trade/types.rs +++ b/python/src/trade/types.rs @@ -417,6 +417,10 @@ pub(crate) enum MultiLegStrategy { Straddle, /// Strangle Strangle, + /// Calendar call spread + CalendarCallSpread, + /// Calendar put spread + CalendarPutSpread, } /// Multi-leg position direction diff --git a/rust/src/trade/types.rs b/rust/src/trade/types.rs index 46f079c17..fbd7b0ce5 100644 --- a/rust/src/trade/types.rs +++ b/rust/src/trade/types.rs @@ -341,6 +341,12 @@ pub enum MultiLegStrategy { /// Strangle #[strum(to_string = "Strangle", serialize = "6")] Strangle, + /// Calendar call spread + #[strum(to_string = "CalendarCallSpread", serialize = "7")] + CalendarCallSpread, + /// Calendar put spread + #[strum(to_string = "CalendarPutSpread", serialize = "8")] + CalendarPutSpread, } /// Multi-leg position direction