Skip to content

feat(tools): route US accounts' asset tools to US endpoints - #173

Merged
hogan-yuan merged 21 commits into
mainfrom
feat/us-asset-routing
Oct 26, 2026
Merged

hogan-yuan merged 21 commits into
mainfrom
feat/us-asset-routing

Conversation

@hogan-yuan

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

Copy link
Copy Markdown
Member

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

The AP endpoints behind account_balance, stock_positions, cash_flow,
exchange_rate, fund_positions and margin_ratio are not deployed in the US
data center and fail with `code 12 internal server error` for US accounts.

- stock_positions: US accounts get the US asset overview (stock_list
  symbols normalized) directly instead of calling the AP endpoint first,
  which made the tool always fail for them.
- account_balance: US accounts use us_cash_and_buy_power
  ({buy_limit_info, cash_list[], margin_status}).
- cash_flow: US accounts use the paged US transaction history, walking
  pages of 100 until a short page (at most 20 pages); the response flags
  `truncated` with a note when the cap is hit.
- exchange_rate: US accounts use us_exchange_rates; bid/offer are dropped
  when they equal average_rate, as on the AP path, and kept otherwise.
- fund_positions, margin_ratio: added to AP_ONLY_TOOLS, so US accounts no
  longer see or call them.

AP behaviour is unchanged. Tool descriptions, zh-CN/zh-HK locales and the
stock_positions output schema describe the US shapes.
- stock_positions: US crypto_list entries now carry quantity, name and
  prices (the SDK used to drop them), and their bare pair code is
  qualified with its market (BTCUSD -> BTCUSD.BKKT) so symbol is
  fully qualified as in every other tool. The output schema and the
  tool descriptions list the crypto fields.
- exchange_rate: describe universal_key as the account channel
  (e.g. us_lb_apex) and spec_key as the rate type (e.g. am, reference),
  as US prod returns them, and use those keys in the tests.
The US-region server instructions said tools with a US variant switch
"for .US symbols", but account_balance, stock_positions, cash_flow,
exchange_rate and order_detail take no symbol and return the US shape for
every US account. Name those tools in the note, derived from the tool
descriptions that say "US accounts get a US-specific variant", and keep
the .US-symbol wording for the symbol-scoped variants.
…t tools

Both tools route every US account to the US order endpoint, which
returns {orders[]{id, ...}, total_count} instead of the AP shape, but
their descriptions only mentioned the US endpoint next to the us_*
params. Describe the US shape with the "US accounts get a US-specific
variant" marker so the generated US region note names them too.
…nd name AP-only alternatives

- stock_positions: always emit the AP-required stock_info keys for US
  holdings (as "" when blank) so structured output matches the schema.
- order_detail: attached_orders is omitted when empty, so it is no longer
  marked required in the output schema.
- Validate every US golden (and the AP stock_positions/order_detail
  goldens) against the tool's output_schema.
- today_orders: a US account's "today" starts at New York midnight
  (DST-aware via time-tz, already in the tree through the SDK).
- history_orders: "excludes today" applies to AP accounts only.
- AP-only tools rejected for US accounts name the US alternative, or say
  there is none and not to retry.
- Drop the interim counter_id note from order descriptions; clarify the
  US-only cash_flow cap and Unknown direction.
…n AP-only alternatives

- today_orders (US): the window now runs from 20:00 New York time on the
  previous calendar day (overnight session open) through 23:59:59 New York
  time today, DST-aware, so overnight orders no longer vanish at New York
  midnight while agents are told to check today_orders before resending.
- history_orders (US): bare yyyy-mm-dd start_at/end_at are read as New York
  days so the range stitches with today_orders.
- margin_ratio/operating rejections for US accounts now say there is no
  equivalent and name the partial account-level / key-metrics data.
- Deliver the US alternative as the envelope hint instead of a data field
  that tool_error never emitted; test the delivered envelope.
- Add a negative test for the output-schema validator.
…cutions its own range rules

- Correct the history_orders comment: today_orders' US window opens at
  20:00 ET the previous day, so it overlaps history_orders for yesterday's
  evening orders. Say so (and to dedupe by order_id) in the today_orders
  and history_orders descriptions (en/zh-CN/zh-HK).
- Give history_executions its own HistoryExecutionsParam without
  us_page/us_limit, and read its bare dates in New York time for US
  accounts, matching history_orders.
- Add history_range() to pick the date parser by region.
- Route US history_executions to v1 /trade/execution/history; v3
  /trade/execution/all fails in the US data center (code 12).
…ide unchanged

- AP today_executions/history_executions render side exactly as before
  0258250: "" for a today fill whose order is not in today's orders,
  "Unknown" for an unknown history side.
- US fills use the same unresolved values instead of null. Sides missing
  after the range order query are looked up via /v1/us/orders/{id}
  (at most 20, 5 at a time); hitting the range query's page cap logs at
  warn.
- A failed order query or detail lookup no longer fails the tool: it
  warns and leaves the side unresolved, still returning the fills.
- Descriptions (en/zh-CN/zh-HK) point to order_detail(order_id) for an
  unresolved US side.
- When the unfiltered US order range query stops at the page cap, run one
  more range query filtered by the deduped symbols of the fills still
  unmatched (at most 50) before falling back to order detail lookups. The
  follow-up is sent directly to /v1/us/orders/query, since the SDK takes a
  single symbol; its failure is non-fatal.
- today_executions with order_id on US skips the range query and resolves
  that order's side from its order detail.
- Share one US order detail request between order_detail and side lookups.
- Descriptions: an unresolved side means the order "could not be resolved".
- Test the page-cap path, the symbol-filtered follow-up and its bound, and
  the by-order shortcut.
hogan-yuan added a commit to longbridge/openapi that referenced this pull request Oct 23, 2026
## 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

- Go SDK: longbridge/openapi-go#135
- Docs: longbridge/developers#1297
- Consumers (draft until this is released):
longbridge/longbridge-terminal#337, longbridge/longbridge-mcp#173

## 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](https://claude.com/claude-code)
@hogan-yuan
hogan-yuan marked this pull request as ready for review October 23, 2026 08:54
@hogan-yuan
hogan-yuan merged commit b7786a5 into main Oct 26, 2026
1 check passed
@hogan-yuan
hogan-yuan deleted the feat/us-asset-routing branch October 26, 2026 02:06
hogan-yuan added a commit that referenced this pull request Oct 26, 2026
Cut **v0.15.0**. Bumps `0.14.0` → `0.15.0` (minor: new tools and output
changes for Longbridge US accounts, listed below).

## Included since v0.14.0
- **Longbridge US accounts** (#173), built on longbridge SDK 5.3.0:
- `account_balance`, `stock_positions`, `cash_flow`, `exchange_rate`,
`today_orders`, `history_orders` and `order_detail` call the US asset
and order APIs for US accounts and return the same shape as for AP
accounts.
- `today_executions` / `history_executions` work on US accounts; `side`
is filled from the account's orders.
- AP-only tools (`fund_positions`, `margin_ratio`, ...) are hidden for
US accounts, and a direct call returns an error whose hint names the
alternative.
- **`fx_rate`** (#175): public ECB reference exchange rates via
Frankfurter (latest, a past date, or a daily series), separate from
Longbridge's account-conversion rates in `exchange_rate`.

## Behaviour changes for API clients
- On US accounts, the tools above now return the AP shape instead of the
raw US payloads; US-only fields stay alongside the AP keys.
- `cash_flow` on US accounts returns a signed `balance` plus
`direction`.
- `today_orders` on US accounts covers the overnight session (from 20:00
ET the previous day); `history_orders` / `history_executions` read
date-only bounds in New York time for US accounts.

Only `Cargo.toml`, `Cargo.lock` and `server.json` change.

🤖 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