Repository navigation
feat(fund): add FundContext for the mutual-fund channel (all layers) - #598
Merged
Merged
Conversation
Add the `fund` module with `FundContext` (async) and `FundContextSync` (blocking) covering 28 mutual-fund OpenAPI endpoints: - catalog & market data: hot funds, fund list, filters, detail, analysis / analysis detail / trend, annual & quarterly returns, performance & comparison, latest & historical NAV, top-10 holdings, reverse stock holdings - user positions: overview, single position, performance, profits, NAV history, dividends - orders & trading: order list & detail, transactions, order validate / submit / cancel Identifiers are exposed as `symbol`; the OpenAPI gateway maps them to the backend `counter_id`. Response models use `#[serde(default)]` throughout. Propagation to C / C++ / Go / Java / Node.js / Python is pending.
Mirror the grid_context C layer: `c/src/fund_context/{mod,types,context}.rs`
exposing the 28 fund methods as `lb_fund_context_*` extern "C" functions with
`C*`/`C*Owned` type pairs and `C*Options` request structs. Header
`c/csrc/include/longbridge.h` regenerated by cbindgen.
serde_json::Value ("any") fields are exposed as JSON strings; unix-second
timestamps as raw i64.
Mirror the Rust-core fund module (28 methods) across the binding layers: - C: `lb_fund_context_*` FFI (c/src/fund_context) + option structs, header regenerated. Fixes a cbindgen name collision where the fund `CFundPosition`/`CGetFundPositionsOptions` clashed with the portfolio types and corrupted longbridge.h (renamed to `CFundPositionItem` / `CFundPositionsOptions`). - C++: `longbridge::fund::FundContext` wrapping the C layer; CMake now compiles fund_context.cpp into the shared lib. - Java: `com.longbridge.fund.FundContext` (JNI + gson classes). - Node.js: napi `FundContext` (position entry exposed as `FundHoldingPosition` to avoid the trade `FundPosition` clash). - Python: PyO3 `FundContext` + openapi.pyi stub. Server-defined "any" JSON fields are surfaced as raw JSON strings across all layers; unix-second timestamps as integers; numeric-string fields as strings. Known follow-up: the fund response structs are not yet emitted into the public C header (reached via the void* async-result pointer); the Go SDK (separate repo) is not yet done.
Add the 39 fund response view structs to the cbindgen `[export] include` list (with `lb_fund_*_t` renames), so C consumers can read the results of the `lb_fund_context_*` calls instead of only receiving an opaque `void*`. All target typedef names are collision-free with the existing header. C and C++ builds verified.
…dpoints The fund identifier (counter_id, e.g. UT/FD/HK0000384492) contains "/", so it cannot be a URL path segment. Move the 18 single-fund endpoints onto fixed sub-paths (e.g. /v1/fund/funds/detail, /v1/fund/funds/nav) that carry the id as a `counter_id` query parameter, and expose the identifier as `counter_id` (not `symbol`). The three batch-backed endpoints — latest NAV, daily performance and held-fund performance — send the id as a one-element JSON array in a `counter_ids` query parameter, matching their backend contract. Propagated across Rust core (async + blocking), C, C++, Java, Node.js and Python. The order-flow body keeps its `symbol` field (the gateway passes the counter_id value through unchanged).
The backend returns several nested fund objects as null for many funds: FundDetail.asset_allocation, FundTrend.contrast_performances, FundPositionDetail.detail_values and FundOrderDetail.order. They were typed non-optional, so those responses failed to deserialize — e.g. `trend` on any fund without benchmark contrast data errored with "invalid type: null, expected struct FundTrendContrast". Model all four as optional/nullable across every layer: Rust `Option` (+ serde default), C nullable pointer via `COption`, C++ `std::optional`, Node.js `T | null`, Python `Optional[T]`; Java is unchanged (its gson bridge maps null to null). Regenerated longbridge.h and index.d.ts; updated openapi.pyi.
Fund endpoints are counter_id-only (request and response). Rename the remaining request-side symbol fields to counter_id across every layer: - GetFundOrdersOptions: symbols -> counter_ids (query key symbol -> counter_id) - ValidateFundOrderOptions / SubmitFundOrderOptions: symbol -> counter_id Rust core + C, C++, Java, Node.js, Python (Go handled separately). Responses were already counter_id. Backend accepts counter_id on both the order body and the orders filter (verified). Regenerated longbridge.h and index.d.ts; updated openapi.pyi.
- Rust core: make int64_str accept both JSON numbers and quoted strings (untagged), so a numeric int64 value never fails the whole response; add a regression test. This fix crosses all six layers since the core decodes to i64 before FFI. - Node.js: rename the fund position wrapper to FundHoldingPosition (drop the js_name alias) so the generated index.d.ts no longer emits a `FundPosition` alias that collided (TS2300) with the trade FundPosition class; regenerated index.d.ts / index.js. - Python: rename the fund pyclass to FundHoldingPosition so it no longer collides with the trade FundPosition when registered into the same module; update mod.rs registration and the openapi.pyi stub. - C: add CFundContext and the 14 fund option structs to the cbindgen [export.rename] map so they emit as lb_*_t like every other channel instead of raw C… names; update the C++ fund layer to the lb_*_t names. - Rust: fix a broken rustdoc intra-doc link (FundContext::list_funds -> FundContext::funds).
This was referenced Sep 28, 2026
TradeContext.fund_positions returned the fund's ISIN in a field named `symbol`. The ISIN cannot be converted back to a fund `counter_id` (the gateway does no ISIN->counter_id conversion and counter_ids have several formats), so it could not be used with the fund channel. Expose the full `counter_id` instead (the ISIN is its last `/`-separated segment). Renamed across all six layers; the Rust core keeps a serde alias for the legacy `symbol` key during transition.
…name The fund_position_response unit test still asserted position.symbol, which no longer compiles under cargo test / clippy --all-targets. Assert counter_id; the fixture still sends the legacy `symbol` JSON key, so it also proves the serde alias deserializes the old key.
The Java JNI field-mapping macro (impl_java_class!) still listed `symbol`, failing the java clippy check after the trade FundPosition.symbol -> counter_id rename. Map `counter_id` (the macro camelCases it to the Java `counterId` field).
- fund_position_response test now sends a full counter_id (UT/FD/HK0000447943) matching the documented format, plus a second case proving the legacy `symbol` key still deserializes via the serde alias. - CHANGELOG: correct the CFundPositionItem note (it IS emitted as lb_fund_position_item_t), and note the request-side filter stays symbol-based while the response returns counter_id.
hogan-yuan
added a commit
to longbridge/openapi-go
that referenced
this pull request
Sep 30, 2026
## Summary Adds a pure-Go `fund` package with `FundContext` (**28 methods**) for the mutual-fund channel, mirroring the Rust core field-for-field. Method groups: catalog & market data, user fund positions, and orders/trading. ## Design notes - **`counter_id`-only, request and response** — funds are identified by `counter_id` (e.g. `UT/FD/HK0000384492`), the deliberate exception to the release-wide symbol migration. Single-fund endpoints pass `counter_id` as a query param (fixed sub-paths like `/v1/fund/funds/detail`); the order filter carries repeated `counter_id`. No `symbol` anywhere in the fund package. - **Batch endpoints** (`Performance`, `Nav`, `PositionPerformance`) send a one-element JSON array in a `counter_ids` query param (`withCounterIDs`). - **`jsontypes.Int64`** — a string-tolerant int64 type is applied to the 27 int64 wire fields; the backend may send them as a number or a quoted string (empty/null → 0). - **Nullable nested objects** (`FundDetail.AssetAllocation`, `FundTrend.ContrastPerformances`, `FundPositionDetail.DetailValues`, `FundOrderDetail.Order`) are pointers so `null` deserializes cleanly. - Server-defined "any" JSON fields → `json.RawMessage`. ## Verification `go build ./...`, `go vet ./fund/...`, and `gofmt -l fund/` all clean. ## Related Core + C/C++/Java/Node.js/Python: longbridge/openapi#598 · CLI: longbridge/longbridge-terminal#327 · MCP: longbridge/longbridge-mcp#161 · Docs: longbridge/developers#1269
Merged
hogan-yuan
added a commit
that referenced
this pull request
Sep 30, 2026
Release **v5.2.0**. Bumps workspace `5.1.0` → `5.2.0`. ### New since v5.1.0 - **Breaking:** `TradeContext.fund_positions` — the `FundPosition` identifier field is renamed `symbol` → `counter_id` across all layers (C `lb_fund_position_t.counter_id`, C++ `FundPosition::counter_id`, Java `getCounterId()`, Node.js `counterId`, Python `counter_id`). The value is now the full fund `counter_id` (e.g. `UT/FD/HK0000384492`); the ISIN is recoverable as its last `/`-separated segment. Rust keeps a `symbol` serde alias for transition; the request-side filter `GetFundPositionsOptions.symbols` is unchanged (#598) - **Added:** mutual-fund channel `FundContext` / `FundContextSync` — 28 endpoints across fund catalog & market data, the user's fund positions, and fund orders/trading. Funds are addressed by `counter_id` (sent as a query parameter; the `nav` / `performance` / `position_performance` batch endpoints use a one-element `counter_ids` JSON array). Mirrored across C, C++, Java, Node.js and Python (the Go SDK ships separately, openapi-go#124) (#598) - **Changed:** `CalendarContext.finance_calendar` now exposes pagination — `count` / `offset` / `next` (with the new `CalendarPageDirection` enum) (#597) - **Fixed:** C/C++ — resolved a cbindgen name collision from the fund FFI: `CFundPosition` / `CGetFundPositionsOptions` were renamed to `CFundPositionItem` (`lb_fund_position_item_t`) / `CFundPositionsOptions` (`lb_fund_positions_options_t`) so they no longer overwrite the portfolio types (#598) > #598 is technically breaking; released as minor per maintainer decision.
hogan-yuan
added a commit
to longbridge/longbridge-mcp
that referenced
this pull request
Sep 30, 2026
Adds the fund (mutual-fund) channel to the MCP server — **28 `fund_*` tools** wrapping the openapi fund SDK: - **25 read tools** — catalog / market data, the user's positions, and orders / order / transactions. The positions-overview tool is `fund_position_overview` to avoid colliding with the trade `fund_positions`. - **3 order tools** — `fund_validate_order` (pre-trade check, places nothing), and `fund_submit_order` / `fund_cancel_order` (writes, gated by the two-step dry-run + `confirmation_code` flow; the confirmation binds every order-shaping field). Funds are addressed by **`counter_id`** (`fund_orders` filters by `counter_ids`). Reads + `fund_validate_order` are exposed on `/mcp` and the read-only `/v2`; the two writes are `/mcp`-only and excluded from `/v2`. zh-CN / zh-HK locales added for all 28 tools, and all 28 are declared in the OAuth consent scope taxonomy (`data/scopes.json`). Pins the openapi SDK to the fund branch (`rev = 47e7572eb`). **Swap to the release version once openapi#598 merges and ships.** ## Testing All read tools + the write dry-run/confirm flow verified live end-to-end against staging (local `--canary` server + OAuth); the submit → cancel path is verified via the identical fund SDK in the CLI. `cargo test` (315 tests: classification / locale-coverage / gated-tool / v2-allowlist invariants) + `clippy` clean. ## Related SDK: longbridge/openapi#598 · CLI: longbridge/longbridge-terminal#327 · Docs: longbridge/developers#1269
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.
Summary
New
FundContextfor the Hong Kong mutual-fund channel — 28 endpoints(catalog & market data, user positions, orders & trading) — across all six SDK
layers: Rust core (async + blocking), C, C++, Java, Node.js, Python.
Design notes
counter_id-only, request and response (e.g.UT/FD/HK0000384492); nosymbolfield. It contains/, so it's passed as acounter_idquery param(fixed sub-paths like
/v1/fund/funds/detail), never a path segment. Batchendpoints (
nav,performance,position_performance) use a one-elementcounter_idsJSON array.asset_allocation,contrast_performances,detail_values,order) are optional/nullable in every layer; int64fields accept a JSON number or a quoted string.
FundHoldingPositiontoavoid a clash with the trade channel's
FundPosition; C uses thelb_*_tnaming convention like every other channel.
Related
Go: longbridge/openapi-go#124 · CLI: longbridge/longbridge-terminal#327 · MCP: longbridge/longbridge-mcp#161 · Docs (CLI only; excluded from SDK/API reference by the counter_id policy): longbridge/developers#1269