Skip to content

feat(fund): add fund channel tools (25 read + 3 order) - #161

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

hogan-yuan merged 10 commits into
mainfrom
feat/fund-channel

Conversation

@hogan-yuan

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

Copy link
Copy Markdown
Member

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

Rename the fund_orders `symbols` param to `counter_ids` (and its zh-CN /
zh-HK locale copy + tool description) and switch to the renamed
GetFundOrdersOptions::counter_ids builder, aligning the tool with the SDK
change that fund order requests take counter_id (not symbol).
Picks up the fund SDK robustness fixes (int64-as-number tolerance, C/Node/
Python naming) from openapi #598.
Follow-up to the fund channel re-review:
- Declare all 28 fund tools in data/scopes.json (and mirror the affected
  scope descriptions in the zh-CN / zh-HK locales): the 16 catalog / market
  data tools under General, the 6 position tools under account.read, the 3
  order-lookup tools + fund_validate_order under trade.read, and
  fund_submit_order / fund_cancel_order under trade.write. Previously the
  money-moving fund writes and all fund reads were absent from the consent
  taxonomy and would not be grouped in the scopes manifest.
- Add fund_submit_order / fund_cancel_order to the v2-allowlist hard-exclusion
  set so their /v2 exclusion is enforced independently of scope grouping (a
  regression flipping them to V2 would otherwise compile and pass).
- Bind the cash amount and unit count as separate fields in the
  fund_submit_order confirmation scope so changing either invalidates the
  code, instead of collapsing them into a single size.
The fund_submit_order confirmation code previously bound action, counter_id,
currency, amount, units and sell_all, but not dividend_option, fee or
trade_method. A preview issued with one dividend option / fee / trade method
could therefore be confirmed by a request carrying different ones. Bind those
three too so the confirmation code covers the full order shape (remark, a
free-text note, is intentionally left unbound).
Bump the openapi SDK to e0df4c1e8 (trade FundPosition.symbol -> counter_id).
The fund_positions tool now returns counter_id in fund_info[]; update the tool
description and the serialization tests accordingly.
The fund_positions tool now emits counter_id, but the declared output schema
(output::FundPosition, backing schema_for) still advertised symbol/"ISIN code".
Rename the schema field so the advertised contract matches the runtime output.
The SDK constructor now takes counter_id, not symbol; the comment was
factually wrong and misleading given the channel is counter_id-only.
- locales zh-CN / zh-HK: fund_positions description advertised the output
  field as `symbol`, but the tool now emits `counter_id` (English default
  and output::FundPosition were already updated). A localized client would
  otherwise tell the model the wrong field name. Mirror the EN counter_id
  / ISIN note.
- server instructions: list fund_submit_order / fund_cancel_order among the
  two-step gated writes for server-level discoverability.
- Cargo.toml: restore the TODO to un-pin the openapi git rev once the fund
  channel ships in a tagged release.
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 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
@hogan-yuan
hogan-yuan merged commit 9322ce2 into main Sep 30, 2026
1 check passed
@hogan-yuan
hogan-yuan deleted the feat/fund-channel branch September 30, 2026 09:14
@hogan-yuan hogan-yuan mentioned this pull request Oct 8, 2026
hogan-yuan added a commit that referenced this pull request Oct 8, 2026
Cut **v0.13.0**. Bumps `0.12.0` → `0.13.0` and pins the openapi
dependency to the published crates.io release.

## Included since v0.12.0
- **Fund channel tools** (#161): 28 mutual-fund tools (25 read +
`fund_validate_order` + 2 gated writes) — catalog & market data, user
positions, orders/trading. Funds are addressed by `counter_id`. Writes
(`fund_submit_order` / `fund_cancel_order`) are behind the two-step
dry-run confirmation; reads are in the v2 allowlist.
- **SDK 5.1 sync** (#157): ports the longbridge SDK 5.1 additions.

## Dependency
- `longbridge` switched from the temporary git rev (`e0df4c1e8`) to the
published crates.io release **`5.2.0`** (the fund channel shipped in
openapi v5.2.0). `cargo build` + `cargo clippy --all-targets` clean.
hogan-yuan added a commit to longbridge/longbridge-terminal that referenced this pull request Oct 8, 2026
Adds the `longbridge fund <sub>` command group — **28 subcommands**
across the
fund catalog / market data (hot, list, filters, detail, analysis, trend,
returns, performance, nav, holdings, stock-holdings), the user's
positions,
and orders/trading (orders, order, transactions, validate-order, and
submit-order / cancel-order with interactive confirmation + `--yes`).

Funds are addressed by **`counter_id`** (e.g. `UT/FD/HK0000384492`) —
request
and response — including the `orders` filter (`--counter-id`). Every
leaf
subcommand has a `--schema` provider (coverage invariant test passes).

Pins the openapi SDK to the fund branch (`rev = 47e7572eb`, = 5.1.0 +
the fund
module). **Swap to the release version once openapi#598 merges and
ships.**

## Testing
All **28/28** subcommands live-verified on staging
(`openapi-global.longbridge.xyz`), including the submit → cancel order
loop.
`cargo test` (incl. the schema-coverage invariant) + `clippy` clean.

## Related
SDK: longbridge/openapi#598 · MCP: longbridge/longbridge-mcp#161 · Docs:
longbridge/developers#1269
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