Skip to content

feat(fund): add FundContext for the mutual-fund channel - #124

Merged
hogan-yuan merged 7 commits into
mainfrom
feat/fund
Sep 30, 2026
Merged

hogan-yuan merged 7 commits into
mainfrom
feat/fund

Conversation

@hogan-yuan

@hogan-yuan hogan-yuan commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

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

hogan-yuan added a commit that referenced this pull request Sep 28, 2026
Cut **v0.28.0** — closes out the `[Unreleased]` CHANGELOG section as
`[v0.28.0] - 2026-09-28`.

## Included since v0.27.0
- **Grid trading** (`grid.GridContext`); `SubmitStrategyQuestionnaire`
endpoint removed
- **Removed all symbol↔counter_id conversions** (port longbridge/openapi
#562) — endpoints send/receive the user symbol directly; `counter`
package removed
- **Multi-leg option orders** — `TradeContext.SubmitMultiLeg` +
`MultiLegInfo` on orders/push (#575, #589, #590)
- **Option chain onto the HTTP endpoint** (#588, **breaking**) —
`StrikePriceInfo` → `OptionChainContract`; see gateway note below
- **FinanceCalendar pagination** (#597, **breaking**) —
`count`/`offset`/`next` + `CalendarPageDirection`
- **HistoryExecutions pages through all `has_more`** (#591)
- **SignalContext** — `Signals` / `Signal` / `SecurityFacts` (#577,
#578)
- **Type refinements** — `Execution.Side`, typed `AlertValueMap`, typed
`RankCategories`, `CorpActionLive.Status` string,
`IndustryPeersResponse.Top` optional, `WarrantStatusUnknown`, Greek doc
refresh (#586)
- Ratings temporarily disabled; `TopMovers.NextParams` string; portfolio
flows timestamp

## ⚠️ Gateway dependency (#588)
`QuoteContext.OptionChainInfoByDate` now calls `GET
/v1/gemini/option/option_chain_list`. That endpoint must be deployed on
the target gateway. Verified live for this release: **available on
staging, not yet on production** (returns `404 api not found` there
until deployed). Called out prominently at the top of the v0.28.0
CHANGELOG entry.

## Verification
Read-only endpoints exercised live against staging + production (report
with per-endpoint request/response params): every change verified on at
least one environment — see internal report. `go build ./...` and
package tests pass.

## Not included
`#123` (DelayedNotReported) and `#124` (fund) remain open for a later
release.

## After merge
Tag `v0.28.0` on the merge commit and push (Go modules resolve by tag).
Pure-Go HTTP bindings for the fund channel (28 methods), mirroring the grid
package: catalog & market data, user fund positions, and fund orders/trading.
Paths mirror the Rust core (`/v1/fund/*`, `/v1/asset/funds/*`); the fund
identifier is `symbol`. Server-defined "any" JSON fields are surfaced as
`json.RawMessage`, unix-second timestamps as `int64`, numeric-string fields as
`string`.
The fund backend returns many int64 fields as JSON strings (created_at,
last_update_time, recent_trading_day, id, …). Add a jsontypes.Int64 type whose
UnmarshalJSON accepts a number, a quoted numeric string, null, or empty string
(→0), and switch the affected response fields to it, so Nav/NavHistory/Orders/
Positions no longer fail to deserialize.

Also fix the three batch-backed endpoints — latest NAV, daily performance and
held-fund performance — to send the identifier as a one-element JSON array in a
counter_ids query parameter (via withCounterIDs) instead of the scalar
counter_id, matching the backend contract.
Align with the Rust core making these optional: the backend returns
FundDetail.asset_allocation, FundTrend.contrast_performances,
FundPositionDetail.detail_values and FundOrderDetail.order as null for many
funds. Change them from value to pointer types (nil = null) so callers can
distinguish null from an empty object, matching the other SDK layers'
optional modeling. (Go already tolerated null by zero-valuing, so this is a
representation/parity change, not a crash fix.)
Fund endpoints are counter_id-only (request and response). Rename the
remaining request-side symbol fields: ValidateFundOrder/SubmitFundOrder body
Symbol -> CounterID (json counter_id), and GetFundOrders filter Symbols ->
CounterIDs (orders query key symbol -> counter_id, backend-verified).
The fund entry was stale text from the first commit and still described the
pre-refactor design: it listed `{symbol}` path segments, claimed the
identifier is passed as `symbol`, and said numeric-string fields are kept as
`string`. All three contradict the shipped code, which identifies funds by
`counter_id` (query param, fixed sub-paths), sends batch endpoints a
`counter_ids` JSON array, uses the string-tolerant `jsontypes.Int64` for the
27 int64 fields, and pointer-izes the four nullable nested objects. Rewrite the
block to match and call out that funds are the deliberate exception to the
release-wide symbol migration.
The fund position identifier was the ISIN in a field named Symbol, which can't
be used with the fund channel (no ISIN->counter_id conversion). Expose the full
CounterID instead (ISIN is its last /-separated segment). The wire type reads
counter_id and still accepts the legacy symbol key during transition.
hogan-yuan added a commit to longbridge/openapi that referenced this pull request Sep 30, 2026
…598)

## Summary
New `FundContext` for 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`); no
`symbol` field. It contains `/`, so it's passed as a `counter_id` query
param
(fixed sub-paths like `/v1/fund/funds/detail`), never a path segment.
Batch
endpoints (`nav`, `performance`, `position_performance`) use a
one-element
  `counter_ids` JSON array.
- **Nullable nested objects** (`asset_allocation`,
`contrast_performances`,
`detail_values`, `order`) are optional/nullable in every layer;
**int64**
  fields accept a JSON number or a quoted string.
- Node.js / Python expose the position type as **`FundHoldingPosition`**
to
avoid a clash with the trade channel's `FundPosition`; C uses the
`lb_*_t`
  naming 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
@hogan-yuan
hogan-yuan merged commit 6a3dce7 into main Sep 30, 2026
@hogan-yuan
hogan-yuan deleted the feat/fund branch September 30, 2026 08:23
hogan-yuan added a commit that referenced this pull request Sep 30, 2026
Cut **v0.29.0** — closes out the `[Unreleased]` CHANGELOG section as
`[v0.29.0] - 2026-09-30`.

## Included since v0.28.0
- **Added — Mutual-fund channel** (`fund.FundContext`, #124): 28 methods
mirroring the Rust core — catalog & market data, user positions, and
orders/trading. Funds are identified by `counter_id` (contains `/`, sent
as a query parameter; `Performance` / `Nav` / `PositionPerformance` use
a one-element `counter_ids` JSON array), the deliberate exception to the
release-wide symbol migration.
- **Breaking — `trade.FundPosition`** (#124): the fund identifier field
is renamed `Symbol` → `CounterID`, matching the Rust core and the fund
channel. It now holds the full fund `counter_id` (e.g.
`UT/FD/HK0000384492`); the ISIN is its last `/`-separated segment. The
wire type reads the new `counter_id` key and still accepts the legacy
`symbol` key during the transition.

## Changelog housekeeping
The `fund.FundContext` **Added** block was moved out of `[v0.28.0]` into
`[v0.29.0]`: the fund package shipped in #124 **after** the v0.28.0 tag,
so the v0.28.0 tag never contained it. `grid` / `signal` / multi-leg
stay in `[v0.28.0]` (they were in that tag).

> #124 is technically breaking (`FundPosition` field rename); released
as minor per maintainer decision, consistent with v0.28.0.
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