Skip to content

feat: sync upstream longbridge/openapi through v5.2.0 + ForexContext (#602) - #11

Open
hogan-yuan wants to merge 24 commits into
longportapp:mainfrom
hogan-yuan:feat/sync-upstream-v5.2-and-forex
Open

hogan-yuan wants to merge 24 commits into
longportapp:mainfrom
hogan-yuan:feat/sync-upstream-v5.2-and-forex

Conversation

@hogan-yuan

Copy link
Copy Markdown

Continues PR #7 to bring this repo to content parity with upstream longbridge/openapi up to the forex head — brand-renamed longbridge → longport, URLs → longportapp.com; proto namespace, workspace version (4.3.7), package coords and CN URLs untouched.

Stacked on #7. This branch is built on top of #7 (feat/sync-longbridge-and-remove-counter-id), which is not yet in main, so this PR's diff currently also contains #7's commits. Merge #7 first, then this PR's diff reduces to just the ~18 commits below. The commits specific to this PR are everything from feat(forex): add ForexContext … onward.

New context modules (all layers: Rust core async+blocking, C, C++, Java, Node.js, Python)

  • ForexContext (#602) — currency-exchange channel: quote / submit_order / order
  • FundContext (#598) — mutual-fund channel, 28 endpoints
  • GridContext (#570 + #585) — grid trading; grid push on the trade channel (PushGridOrderChanged)
  • SignalContext (#577 + #578) — Rust core only (matches upstream; other bindings not wired up upstream)

Cross-cutting features

  • Multi-leg option orders (#575) + calendar spreads (#590): TradeContext.submit_multileg, Order/OrderDetail/PushOrderChanged.multi_leg
  • Option chain → OpenAPI endpoint (#588, breaking): StrikePriceInfo → OptionChainContract, new OptionExpiryCycleType / OptionStandardAttr, standard_only parameter
  • finance_calendar pagination (#597), history_executions full paging (#591), Execution.side

Fixes / chores

  • C null-pointer tolerance (#579), C++ AssetContext build+repair (#580), Java JNI signature repair (#587), Java javadoc (#582), option Greek docs (#586), CompanyOverview.employees doc (#574), nightly rustfmt reflow (#584), CI action bumps (#600)

Verification

  • cargo check --workspace --all-features
  • cargo clippy --all --all-features (no errors)
  • cargo +nightly fmt --all (clean)
  • cargo build -p longport-c (regenerated longport.h)
  • cmake C++ full build (liblongport_cpp + examples)
  • javac over all Java sources + JNI header regen
  • npm run build:debug (regenerated index.d.ts / index.js)
  • openapi.pyi hand-synced + parse-checked

Deliberately omitted

🤖 Generated with Claude Code

Brings over everything the upstream repo shipped in 4.4.0 through 4.5.0
(brand-renamed longbridge -> longport):

- US market APIs (14 interfaces, us_ token + x-dc-region routing)
- AI Agent module (AgentContext: workspaces/agents/public_agents,
  blocking + SSE-streamed conversations, typed events, references,
  interrupts/interactions)
- Paper trading mode, attached orders (TP/SL), client_request_id,
  all_executions (disabled pending API), OutsideRTH.OptionPreMarket
- OrderTag GTC -> Gtc; charge_detail now optional; musl target removed
- HTTP error responses preserve status/trace-id/headers/body

The 4.3.6/4.3.7 stability fixes previously released here (shared
reqwest::Client, reconnect-loop shutdown, Java JNI hardening) are part
of the same upstream range and remain unchanged.
…api #562)

- Request params counter_id/counter_ids/underlying_counter_id -> symbol/symbols,
  sending the user-facing symbol as-is; comparison_counter_ids ->
  comparison_symbols; us_query_orders body counter_ids -> symbols
- Responses read the server's symbol field directly; US assets stock entries
  read full_symbol, crypto entries read symbol; removed utils/counter.rs and
  the embedded US-ETF/IX/WT directories
- DELETE requests send parameters via query string with no body (alert ids,
  sharelist symbols); alert/sharelist write methods now return ()/void
- AlertItem.value_map typed (AlertValueMap); CorpActionLive.status normalized
  to string; RankCategoriesResponse typed; next_params/executed_timestamp
  typed; industry_rank/industry_peers expose symbol
- Every optional response field now tolerates an explicit JSON null (~700
  fields); AI Agent streamed events tolerate null outputs/lists
- Java: AlertContext.update wired up; six FundamentalContext JNI bindings
  implemented
- fundamental ratings endpoint (GET /v1/quote/ratings) not yet open upstream
The longport-local Makefile.toml adds a create-cmake-build-dir dependency to
every cmake task (this repo's CI runs 'cargo make cmake' on a fresh checkout
where cmake.build does not exist). The 4.4.0-4.5.0 sync clobbered it with the
upstream Makefile.toml; restore the local version.
…ccepts comparison_symbols

The gateway does not yet accept the new comparison_symbols parameter for
GET /v1/quote/compare/valuation and answers 500 for any non-empty peer
list. As a stopgap, valuation_comparison converts the peer symbols to
counter-ids locally and keeps sending the legacy comparison_counter_ids
parameter. The public API is unchanged (still takes user symbols) and the
response already reads symbol directly. Marked with a TODO to drop the
local conversion once the gateway supports comparison_symbols.
…ow accepts it)

The gateway now supports the comparison_symbols parameter on
GET /v1/quote/compare/valuation as a standard HTTP array (repeated keys),
not a JSON array string. Drop the stopgap that converted peer symbols to
counter-ids and sent the legacy comparison_counter_ids parameter; pass the
user symbols straight through as Option<Vec<String>>, which the qs
serializer emits as repeated keys. Removes the symbol_to_counter_id shim.

Verified on staging. Note: the gateway fix is on staging first; production
still answers this format with code 13 until its rollout lands.
…layers)

Sync longbridge/openapi #602 (brand-renamed longbridge -> longport).

New ForexContext / ForexContextSync for the currency-exchange flow across
all SDK layers (Rust core async + blocking, C, C++, Java, Node.js, Python):

- quote        POST /v1/forex/quote  -> rate + quote_id
- submit_order POST /v1/forex/order  -> accepted only (returns nothing)
- order        GET  /v1/forex/order  -> poll by client_order_id to terminal state

Conversion is asynchronous: a successful submit_order only means the order was
accepted; callers poll order by client_order_id until Success/Failed. The order
state is the integer-coded enum ForexOrderStatus (Processing/Success/Failed,
unknown -> Processing). Money fields use Decimal; the query response's rate /
from_amount / to_amount are nullable (empty until filled). rate / expire_at
(Unix ms) / ccy_pair follow the standard currency-pair convention
(1 BASE = rate QUOTE), independent of the request from/to direction.

Generated artifacts regenerated (not hand-edited): c/csrc/include/longport.h,
nodejs/index.d.ts, nodejs/index.js, java JNI header. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (liblongport_cpp), javac over all
Java sources incl. com.longport.forex + JNI header regen.
Sync longbridge/openapi #579. std::vector::data() may return nullptr for
an empty vector (what the C++ binding passes for an omitted list arg); the
C layer fed it straight to std::slice::from_raw_parts, which is UB and
aborts under debug UB checks. Route the list-argument call sites across
quote_context, trade_context, agent_context, alert_context and types
through a null-tolerant slice_from_raw_parts helper.

The submit_multileg call site is deferred to the multi-leg sync (#575),
which introduces that code.
Sync longbridge/openapi #598 (brand-renamed longbridge -> longport).

New FundContext / FundContextSync for the mutual-fund channel (28 endpoints:
catalog & market data, user fund positions, and fund orders/trading) across
all SDK layers (Rust core async + blocking, C, C++, Java, Node.js, Python).

The fund identifier is counter_id (e.g. UT/FD/HK0000384492); since it contains
/, single-fund endpoints pass it as a counter_id query parameter and the three
batch endpoints (nav/performance/position_performance) as a one-element
counter_ids array. Nullable nested objects (FundDetail.asset_allocation,
FundTrend.contrast_performances, FundPositionDetail.detail_values,
FundOrderDetail.order) are modeled optional; int64 fields tolerate number or
quoted-string (serde_utils::int64_str).

Breaking: TradeContext.fund_positions FundPosition.symbol -> counter_id
(serde alias "symbol" for transition) across every layer.

The fund-channel position entry is exposed as FundHoldingPosition in
Node.js/Python to avoid colliding with the trade FundPosition. cbindgen
renames resolve the CFundPosition / CGetFundPositionsOptions name collision
(CFundPositionItem / CFundPositionsOptions).

Generated artifacts regenerated (not hand-edited): c/csrc/include/longport.h,
nodejs/index.d.ts, nodejs/index.js, java JNI header. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (liblongport_cpp), javac over all
Java sources (54 fund classes) + JNI header regen.
Sync longbridge/openapi #577 + #578 (Rust core only; other language bindings
are not wired up upstream yet).

New SignalContext / SignalContextSync:
- signals        GET /v1/signals               query with symbol/strategy/
                                               catalyst/time-range filters + paging
- signal         GET /v1/signals/{signal_id}   one signal incl. full json_data
- security_facts GET /v1/facts/security_facts  a security's catalyst fact events

security_facts returns a typed SecurityFact (fact id/type/direction, securities,
factors with anomaly test and groups, data sources, nl_info) with FactType /
FactDirection enums and FactNlInfo tag accessors. Signal.status is a typed
SignalStatus enum; SignalsResponse.total is i32; non-contract risk_level /
display_control dropped. Adds serde_utils::timestamp_ms.

Verified: cargo check -p longport --all-features, clippy clean.
Sync longbridge/openapi #584. Run `cargo +nightly fmt --all`; the only
changes are doc/line-comment rewraps to satisfy the current nightly
rustfmt, with no code changes. This keeps subsequent per-feature `fmt`
runs clean so feature diffs stay free of incidental reflow noise.
Sync longbridge/openapi #570 (re-land grid) + #585 (drop the retired
submit_strategy_questionnaire), brand-renamed longbridge -> longport, taken
at the final post-#585 state.

New standalone GridContext / GridContextSync across all SDK layers (Rust core
async + blocking, C, C++, Java, Node.js, Python):
- submit / replace / cancel / suspend / restart grid orders
- list (paged and by IDs), order detail, trigger history
- symbol_info -> GridSymbolInfo (name, last price, lot sizes, price-step
  rules, channel/authorization) to build a grid order

Grid master-order changes arrive on the trade channel as a new
PushGridOrderChanged event (TradeContext.set_on_grid_order_changed /
setOnGridOrderChange), added to the trade PushEvent enum and the per-binding
trade push handlers.

submit_strategy_questionnaire (POST /v1/record/questionnaire) is intentionally
omitted (retired upstream in #585), so it is never added here.

Generated artifacts regenerated (not hand-edited): c/csrc/include/longport.h,
nodejs/index.d.ts, nodejs/index.js, java JNI header. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (liblongport_cpp), javac over all
Java sources (22 grid classes) + JNI header regen.
Sync longbridge/openapi #575 (brand-renamed longbridge -> longport).

New TradeContext.submit_multileg (POST /v1/trade/order/multileg) across all
SDK layers (Rust core async + blocking, C, C++, Java, Node.js, Python) —
submits a multi-leg option combination order (vertical spreads, straddles,
strangles, collars, ...) whose legs are placed together as one strategy
order. Takes side / order_type / submitted_quantity / strategy
(MultiLegStrategy) / legs (symbol + ratio_quantity) and optional
submitted_price / remark / client_request_id; returns SubmitOrderResponse.

Order, OrderDetail and the PushOrderChanged event gain an optional multi_leg
field (MultiLegInfo: strategy, strategy_name, multileg_id, code, legs with
symbol/side/position/ratio_quantity/strike_price/expire_date/contract_direction).
Adds MultiLegStrategy, MultiLegPosition, ContractDirection enums and
serde_utils::date_ymd_opt.

Generated artifacts regenerated (not hand-edited): c/csrc/include/longport.h,
nodejs/index.d.ts, nodejs/index.js, java JNI header. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (liblongport_cpp), javac over all
Java sources + JNI header regen.
…c #591)

Sync longbridge/openapi #591. TradeContext::history_executions now pages
through all results internally via the page parameter (the endpoint caps each
response at 1000 records) until has_more is false, so callers get the complete
execution set instead of a silently-truncated first page. Applies to the
blocking wrapper and every binding that delegates to it.

Verified: cargo check -p longport --all-features.
Sync longbridge/openapi #590. MultiLegStrategy gains CalendarCallSpread (7)
and CalendarPutSpread (8) — calendar (horizontal) spreads, accepted by
TradeContext.submit_multileg and reported back on the multi_leg field of
order queries and the order-changed push. Both are appended after Strangle,
so the discriminants of the existing variants are unchanged. Added across
Rust, C, C++, Java, Node.js, and Python.

Generated artifacts regenerated: c/csrc/include/longport.h, nodejs/index.d.ts,
nodejs/index.js. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build, javac over all Java sources.
…597)

Sync longbridge/openapi #597 (brand-renamed longbridge -> longport).

CalendarContext.finance_calendar now exposes the endpoint pagination controls
across all SDK layers (Rust core + blocking, C, C++, Java, Node.js, Python):
three optional parameters count (max events per page), offset (events to skip),
and next (new CalendarPageDirection enum: Later / Earlier); the response
next_date cursor is returned as-is. Previously a single uncontrolled request
capped results at the server default (~10 events). Signature change across
every layer; new arguments optional where defaults are supported.

Generated artifacts regenerated: c/csrc/include/longport.h, nodejs/index.d.ts,
nodejs/index.js. openapi.pyi updated by hand (sync finance_calendar stub; note
longport.openapi.pyi has no AsyncCalendarContext stub to update).

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (calendar_context.cpp), javac over
all Java sources + JNI header regen.
…sync #586)

Sync longbridge/openapi #586. Doc comments only on SecurityCalcIndex
(calc_indexes) option Greeks (delta/gamma/theta/vega/rho). Notably theta is
now documented as a per-day value (server divides the raw annualized value by
365, so the returned theta is already daily). No field or behaviour changes.

Verified: cargo check -p longport --all-features, cargo build -p longport-c,
nodejs npm run build:debug.
…588)

Sync longbridge/openapi #588 (brand-renamed longbridge -> longport). Breaking.

QuoteContext.option_chain_info_by_date moves off quote socket business command
21 onto the plain HTTP endpoint GET /v1/gemini/option/option_chain_list, and
its return type changes shape across all SDK layers (Rust core + blocking, C,
C++, Java, Node.js, Python).

- The paired StrikePriceInfo (price + call_symbol + put_symbol + standard) is
  REMOVED in favour of a flat, one-entry-per-contract OptionChainContract
  (symbol, expiry_date, strike_price, direction, option_type, standard_attr,
  days_to_expiry). Calls and puts are no longer paired by strike; callers must
  filter on direction.
- New enums OptionExpiryCycleType (Unknown/Monthly/Weekly/Quarterly) and
  OptionStandardAttr (Unknown/Normal/Old), both falling back to Unknown.
- New standard_only parameter filters legacy (corporate-action) contracts
  server-side (positional bool in Rust/C/C++/Java, optional in Node.js,
  default False in Python); omitted from the query string when false.
- The 30-minute client-side chain cache is removed.
- option_chain_expiry_date_list is unchanged (still socket command 20).

Java StrikePriceInfo deleted; Makefile.toml doc list updated.

Generated artifacts regenerated (not hand-edited): c/csrc/include/longport.h,
nodejs/index.d.ts, nodejs/index.js, java JNI header. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build (liblongport_cpp), javac over all
Java sources + JNI header regen.
Sync longbridge/openapi #580 (brand-renamed longbridge -> longport).

C++ asset::AssetContext (statements / statement_download_url) is now actually
built and usable: cpp/src/asset_context.cpp was never listed in CMakeLists so
the class linked-failed, and it had never compiled (missing longport.h include;
statement_download_url read res->data as a nonexistent
lb_statement_download_url_response_t* instead of the bare const char* the C
layer delivers). Fixed the include and the callback and added the file to the
build.

C: export lb_statement_item_t from longport.h (CStatementItem is only reachable
via the void* async-result pointer, so cbindgen did not emit it) and add the
missing CAssetContext -> lb_asset_context_t rename entry.

Verified: cargo build -p longport-c, cmake C++ build (asset_context.cpp +
liblongport_cpp link).
…582)

Sync longbridge/openapi #582. StockRatings javadoc referenced the disabled
getRatings method via @link, breaking the javadoc build; replaced with a plain
reference. No behaviour change.

Verified: javac over all Java sources.
… (sync #587)

Sync longbridge/openapi #587 (java JNI correctness only; the PR's large
openapi.pyi resync is omitted — longport maintains openapi.pyi per-feature and
it is already in sync).

Repairs several JNI functions whose Rust signatures disagreed with the Java
native declarations / option-object fields (compiles, fails at link):
fundamental_context, market_context, quote_context, screener_context read their
option objects via get_field instead of positional args, and
quoteContextShortPositions / getShortPositions gain the missing count parameter.
JNI header regenerated.

Verified: cargo check -p longport-java --all-features, javac over all Java
sources + JNI header regen.
Sync longbridge/openapi #600. Bump GitHub-maintained actions in ci.yml to the
current majors: checkout v4->v7, setup-python v5->v7, upload-artifact v4->v7,
setup-node v6->v7, cache v4->v6, setup-java v5->v6. Applied to longport ci.yml
(structurally divergent from upstream, so the version bumps were applied
directly rather than by patch).
…ange)

Sync longbridge/openapi 6fb29c05c. Execution gains a side field (OrderSide) —
the buy/sell direction of the fill — across all SDK layers (Rust core, C, C++,
Java, Node.js, Python). Returned by today_executions and history_executions.

Generated artifacts regenerated: c/csrc/include/longport.h, nodejs/index.d.ts,
nodejs/index.js. openapi.pyi updated by hand.

Verified: cargo check --workspace --all-features, cargo build -p longport-c,
nodejs npm run build:debug, cmake C++ build, javac over all Java sources.
…Ks (sync #574)

Sync longbridge/openapi #574. Doc-comment clarification that
CompanyOverview.employees is returned as a string by the API (longport already
types it as String across all bindings). Doc comments only; no type or
behaviour change.
…3f33ebf8b)

Sync longbridge/openapi 3f33ebf8b, the test companion to the Execution.side
field. The Execution struct deserializes side without a serde default, so the
history_executions unit test fixture must carry "side": "Buy"; add it and the
matching assertion. Fixes the check-rust-sdk CI failure (cargo test) that the
earlier cargo check could not surface.

Verified: cargo test --workspace --all-features (all pass).

This branch has not been deployed

No deployments
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