Skip to content

feat(trade): add US asset APIs and make fund/margin calls AP-only - #613

Merged
hogan-yuan merged 17 commits into
mainfrom
feat/us-asset-apis
Oct 23, 2026
Merged

hogan-yuan merged 17 commits into
mainfrom
feat/us-asset-apis

Conversation

@hogan-yuan

@hogan-yuan hogan-yuan commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Why

LBUS (US data-center) accounts can't read balances, cash flow or exchange rates through the SDK: the generic /v1/asset/* and /v1/risk/margin-ratio endpoints are AP-only. On US they return code 12 internal server error, because the gateway reports unknown service PortfolioAsset (the backend isn't deployed in US). This was found in the US UAT (items 3.3 / 4.3 / 5.4).

What

New TradeContext methods for US accounts (dc_restrict(DcRegion::Us)):

Method Endpoint
us_cash_and_buy_power(currency) GET /v1/us/assets/cash-and-buy-power
us_available_holding(opts) GET /v1/us/assets/available-holding
us_transaction_history(opts) POST /v1/us/assets/transaction-history
us_exchange_rates() GET /v1/us/assets/exchange-rates
  • Response types follow the live lb-portfolio-liberty schemas (read via inner-gateway reflection). The exchange-rate response is a two-level map keyed by account channel (e.g. us_lb_apex) and rate type (am / reference). The SDK flattens it into exchanges[] and keeps both keys on each entry.
  • fund_positions and margin_ratio are now AP-only (dc_restrict(DcRegion::Ap)), so US accounts get a clear region error instead of code 12. US accounts have no fund holdings or margin-ratio data.
  • Bindings: Rust (async + blocking), Python (sync + async + openapi.pyi), Node.js (napi; index.d.ts regenerated with @napi-rs/cli 3.8.0), Java JNI layer.
    • Java classes and C/C++ are not included, following the precedent of the existing US methods (us_asset_overview etc.), which are not exposed there either.
  • CHANGELOG updated.

Gateway

All four routes are deployed to prod. transaction-history (559) and exchange-rates (558) are new. available-holding (440) now has symbol2CounterID, so callers pass AAPL.US. The routes are also bound to MCP scopes (account.read / trade.read) on the HK and US registries.

Testing

  • cargo test -p longbridge --lib: 76 passed. Clippy shows no new warnings. Python and Node built locally, and the new methods were checked by importing them.
  • Against US prod with an LBUS API key: all four endpoints return data. The real responses deserialize into the new types. This caught a bug, fixed in f8877d2ab: the gateway returns both symbol and counter_id, so the earlier counter_id alias caused a duplicate-field error.

Related

Updated after real-data review

Checked against masked US prod responses (overview, cash-and-buy-power, available-holding, transaction-history, exchange-rates). The new types match. One gap fixed in cab7903: USCryptoEntry (used by us_asset_overview) kept only 5 of the fields prod returns for a crypto holding, so it had no quantity. It now has the same fields as USStockEntry (quantity, market, name, prices, today_pl, position_side, ...) in Rust, Python and Node.js. The symbol doc now says it is the bare pair code (BTCUSD), and the user-facing symbol is <symbol>.<market>. Also added a test using the prod response shapes (current_page as a number, us_lb_apex/am/reference keys, numeric-string timestamps). cargo test -p longbridge --lib passes (77 tests). index.d.ts was regenerated with @napi-rs/cli@3.8.0.

🤖 Generated with Claude Code

US data-center accounts have no fund holdings or margin-ratio data, so
these calls now fail fast with a region error instead of an opaque
server error. Document the restriction in the Rust, C, C++ and Java
method docs.
Add four US-only TradeContext methods (async + blocking), all behind
dc_restrict(DcRegion::Us):

- us_cash_and_buy_power: GET /v1/us/assets/cash-and-buy-power
- us_available_holding: GET /v1/us/assets/available-holding
- us_transaction_history: POST /v1/us/assets/transaction-history
- us_exchange_rates: GET /v1/us/assets/exchange-rates

The exchange-rate response is a two-level map upstream; flatten it into
a sorted list that keeps both map keys on each entry.
- Python: sync and async methods, result classes, and openapi.pyi stubs
- Node.js: napi methods, result types and GetUSTransactionHistoryOptions;
  regenerate index.d.ts
- Java: JNI entry points returning JSON, like the existing US stubs
- Note the AP-only restriction on fund_positions and margin_ratio in the
  Python and Node.js docs
The gateway's counterID2Symbol transform adds symbol and keeps
counter_id, so the counter_id alias on USTransaction.symbol made serde
fail with a duplicate-field error on real responses. Also document
what the two exchange-rate map keys are (account channel and rate
type), confirmed against US prod responses.
# Conflicts:
#	CHANGELOG.md
GET /v1/us/assets/overview returns crypto_list entries with the same
fields as stock_list (quantity, market, name, prices, today_pl, ...),
but USCryptoEntry only kept five of them, so a crypto holding had no
quantity. Mirror the stock entry's fields in Rust, Python and Node.js,
correct the symbol doc (bare pair code such as BTCUSD; the user-facing
symbol is <symbol>.<market>), and add a test using the response shapes
returned by US prod.
GET /v1/trade/order returns HTTP 500 for US data-center accounts
because the order gateway panics on US orders (seen in the LBUS UAT
and reproduced on US prod). Fail fast with a region error instead and
point US callers at us_order_detail in every layer's docs.
hogan-yuan added a commit to longbridge/openapi-go that referenced this pull request Oct 22, 2026
GET /v1/trade/order returns HTTP 500 for US data-center accounts
because the order gateway panics on US orders. Fail fast with
RegionRestrictedError and point US callers at USOrderDetail, matching
longbridge/openapi#613.
…to AP

account_balance (/v1/asset/account), cash_flow (/v1/asset/cashflow),
stock_positions (/v1/asset/stock) and PortfolioContext::exchange_rate
(/v1/asset/exchange_rates) fail with gateway code 12 for US data-center
accounts. Guard them with dc_restrict(DcRegion::Ap) so US tokens get a
region error without a request, and point US callers at the us_*
equivalents in every layer's docs. Also state that fund_positions and
margin_ratio have no US equivalent, and add unit tests that all
AP-only trade/portfolio methods reject a us_ token.
Append a path-based hint to the HttpClientError::DcRegionRestricted
message so callers and LLM agents know what to call instead: the
us_* equivalent for AP-only asset/order APIs, "no US equivalent" for
fund positions and margin ratio, and a US-accounts-only note for US
APIs. The existing message stays as the prefix and the variant's
fields are unchanged.

Also add the AP-only and "US token required" notes to the blocking
trade/portfolio wrappers, and fix the AsyncPortfolioContext stub that
pointed at the sync TradeContext.
The region-error hint named Rust/Python methods, but the text is shared by Node.js, Java, C/C++ and MCP servers whose names differ. Name the alternative by HTTP endpoint instead, cover every dc_restrict call site in both directions, and state when there is no equivalent.
…ints at AP endpoints

GET /v3/trade/execution/all answers HTTP 500 code 12 for US accounts. Guard all_executions with dc_restrict(DcRegion::Ap) and hint at GET /v1/trade/execution/history. AP accounts calling US fundamentals now get pointed at the matching /v1/quote/ endpoints, and the exchange-rates hint uses the same wording as the other US hints.
@hogan-yuan
hogan-yuan merged commit 1943f66 into main Oct 23, 2026
56 checks passed
@hogan-yuan
hogan-yuan deleted the feat/us-asset-apis branch October 23, 2026 08:36
hogan-yuan added a commit to longbridge/openapi-go that referenced this pull request Oct 23, 2026
## Why

LBUS accounts can't read balances, cash flow or exchange rates: the
generic `/v1/asset/*` and `/v1/risk/margin-ratio` endpoints are AP-only
and return `code 12` on US. Ports longbridge/openapi#613.

## What

- New `TradeContext` methods: `USCashAndBuyPower`, `USAvailableHolding`,
`USTransactionHistory`, `USExchangeRates` (paths
`/v1/us/assets/cash-and-buy-power`, `/available-holding`,
`/transaction-history`, `/exchange-rates`). US region check as for the
other US methods.
- `FundPositions` and `MarginRatio` now check for the AP region. Note:
the existing region check treats OAuth sessions as AP, so it doesn't
stop OAuth US users. That behaviour is pre-existing and unchanged here.
- The exchange-rate map is flattened as in the Rust SDK. Transaction
timestamps (unix seconds as a number or string) are parsed into
`time.Time`.
- CHANGELOG updated.

## Testing

- `go test ./trade/...` passes. The only failure in `go test ./...` is
the pre-existing `config.Test_YamlConfig`, whose testdata file is
missing from the repo.
- The real US prod responses for all four endpoints parse correctly
through the SDK code path.

## Updated after real-data review

Checked against masked US prod responses. Fixed in ab672b7:
- `USCryptoEntry` now has the same fields as `USStockEntry` (`Quantity`,
`Market`, `Name`, prices, `TodayPL`, ...). Before, it had no `Quantity`.
The `Symbol` doc is corrected: it is the bare pair code, and the
user-facing symbol is `Symbol.Market`.
- `USTransaction.Symbol` no longer falls back to `counter_id`. Prod
always sends the user-facing `symbol` alongside `counter_id`, and the
fallback would expose an internal id. This matches the Rust SDK.
- Added `TestUSAssetProdShapes`. `go test ./trade/...` passes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@hogan-yuan hogan-yuan mentioned this pull request Oct 23, 2026
hogan-yuan added a commit that referenced this pull request Oct 23, 2026
Release **v5.3.0**. Bumps workspace `5.2.2` → `5.3.0`.

Minor bump: new US-account APIs, and calls that only work in the AP data
center now fail fast with a region error.

### New since v5.2.2 (#613)
- **Added:** US-account asset methods on `TradeContext` —
`us_cash_and_buy_power`, `us_available_holding`,
`us_transaction_history`, `us_exchange_rates` (Rust async + blocking,
Node.js, Python; Java JNI entry points). US data-center tokens only.
- **Changed:** `account_balance`, `cash_flow`, `stock_positions`,
`fund_positions`, `margin_ratio`, `order_detail`, `all_executions` and
`PortfolioContext.exchange_rate` are AP-only. A US data-center account
gets `HttpClientError::DcRegionRestricted` without a request, instead of
gateway code 12 / HTTP 500.
- **Changed:** the `DcRegionRestricted` message keeps its existing text
and appends `; <hint>` naming the alternative endpoint. Code that
matches the message by exact equality must be updated; prefix matching
keeps working.
- **Fixed:** `Execution.side` defaults to `Unknown` when the response
omits it (US `/v1/trade/execution/history`), so `history_executions` no
longer fails to deserialize.
- **Fixed:** `USCryptoEntry` carries the full set of fields returned by
the gateway (`quantity`, `market`, `name`, prices, ...).
- **Fixed:** `option_chain_info_by_date` calls `POST
/v1/quote/options/strikes`.

Verified on a Longbridge US production account before merge: all new US
methods return data, `history_executions` parses fills without `side`,
and every AP-only method returns the region error with its hint.

Only `Cargo.toml` (workspace version + internal dep reqs) and
`CHANGELOG.md` change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
hogan-yuan added a commit to longbridge/longbridge-terminal that referenced this pull request Oct 26, 2026
> **Draft:** depends on longbridge/openapi#613. Before merging, drop the
two `chore(deps): TEMP …` commits and bump `longbridge` to the released
version.

## Why

On LBUS accounts, `longbridge portfolio` and `assets` fail with `code 12
internal server error`. In the US UAT (4.3 / 5.4), the trace shows `GET
/v1/asset/account` → `unknown service PortfolioAsset`. The same happens
for `cash-flow`, `exchange-rate`, `fund-positions`, `margin-ratio` and
the matching `serve` methods. The generic asset endpoints are AP-only,
and only `positions` already branched to the US API.

## What (US accounts only; AP behaviour unchanged)

| Command | US account |
|---|---|
| `assets` | `us_cash_and_buy_power`: buying power, cash per currency,
margin status |
| `portfolio` | `us_asset_overview` + `us_cash_and_buy_power` |
| `cash-flow` | `us_transaction_history`, paging through all pages |
| `exchange-rate` | `us_exchange_rates` |
| `fund-positions`, `margin-ratio` | Clear "not available for US
accounts" error, no request sent |
| `serve` | New `trade.us_*` methods. AP-only methods return a clear
error naming the US equivalent |

Also:
- Fixes the empty Symbol column in the US `positions` table.
- Updates help text, `--schema` and READMEs.
- A separate commit bumps the SDK to the published 5.2.2 and handles the
new `OrderStatus::DelayedNotReported`.

Not included: the TUI portfolio for US accounts, and using
`us_available_holding` in the sell flow.

## Testing

- `cargo test`: all pass. 13 new tests cover routing, no AP call on US,
paging, JSON shape and serve errors.
- `cargo clippy`: the only new warnings are 5 `double_must_use` from
`async_trait`, the same as on every existing trait method.
- Not run against a live account.

## Updated after real-data review

Checked against masked US prod responses. Changes in 93af6fe, with the
SDK lock bumped to cab79039 in b56b028:
- `positions` / `portfolio`: crypto entries get a `full_symbol`
(`BTCUSD.BKKT`) built from the bare `symbol` plus `market`, the same way
stock entries already have one. The crypto table now shows Qty / Cost /
Last / Today P&L, since the SDK now returns them.
- `exchange-rate`: a US account gets one row per rate type (`am`,
`reference`) for the same pair. A `rate_type` column (`spec_key`) now
tells those rows apart, and `--schema` explains `universal_key` (account
channel, e.g. `us_lb_apex`) and `spec_key` (rate type).
- `cash-flow`: the transaction `symbol` is already user-facing, so the
`counter_id` conversion was removed.
- `cargo test` passes (915 unit tests, plus the integration tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
hogan-yuan added a commit to longbridge/longbridge-mcp that referenced this pull request Oct 26, 2026
> **Draft:** depends on longbridge/openapi#613. Before merging, drop the
two `chore(deps): TEMP …` commits and bump `longbridge` to the released
version.

## Why

For LBUS accounts, `stock_positions`, `account_balance`, `cash_flow` and
`exchange_rate` fail with `code 12`, because they call AP-only endpoints
(US UAT 5.2 / 5.4). `stock_positions` even called the AP endpoint before
the US overview supplement, so it never succeeded for US accounts.
`fund_positions` and `margin_ratio` have no US equivalent.

## What (US accounts only; AP behaviour unchanged)

| Tool | US account |
|---|---|
| `stock_positions` | `us_asset_overview` only (US fields at top level;
output schema covers both shapes) |
| `account_balance` | `us_cash_and_buy_power` |
| `cash_flow` | `us_transaction_history`, size 100, up to 20 pages,
`truncated` + note when capped |
| `exchange_rate` | `us_exchange_rates`. `bid`/`offer` are dropped when
equal to `average` |
| `fund_positions`, `margin_ratio` | Added to `AP_ONLY_TOOLS` (hidden
from and rejected for US accounts) |

Tool descriptions are updated in en / zh-CN / zh-HK.

The gateway routes are live in prod and bound to `account.read` /
`trade.read` on the HK and US scope registries.

## Testing

- `cargo +nightly fmt --check`, `cargo clippy --all-features
--all-targets` (0 warnings), `cargo test` (389 passed).
- Not yet run against a live US account through MCP.

## Updated after real-data review

Checked against masked US prod responses. Changes in 9052862, with the
SDK lock bumped to cab79039 in 592e1f6:
- `stock_positions` (US): `crypto_list` entries now include `quantity`,
`name` and prices. Their `symbol` is qualified with the market (`BTCUSD`
becomes `BTCUSD.BKKT`). The output schema and the descriptions (en /
zh-CN / zh-HK) list these fields.
- `exchange_rate` (US): the descriptions say `universal_key` is the
account channel (e.g. `us_lb_apex`) and `spec_key` is the rate type
(e.g. `am`, `reference`). The tests use those keys.
- `cargo +nightly fmt`, `cargo clippy --all-features --all-targets` (0
warnings), `cargo test` (391 passed).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
hogan-yuan added a commit to longbridge/skills that referenced this pull request Oct 26, 2026
…ount limits (#68)

## Why

Found in the LBUS user acceptance test:

- **5.4 "show my positions" failed for an LBUS account.** The base skill
tells the model to always run `portfolio` for "my portfolio". On LBUS
accounts the current CLI's `portfolio` calls AP-only endpoints and fails
with `internal server error` (code 12). `positions` works.
- **5.5 / 5.x: order confirmation is described wrong.**
`references/order.md` was a stale capture of the old CLI help ("prompts
for confirmation"). The CLI now previews by default and only acts when
the same command is re-run with `--execute <CODE>`. `--execute` appeared
nowhere in this repo.

## What

- `longbridge-portfolio/references/order.md`: refreshed the captured
`longbridge order --help` (CLI 0.30.0) and added the preview → confirm →
`--execute <CODE>` flow, with "never execute before the user confirms".
- `longbridge-portfolio/SKILL.md`:
  - The Auth section explains the dry-run default.
- New "Longbridge US accounts" section: use `positions` for holdings,
cash and buying power; fund positions, margin ratio and executions are
not available for US accounts; fall back to `positions` if `portfolio` /
`assets` / `cash-flow` fail on an older CLI.
- Two new error-handling rows for the AP-only region error and the code
12 case.
- `longbridge/SKILL.md`: one-line LBUS note next to the `portfolio`
example.

Follow-ups, not in this PR:
- Once longbridge/longbridge-terminal#337 ships (US support for
`portfolio`, `assets`, `cash-flow`, `exchange-rate`), the "older CLI"
fallback note can be narrowed to a version.
- Once longbridge/openapi#613 is released, add the new US SDK methods to
`references/*-sdk/trade-context.md`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
hogan-yuan added a commit to longbridge/developers that referenced this pull request Oct 26, 2026
…1297)

## Why

LBUS users follow the asset docs and get `code 12`: the generic asset
endpoints are AP-only, and the docs don't say so or point to the US
endpoints (US UAT 3.3).

## What

- New pages (en / zh-CN / zh-HK) under `trade/asset/`: Cash and Buying
Power, Available Holding, Transaction History, Exchange Rates (US). The
API reference (`openapi.yaml`) gets the 4 endpoints too. Examples are in
Python, Python async, Node.js, Rust and Go. Java is not included because
the Java SDK does not expose these methods yet.
- An "AP accounts only" notice on Account Assets, Stock Positions, Fund
Positions, Cash Flow, Margin Ratio and Exchange Rates, linking to the US
equivalents. Fund positions and margin ratio are marked not available
for US accounts.
- Getting Started: US accounts are pointed to `us_asset_overview` /
`us_cash_and_buy_power` for balances.

SDK: longbridge/openapi#613. The endpoints are already live on the prod
gateway.

## Testing

- `autocorrect --lint` passes. `openapi.yaml` parses. The Node.js and Go
examples type-check against the SDK branches.
- The site build was not run here (`bun run build:canary` should be run
before merge).

## Also in this PR

Doc fixes from the LBUS UAT replies:

- Order Details is marked AP-only and points US accounts to
`us_order_detail`. Today Orders and History Orders recommend
`us_query_orders` for US accounts (UAT 3.4).
- Paper trading: the MCP prerequisites and the general FAQ say LBUS has
no paper accounts, and tell users to test with the paper account's own
credentials (UAT 3.6).
- Error codes: added `403308` (scope not authorized), with the fix and
the no-securities-account/no-permission case. The MCP troubleshooting
section links to it (UAT 3.x / 5.5).
- Getting Started: new OAuth scopes section (`watchlist`,
`account.read`, `trade.read`, `trade.write`, and the production ids
`4/6/10/11` from longbridge-mcp; other ids are treated as opaque) and a
Sign Out section (delete the stored token file, or `longbridge auth
logout`) (UAT 2.3).
- CLI `order` / `grid`: replaced "prompts for confirmation" / `-y` /
`--dry-run` with the actual preview + `--execute <CODE>` flow (UAT 5.5).
- CLI `portfolio` / `assets` / `cash-flow` / `exchange-rate`: one-line
note that LBUS should use `positions` for now, with US support coming in
longbridge/longbridge-terminal#337 (UAT 4.3 / 5.4).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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