Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **Python SDK:** reconciled the hand-maintained type stub `python/pysrc/longbridge/openapi.pyi` with the actual PyO3 implementation. Removed phantom methods that did not exist and would raise `AttributeError` when called (`AlertContext.enable`/`disable`, `AsyncQuoteContext.option_volume`/`option_volume_daily`); fixed wrong signatures/return types (`FundamentalContext.macroeconomic_indicators` had dropped its `country`/`keyword` params and had the wrong return type, `macroeconomic` was missing `offset`, `DCAContext.create`/`update` return `DcaCreateResult` not `DcaList`, `DCAContext.pause`/`resume`/`stop` and `SharelistContext.create` return `None`); added missing methods (`QuoteContext.filings` + async, `AlertContext.update`, `DCAContext.update`, `TradeContext.set_on_grid_order_changed` + async); added the three entirely-missing async context classes (`AsyncMarketContext`, `AsyncCalendarContext`, `AsyncPortfolioContext`) plus the missing method surfaces of `AsyncFundamentalContext` and `AsyncContentContext`; and added the missing referenced types (`FilingItem`, `DcaCreateResult`, `MacroeconomicCountry`, `MacroeconomicIndicatorListResponse`, `PushGridOrderChanged`). Type hints only — no runtime/behaviour change to the native module
- **Java SDK:** fixed six JNI methods whose Rust `extern "system"` signature no longer matched the Java `native` declaration, so every call aborted with `java.lang.RuntimeException: JNI call failed` (or read misaligned stack arguments). The Java layer had been migrated to options objects / trimmed argument lists but the Rust JNI side was left in the old positional form. `MarketContext.getRankList` (`RankListOptions`), `QuoteContext.getShortTrades` (`ShortTradesOptions`), `ScreenerContext.getStrategy` (`ScreenerStrategyOptions`), `FundamentalContext.shareholderDetail` (`ShareholderDetailOptions`) and `FundamentalContext.valuationComparison` (`ValuationComparisonOptions`) now read their fields off the options object. Separately, `QuoteContext.getShortPositions` was missing the `count` parameter that the Rust core, Node.js and Python bindings all require — the Rust JNI still expected it, so the call crashed — so `getShortPositions(String symbol)` becomes `getShortPositions(String symbol, int count)`. Reported as longbridge/developers#1249 (`getRankList`)
- **C/C++ SDKs:** every list argument that crosses the FFI boundary now tolerates a null pointer with a zero length. `std::vector::data()` is allowed to return `nullptr` for an empty vector, which is exactly what the C++ binding passes for an omitted list argument, but the C layer fed it straight to `std::slice::from_raw_parts` — undefined behaviour that **aborts the process** under the debug UB checks. Hit live by `QuoteContext::warrant_list` with no filters (`c/src/quote_context/context.rs:784`); all 17 call sites across `quote_context`, `trade_context`, `agent_context`, `alert_context`, and `types` now go through a null-tolerant `slice_from_raw_parts` helper
- **C++ SDK:** `asset::AssetContext` (`statements` / `statement_download_url`) is now actually built and usable. `longbridge.hpp` has always included `asset_context.hpp`, but `cpp/src/asset_context.cpp` was never listed in `cpp/CMakeLists.txt`, so the class was declared to users and then failed to link. It had also never compiled: it included neither `longbridge.h` nor the C declarations, and `statement_download_url` read `res->data` as a `lb_statement_download_url_response_t*` — a type that does not exist anywhere in the C layer, which delivers the URL as a bare `const char*` (the same convention as `QuoteContext::quote_level`). Fixed the include and the callback, and added the file to the build
- **C SDK:** export `lb_statement_item_t` from `longbridge.h`. `CStatementItem` is only reachable through the `void*` async-result pointer, so cbindgen did not emit it and no C or C++ caller could read what `lb_asset_context_statements` returns. Also added the missing `CAssetContext` → `lb_asset_context_t` entry to the cbindgen rename map: every other context type was mapped, so the header exposed the raw Rust name (`const struct CAssetContext *lb_asset_context_new(...)`) while the C++ side forward-declared `lb_asset_context_t`
Expand Down
2 changes: 1 addition & 1 deletion java/javasrc/src/main/java/com/longbridge/SdkNative.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ public static native void gridContextTriggerHistory(long context, GetGridTrigger

// ── QuoteContext extensions (Step 3) ─────────────────────────

public static native void quoteContextShortPositions(long context, String symbol, AsyncCallback callback);
public static native void quoteContextShortPositions(long context, String symbol, int count, AsyncCallback callback);
public static native void quoteContextOptionVolume(long context, String symbol, AsyncCallback callback);
public static native void quoteContextOptionVolumeDaily(long context, Object opts, AsyncCallback callback);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1396,12 +1396,13 @@ public synchronized CompletableFuture<Trade[]> getRealtimeTrades(String symbol,
* Get short positions for a symbol
*
* @param symbol Security symbol
* @param count Number of records to return
* @return A Future representing the short positions response
* @throws OpenApiException If an error occurs
*/
public synchronized CompletableFuture<ShortPositionsResponse> getShortPositions(String symbol) throws OpenApiException {
public synchronized CompletableFuture<ShortPositionsResponse> getShortPositions(String symbol, int count) throws OpenApiException {
return AsyncCallback.executeTask((callback) -> {
SdkNative.quoteContextShortPositions(raw(), symbol, callback);
SdkNative.quoteContextShortPositions(raw(), symbol, count, callback);
});
}

Expand Down
23 changes: 9 additions & 14 deletions java/src/fundamental_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,14 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundamentalContextSh
mut env: JNIEnv,
_class: JClass,
context: i64,
symbol: JObject,
object_id: i64,
opts: JObject,
callback: JObject,
) {
jni_result(&mut env, (), |env| {
let context = &*(context as *const ContextObj);
let __owned_ctx = context.ctx.clone();
let symbol: String = FromJValue::from_jvalue(env, symbol.into())?;
let symbol: String = get_field(env, &opts, "symbol")?;
let object_id: i64 = get_field(env, &opts, "objectId")?;
async_util::execute(env, callback, async move {
let resp = __owned_ctx.shareholder_detail(symbol, object_id).await?;
Ok(resp)
Expand All @@ -365,22 +365,17 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundamentalContextVa
mut env: JNIEnv,
_class: JClass,
context: i64,
symbol: JObject,
currency: JObject,
comparison_symbols: JObject,
opts: JObject,
callback: JObject,
) {
jni_result(&mut env, (), |env| {
let context = &*(context as *const ContextObj);
let __owned_ctx = context.ctx.clone();
let symbol: String = FromJValue::from_jvalue(env, symbol.into())?;
let currency: String = FromJValue::from_jvalue(env, currency.into())?;
let comparison_syms: Option<Vec<String>> = if comparison_symbols.is_null() {
None
} else {
let arr: ObjectArray<String> = FromJValue::from_jvalue(env, comparison_symbols.into())?;
Some(arr.0)
};
let symbol: String = get_field(env, &opts, "symbol")?;
let currency: String = get_field(env, &opts, "currency")?;
let comparison_syms: Option<Vec<String>> =
get_field::<_, _, Option<ObjectArray<String>>>(env, &opts, "comparisonSymbols")?
.map(|arr| arr.0);
async_util::execute(env, callback, async move {
let resp = __owned_ctx
.valuation_comparison(symbol, currency, comparison_syms)
Expand Down
6 changes: 3 additions & 3 deletions java/src/market_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,14 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_marketContextRankLis
mut env: JNIEnv,
_class: JClass,
context: i64,
key: JObject,
need_article: bool,
opts: JObject,
callback: JObject,
) {
jni_result(&mut env, (), |env| {
let context = &*(context as *const ContextObj);
let __owned_ctx = context.ctx.clone();
let key: String = FromJValue::from_jvalue(env, key.into())?;
let key: String = get_field(env, &opts, "key")?;
let need_article: bool = get_field(env, &opts, "needArticle")?;
async_util::execute(env, callback, async move {
let resp = __owned_ctx.rank_list(key, need_article).await?;
Ok(resp)
Expand Down
6 changes: 3 additions & 3 deletions java/src/quote_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,14 +1250,14 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_quoteContextShortTra
mut env: JNIEnv,
_class: JClass,
context: i64,
symbol: JObject,
count: i32,
opts: JObject,
callback: JObject,
) {
jni_result(&mut env, (), |env| {
let context = &*(context as *const ContextObj);
let __owned_ctx = context.ctx.clone();
let symbol: String = FromJValue::from_jvalue(env, symbol.into())?;
let symbol: String = get_field(env, &opts, "symbol")?;
let count: i32 = get_field(env, &opts, "count")?;
let count = count.max(1) as u32;
async_util::execute(env, callback, async move {
let resp = __owned_ctx.short_trades(symbol, count).await?;
Expand Down
3 changes: 2 additions & 1 deletion java/src/screener_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_screenerContextStrat
mut env: JNIEnv,
_class: JClass,
context: i64,
id: i64,
opts: JObject,
callback: JObject,
) {
jni_result(&mut env, (), |env| {
let context = &*(context as *const ContextObj);
let __owned_ctx = context.ctx.clone();
let id: i64 = get_field(env, &opts, "id")?;
async_util::execute(env, callback, async move {
let resp = __owned_ctx.screener_strategy(id).await?;
Ok(resp)
Expand Down
Loading
Loading