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: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **All languages:** `QuoteContext` gains `etf_asset_allocation(symbol)` — queries `GET /v1/quote/etf-asset-allocation` for ETF asset allocation grouped by element type (`Holdings` / `Regional` / `AssetClass` / `Industry`); returns `AssetAllocationResponse` with report date, position ratios, localized names, and per-holding detail
- **All languages:** `FundamentalContext` gains `etf_asset_allocation(symbol)` — queries `GET /v1/quote/etf-asset-allocation` for ETF asset allocation grouped by element type (`Holdings` / `Regional` / `AssetClass` / `Industry`); returns `AssetAllocationResponse` with report date, position ratios, localized names, and per-holding detail

## [4.2.2]

Expand Down
4 changes: 2 additions & 2 deletions c/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,13 @@ cpp_compat = true
"COptionVolumeStats" = "lb_option_volume_stats_t"
"COptionVolumeDailyStat" = "lb_option_volume_daily_stat_t"
"COptionVolumeDaily" = "lb_option_volume_daily_t"
# FundamentalContext new types
"CElementType" = "lb_element_type_t"
"CLocaleName" = "lb_locale_name_t"
"CHoldingDetail" = "lb_holding_detail_t"
"CAssetAllocationItem" = "lb_asset_allocation_item_t"
"CAssetAllocationGroup" = "lb_asset_allocation_group_t"
"CAssetAllocationResponse" = "lb_asset_allocation_response_t"
# FundamentalContext new types
"CShareholderTopResponse" = "lb_shareholder_top_response_t"
"CShareholderDetailResponse" = "lb_shareholder_detail_response_t"
"CValuationHistoryPoint" = "lb_valuation_history_point_t"
Expand Down Expand Up @@ -447,10 +447,10 @@ include = [
"CShortTradesItem", "CShortTradesResponse",
"COptionVolumeStats",
"COptionVolumeDailyStat", "COptionVolumeDaily",
# FundamentalContext new types
"CElementType",
"CLocaleName", "CHoldingDetail",
"CAssetAllocationItem", "CAssetAllocationGroup", "CAssetAllocationResponse",
# FundamentalContext new types
"CShareholderTopResponse", "CShareholderDetailResponse",
"CValuationHistoryPoint", "CValuationComparisonItem", "CValuationComparisonResponse",
# MarketContext new types
Expand Down
18 changes: 9 additions & 9 deletions c/csrc/include/longbridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -9543,6 +9543,15 @@ void lb_fundamental_context_valuation_comparison(const struct lb_fundamental_con
lb_async_callback_t callback,
void *userdata);

/**
* Get ETF asset allocation (holdings / regional / asset class / industry).
* Returns `CAssetAllocationResponse`.
*/
void lb_fundamental_context_etf_asset_allocation(const struct lb_fundamental_context_t *ctx,
const char *symbol,
lb_async_callback_t callback,
void *userdata);

/**
* Create a HTTP client using API Key authentication
*
Expand Down Expand Up @@ -10327,15 +10336,6 @@ void lb_quote_context_option_volume_daily(const struct lb_quote_context_t *ctx,
lb_async_callback_t callback,
void *userdata);

/**
* Get ETF asset allocation (holdings / regional / asset class / industry).
* Returns `CAssetAllocationResponse`.
*/
void lb_quote_context_etf_asset_allocation(const struct lb_quote_context_t *ctx,
const char *symbol,
lb_async_callback_t callback,
void *userdata);

const struct lb_screener_context_t *lb_screener_context_new(const struct lb_config_t *config);

void lb_screener_context_retain(const struct lb_screener_context_t *ctx);
Expand Down
19 changes: 19 additions & 0 deletions c/src/fundamental_context/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,3 +661,22 @@ pub unsafe extern "C" fn lb_fundamental_context_valuation_comparison(
Ok(resp)
});
}

/// Get ETF asset allocation (holdings / regional / asset class / industry).
/// Returns `CAssetAllocationResponse`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_fundamental_context_etf_asset_allocation(
ctx: *const CFundamentalContext,
symbol: *const c_char,
callback: CAsyncCallback,
userdata: *mut c_void,
) {
let ctx_inner = (*ctx).ctx.clone();
let symbol = cstr_to_rust(symbol);
execute_async(callback, ctx, userdata, async move {
let resp: CCow<CAssetAllocationResponseOwned> = CCow::new(
CAssetAllocationResponseOwned::from(ctx_inner.etf_asset_allocation(symbol).await?),
);
Ok(resp)
});
}
23 changes: 23 additions & 0 deletions c/src/fundamental_context/enum_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,26 @@ pub enum CFinancialReportPeriod {
#[c(remote = "ThreeQ")]
FinancialReportPeriodThreeQ,
}

/// ETF asset allocation element type
#[derive(Debug, Copy, Clone, Eq, PartialEq, CEnum)]
#[c(remote = "longbridge::fundamental::types::ElementType")]
#[allow(clippy::enum_variant_names)]
#[repr(C)]
pub enum CElementType {
/// Unknown
#[c(remote = "Unknown")]
ElementTypeUnknown,
/// Holdings
#[c(remote = "Holdings")]
ElementTypeHoldings,
/// Regional
#[c(remote = "Regional")]
ElementTypeRegional,
/// Asset class
#[c(remote = "AssetClass")]
ElementTypeAssetClass,
/// Industry
#[c(remote = "Industry")]
ElementTypeIndustry,
}
220 changes: 219 additions & 1 deletion c/src/fundamental_context/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::os::raw::c_char;
use longbridge::fundamental::types::*;

use crate::{
fundamental_context::enum_types::CInstitutionRecommend,
fundamental_context::enum_types::{CElementType, CInstitutionRecommend},
types::{COption, CString, CVec, ToFFI},
};

Expand Down Expand Up @@ -3842,3 +3842,221 @@ impl ToFFI for CFinancialReportSnapshotOwned {
}
}
}

// ── EtfAssetAllocation ────────────────────────────────────────────

/// Localized name entry (locale → name)
#[repr(C)]
pub struct CLocaleName {
/// Locale (e.g. `zh-CN`)
pub locale: *const c_char,
/// Localized name
pub name: *const c_char,
}

pub(crate) struct CLocaleNameOwned {
locale: CString,
name: CString,
}

impl From<(String, String)> for CLocaleNameOwned {
fn from((locale, name): (String, String)) -> Self {
Self {
locale: locale.into(),
name: name.into(),
}
}
}

impl ToFFI for CLocaleNameOwned {
type FFIType = CLocaleName;
fn to_ffi_type(&self) -> Self::FFIType {
CLocaleName {
locale: self.locale.to_ffi_type(),
name: self.name.to_ffi_type(),
}
}
}

/// Holding detail of an ETF asset allocation element (holdings only)
#[repr(C)]
pub struct CHoldingDetail {
/// Industry ID
pub industry_id: *const c_char,
/// Industry name
pub industry_name: *const c_char,
/// Index counter ID (e.g. `BK/US/CP99000`)
pub index: *const c_char,
/// Index name
pub index_name: *const c_char,
/// Holding type (e.g. `E` for stock)
pub holding_type: *const c_char,
/// Holding type name
pub holding_type_name: *const c_char,
}

pub(crate) struct CHoldingDetailOwned {
industry_id: CString,
industry_name: CString,
index: CString,
index_name: CString,
holding_type: CString,
holding_type_name: CString,
}

impl From<HoldingDetail> for CHoldingDetailOwned {
fn from(v: HoldingDetail) -> Self {
Self {
industry_id: v.industry_id.into(),
industry_name: v.industry_name.into(),
index: v.index.into(),
index_name: v.index_name.into(),
holding_type: v.holding_type.into(),
holding_type_name: v.holding_type_name.into(),
}
}
}

impl ToFFI for CHoldingDetailOwned {
type FFIType = CHoldingDetail;
fn to_ffi_type(&self) -> Self::FFIType {
CHoldingDetail {
industry_id: self.industry_id.to_ffi_type(),
industry_name: self.industry_name.to_ffi_type(),
index: self.index.to_ffi_type(),
index_name: self.index_name.to_ffi_type(),
holding_type: self.holding_type.to_ffi_type(),
holding_type_name: self.holding_type_name.to_ffi_type(),
}
}
}

/// One element of an ETF asset allocation group
#[repr(C)]
pub struct CAssetAllocationItem {
/// Element name
pub name: *const c_char,
/// Security code (holdings only, e.g. `NVDA`)
pub code: *const c_char,
/// Position ratio (e.g. `0.0861114`)
pub position_ratio: *const c_char,
/// Security symbol (holdings only, e.g. `NVDA.US`)
pub symbol: *const c_char,
/// Pointer to array of localized name entries
pub name_locales: *const CLocaleName,
/// Number of elements in the localized name array
pub num_name_locales: usize,
/// Holding detail (holdings only, maybe null)
pub holding_detail: *const CHoldingDetail,
}

pub(crate) struct CAssetAllocationItemOwned {
name: CString,
code: CString,
position_ratio: CString,
symbol: CString,
name_locales: CVec<CLocaleNameOwned>,
holding_detail: COption<CHoldingDetailOwned>,
}

impl From<AssetAllocationItem> for CAssetAllocationItemOwned {
fn from(v: AssetAllocationItem) -> Self {
let mut name_locales = v.name_locales.into_iter().collect::<Vec<_>>();
name_locales.sort();
Self {
name: v.name.into(),
code: v.code.into(),
position_ratio: v.position_ratio.into(),
symbol: v.symbol.into(),
name_locales: name_locales.into(),
holding_detail: v.holding_detail.into(),
}
}
}

impl ToFFI for CAssetAllocationItemOwned {
type FFIType = CAssetAllocationItem;
fn to_ffi_type(&self) -> Self::FFIType {
CAssetAllocationItem {
name: self.name.to_ffi_type(),
code: self.code.to_ffi_type(),
position_ratio: self.position_ratio.to_ffi_type(),
symbol: self.symbol.to_ffi_type(),
name_locales: self.name_locales.to_ffi_type(),
num_name_locales: self.name_locales.len(),
holding_detail: self.holding_detail.to_ffi_type(),
}
}
}

/// One ETF asset allocation group (grouped by element type)
#[repr(C)]
pub struct CAssetAllocationGroup {
/// Report date (e.g. `20260601`)
pub report_date: *const c_char,
/// Element type of this group
pub asset_type: CElementType,
/// Pointer to array of elements
pub lists: *const CAssetAllocationItem,
/// Number of elements in the array
pub num_lists: usize,
}

pub(crate) struct CAssetAllocationGroupOwned {
report_date: CString,
asset_type: ElementType,
lists: CVec<CAssetAllocationItemOwned>,
}

impl From<AssetAllocationGroup> for CAssetAllocationGroupOwned {
fn from(v: AssetAllocationGroup) -> Self {
Self {
report_date: v.report_date.into(),
asset_type: v.asset_type,
lists: v.lists.into(),
}
}
}

impl ToFFI for CAssetAllocationGroupOwned {
type FFIType = CAssetAllocationGroup;
fn to_ffi_type(&self) -> Self::FFIType {
CAssetAllocationGroup {
report_date: self.report_date.to_ffi_type(),
asset_type: self.asset_type.into(),
lists: self.lists.to_ffi_type(),
num_lists: self.lists.len(),
}
}
}

/// ETF asset allocation response
#[repr(C)]
pub struct CAssetAllocationResponse {
/// Pointer to array of asset allocation groups
pub info: *const CAssetAllocationGroup,
/// Number of elements in the array
pub num_info: usize,
}

pub(crate) struct CAssetAllocationResponseOwned {
info: CVec<CAssetAllocationGroupOwned>,
}

impl From<AssetAllocationResponse> for CAssetAllocationResponseOwned {
fn from(v: AssetAllocationResponse) -> Self {
Self {
info: v.info.into(),
}
}
}

impl ToFFI for CAssetAllocationResponseOwned {
type FFIType = CAssetAllocationResponse;
fn to_ffi_type(&self) -> Self::FFIType {
CAssetAllocationResponse {
info: self.info.to_ffi_type(),
num_info: self.info.len(),
}
}
}
20 changes: 0 additions & 20 deletions c/src/quote_context/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1290,23 +1290,3 @@ pub unsafe extern "C" fn lb_quote_context_option_volume_daily(
Ok(resp)
});
}

/// Get ETF asset allocation (holdings / regional / asset class / industry).
/// Returns `CAssetAllocationResponse`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_quote_context_etf_asset_allocation(
ctx: *const CQuoteContext,
symbol: *const c_char,
callback: CAsyncCallback,
userdata: *mut c_void,
) {
use crate::{quote_context::types::CAssetAllocationResponseOwned, types::CCow};
let ctx_inner = (*ctx).ctx.clone();
let symbol = cstr_to_rust(symbol);
execute_async(callback, ctx, userdata, async move {
let resp: CCow<CAssetAllocationResponseOwned> = CCow::new(
CAssetAllocationResponseOwned::from(ctx_inner.etf_asset_allocation(symbol).await?),
);
Ok(resp)
});
}
23 changes: 0 additions & 23 deletions c/src/quote_context/enum_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,26 +632,3 @@ pub enum CGranularity {
#[c(remote = "Monthly")]
GranularityMonthly,
}

/// ETF asset allocation element type
#[derive(Debug, Copy, Clone, Eq, PartialEq, CEnum)]
#[c(remote = "longbridge::quote::ElementType")]
#[allow(clippy::enum_variant_names)]
#[repr(C)]
pub enum CElementType {
/// Unknown
#[c(remote = "Unknown")]
ElementTypeUnknown,
/// Holdings
#[c(remote = "Holdings")]
ElementTypeHoldings,
/// Regional
#[c(remote = "Regional")]
ElementTypeRegional,
/// Asset class
#[c(remote = "AssetClass")]
ElementTypeAssetClass,
/// Industry
#[c(remote = "Industry")]
ElementTypeIndustry,
}
Loading
Loading