diff --git a/CHANGELOG.md b/CHANGELOG.md index fca045f87..102d9df49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **Rust core (async + blocking):** new `FundContext` / `FundContextSync` for the mutual-fund channel, covering 28 endpoints across fund catalog & market data (hot funds, fund list, filters, detail, analysis/trend, annual/quarterly returns, performance & comparison, latest/historical NAV, top-10 holdings, reverse stock holdings), the user's fund positions (overview, single position, performance, profits, NAV history, dividends), and fund orders/trading (order list & detail, transactions/cash-flow, order validate/submit/cancel). The fund identifier is exposed as `counter_id` (e.g. `UT/FD/HK0000384492`); because it contains `/` it cannot sit in the URL path, so single-fund endpoints take it as a `counter_id` query parameter (fixed sub-paths like `/v1/fund/funds/detail`, `/v1/fund/funds/nav`) rather than a path segment. Three batch-backed endpoints — latest NAV (`nav`), daily performance (`performance`) and held-fund performance (`position_performance`) — instead send the identifier as a one-element JSON array in a `counter_ids` query parameter, matching their backend contract. Nested response objects the backend may return as `null` — `FundDetail.asset_allocation`, `FundTrend.contrast_performances`, `FundPositionDetail.detail_values` and `FundOrderDetail.order` — are modeled as optional/nullable (`Option` / `std::optional` / nullable pointer / `T | null` / `Optional[T]`) so those responses deserialize instead of erroring on `null`. Numeric `int64` fields tolerate the backend sending them as either a JSON number or a quoted string, so a numeric value never fails the whole response. Mirrored across the C, C++, Java, Node.js and Python layers; the separate Go SDK is pending. +- **C:** `lb_fund_context_*` FFI (all 28 methods via `c/src/fund_context`) with the request-option structs (`lb_get_funds_options_t`, `lb_submit_fund_order_options_t`, …) and all 39 fund response structs (`lb_fund_detail_t`, `lb_hot_fund_t`, `lb_fund_order_t`, …) emitted into `c/csrc/include/longbridge.h`. The fund context and its option structs are added to the cbindgen `[export.rename]` map so they follow the `lb_*_t` naming convention like every other channel (rather than emitting raw `C…` names). +- **Node.js:** `FundContext` (napi) binding mirroring the Rust `FundContext` (all 28 methods) plus the fund response/option types, regenerated into `nodejs/index.d.ts`. The fund-channel position entry is exposed to JS as `FundHoldingPosition` to avoid colliding with the existing trade `FundPosition`. Server-defined "any" JSON fields are surfaced as raw JSON `string` (or `Array`), unix-second timestamps as `number`, and numeric-string fields as `string`. +- **Java:** `com.longbridge.fund.FundContext` JNI binding mirroring the Rust `FundContext` (all 28 methods), plus the fund response and option types under `com.longbridge.fund`. Server-defined "any" JSON fields are surfaced as raw JSON `String` (or `String[]`), unix-second timestamps as `long`, and numeric-string fields as `String`. +- **Python:** `longbridge.openapi.FundContext` (PyO3) binding mirroring the Rust `FundContext` (all 28 methods), plus all fund response types (`HotFund`, `FundBrief`, `FundFilters`, `FundDetail`, `FundAnalysis`/`FundAnalysisDetail`, `FundTrend`, `FundPerformanceComparison`, `FundHoldings`, `FundPositions`, `FundPositionDetail`, `FundOrderDetail`, `FundDividends`, `FundOrderValidation`, `FundOrderSubmitResponse`, and their nested types). The fund-channel position entry is exposed as `FundHoldingPosition` to avoid colliding with the existing trade `FundPosition`. Server-defined "any" JSON fields are surfaced as raw JSON `str` (or `list[str]`), unix-second timestamps as `int`, and numeric-string fields as `str`. The `openapi.pyi` type stub is updated to match. +- **C++:** `longbridge::fund::FundContext` binding wrapping the C FFI layer (all 28 methods), plus the fund request-option and response types under `longbridge::fund` in `types.hpp`. Server-defined "any" JSON fields are surfaced as raw JSON `std::string` (or `std::vector`), unix-second timestamps as `int64_t`, and numeric-string fields as `std::string`. + +### Fixed + +- **C / C++:** resolved a cbindgen name collision introduced with the fund C FFI layer: the fund module's `CFundPosition` and `CGetFundPositionsOptions` shared their names with the existing portfolio types, so the regenerated `longbridge.h` overwrote `lb_fund_position_t` and `lb_get_fund_positions_options_t` with the fund field layouts, breaking the C++ trade/portfolio converters. The fund structs were renamed to `CFundPositionItem` (emitted as `lb_fund_position_item_t`, reachable via `lb_fund_positions_t.list`) and `CFundPositionsOptions` (`lb_fund_positions_options_t`, a `lb_fund_context_positions` parameter) so both modules coexist with distinct names. + ### Changed +- **All SDKs (breaking):** `TradeContext.fund_positions` — the `FundPosition` identifier field is renamed from `symbol` (which carried the fund's ISIN) to `counter_id`, aligning it with the rest of the fund tooling. The value is now the full fund `counter_id` (e.g. `UT/FD/HK0000384492`); the ISIN remains recoverable as the last `/`-separated segment. Renamed across every layer: Rust core (`counter_id`, deserializes the legacy `symbol` key via a serde alias for transition), C (`lb_fund_position_t.counter_id`), C++ (`FundPosition::counter_id`), Java (`FundPosition.getCounterId()`), Node.js (`FundPosition.counterId`), Python (`FundPosition.counter_id`). Note the request-side filter (`GetFundPositionsOptions.symbols`) is unchanged — you still filter holdings by user symbol, but the response now returns the fund `counter_id`. - **All SDKs:** `CalendarContext.finance_calendar` now exposes the endpoint's pagination controls so callers can page through results themselves. Previously the method issued a single request with no page-size control, so the server's default page cap (historically ~10 events) made even a one-day query look truncated at 10 results. Three optional parameters were added — `count` (max events per page), `offset` (events to skip), and `next` (a new `CalendarPageDirection` enum: `Later` / `Earlier`) — and the response's `next_date` cursor is returned as-is. To retrieve a full window, request a larger `count`, or re-call with the returned `next_date` as `start` until it comes back empty. This is a signature change across every layer (Rust core + blocking, C, C++, Java, Node.js, Python); the new arguments are optional in the languages that support defaults. Reported as longbridge/developers#1265 ## [5.1.0] - 2026-09-20 diff --git a/c/cbindgen.toml b/c/cbindgen.toml index f8a0f0df0..24750a1fd 100644 --- a/c/cbindgen.toml +++ b/c/cbindgen.toml @@ -95,9 +95,65 @@ cpp_compat = true "CChargeCategoryCode" = "lb_charge_category_code_t" "CGetCashFlowOptions" = "lb_get_cash_flow_options_t" "CGetFundPositionsOptions" = "lb_get_fund_positions_options_t" +"CFundPositionsOptions" = "lb_fund_positions_options_t" "CFundPositionsResponse" = "lb_fund_position_response_t" "CFundPositionChannel" = "lb_fund_position_channel_t" "CFundPosition" = "lb_fund_position_t" +# --- fund module response views --- +"CFundAnalysis" = "lb_fund_analysis_t" +"CFundAnalysisDetail" = "lb_fund_analysis_detail_t" +"CFundAnnualReturn" = "lb_fund_annual_return_t" +"CFundAssetAllocation" = "lb_fund_asset_allocation_t" +"CFundAssetAllocationItem" = "lb_fund_asset_allocation_item_t" +"CFundBrief" = "lb_fund_brief_t" +"CFundDatedValue" = "lb_fund_dated_value_t" +"CFundDetail" = "lb_fund_detail_t" +"CFundDividend" = "lb_fund_dividend_t" +"CFundDividends" = "lb_fund_dividends_t" +"CFundFilters" = "lb_fund_filters_t" +"CFundHolding" = "lb_fund_holding_t" +"CFundHoldings" = "lb_fund_holdings_t" +"CFundNamedContrast" = "lb_fund_named_contrast_t" +"CFundNavValue" = "lb_fund_nav_value_t" +"CFundOrder" = "lb_fund_order_t" +"CFundOrderDetail" = "lb_fund_order_detail_t" +"CFundOrderInfo" = "lb_fund_order_info_t" +"CFundOrderKeyword" = "lb_fund_order_keyword_t" +"CFundOrderStage" = "lb_fund_order_stage_t" +"CFundOrderSubmitResponse" = "lb_fund_order_submit_response_t" +"CFundOrderValidation" = "lb_fund_order_validation_t" +"CFundPerformance" = "lb_fund_performance_t" +"CFundPerformanceComparison" = "lb_fund_performance_comparison_t" +"CFundPerformancePoint" = "lb_fund_performance_point_t" +"CFundPositionDetail" = "lb_fund_position_detail_t" +"CFundPositionDetailValues" = "lb_fund_position_detail_values_t" +"CFundPositionItem" = "lb_fund_position_item_t" +"CFundPositionNav" = "lb_fund_position_nav_t" +"CFundPositionPerformance" = "lb_fund_position_performance_t" +"CFundPositionProfits" = "lb_fund_position_profits_t" +"CFundPositions" = "lb_fund_positions_t" +"CFundQuarterlyReturn" = "lb_fund_quarterly_return_t" +"CFundStockHolding" = "lb_fund_stock_holding_t" +"CFundTransaction" = "lb_fund_transaction_t" +"CFundTrend" = "lb_fund_trend_t" +"CFundTrendContrast" = "lb_fund_trend_contrast_t" +"CFundUnitValue" = "lb_fund_unit_value_t" +"CHotFund" = "lb_hot_fund_t" +# --- fund module: context + option structs --- +"CFundContext" = "lb_fund_context_t" +"CGetFundsOptions" = "lb_get_funds_options_t" +"CGetFundAnalysisOptions" = "lb_get_fund_analysis_options_t" +"CFundPageOptions" = "lb_fund_page_options_t" +"CFundNavRangeOptions" = "lb_fund_nav_range_options_t" +"CGetFundHoldingsOptions" = "lb_get_fund_holdings_options_t" +"CGetFundStockHoldingsOptions" = "lb_get_fund_stock_holdings_options_t" +"CGetFundPositionOptions" = "lb_get_fund_position_options_t" +"CGetFundPositionProfitsOptions" = "lb_get_fund_position_profits_options_t" +"CGetFundPositionDividendsOptions" = "lb_get_fund_position_dividends_options_t" +"CGetFundOrdersOptions" = "lb_get_fund_orders_options_t" +"CGetFundTransactionsOptions" = "lb_get_fund_transactions_options_t" +"CValidateFundOrderOptions" = "lb_validate_fund_order_options_t" +"CSubmitFundOrderOptions" = "lb_submit_fund_order_options_t" "CGetStockPositionsOptions" = "lb_get_stock_positions_options_t" "CStockPositionsResponse" = "lb_stock_position_response_t" "CStockPositionChannel" = "lb_stock_position_channel_t" @@ -444,6 +500,17 @@ include = [ "CGridOrder", "CGridOrderSubOrder", "CGridOrderHistory", "CGridOrderDetail", "CTriggerOrder", "CGridBidSize", "CGridChannelInfo", "CGridSymbolInfo", "CGridOrdersResponse", "CGridTriggerHistoryResponse", + # FundContext: mutual-fund response views (via void* async data pointer) + "CFundAnalysis", "CFundAnalysisDetail", "CFundAnnualReturn", "CFundAssetAllocation", + "CFundAssetAllocationItem", "CFundBrief", "CFundDatedValue", "CFundDetail", + "CFundDividend", "CFundDividends", "CFundFilters", "CFundHolding", + "CFundHoldings", "CFundNamedContrast", "CFundNavValue", "CFundOrder", + "CFundOrderDetail", "CFundOrderInfo", "CFundOrderKeyword", "CFundOrderStage", + "CFundOrderSubmitResponse", "CFundOrderValidation", "CFundPerformance", "CFundPerformanceComparison", + "CFundPerformancePoint", "CFundPositionDetail", "CFundPositionDetailValues", "CFundPositionItem", + "CFundPositionNav", "CFundPositionPerformance", "CFundPositionProfits", "CFundPositions", + "CFundQuarterlyReturn", "CFundStockHolding", "CFundTransaction", "CFundTrend", + "CFundTrendContrast", "CFundUnitValue", "CHotFund", # TradeContext: grid trading push (stays on trade side) "CPushGridOrderChanged", "CEstimateMaxPurchaseQuantityResponse", diff --git a/c/csrc/include/longbridge.h b/c/csrc/include/longbridge.h index 7b8fddbd4..9d281ca68 100644 --- a/c/csrc/include/longbridge.h +++ b/c/csrc/include/longbridge.h @@ -1992,6 +1992,8 @@ typedef struct lb_decimal_t lb_decimal_t; typedef struct lb_error_t lb_error_t; +typedef struct lb_fund_context_t lb_fund_context_t; + typedef struct lb_fundamental_context_t lb_fundamental_context_t; typedef struct lb_grid_context_t lb_grid_context_t; @@ -3237,6 +3239,354 @@ typedef struct lb_alert_item_t { struct CAlertValueMap value_map; } lb_alert_item_t; +/** + * Options for the fund list request. + */ +typedef struct lb_get_funds_options_t { + /** + * Server-defined filter object as a JSON string (can be null) + */ + const char *filter; + /** + * Quick-filter ids (can be null) + */ + const int64_t *quick_ids; + /** + * Number of quick-filter ids + */ + uintptr_t num_quick_ids; + /** + * Earning-rate time intervals (can be null) + */ + const char *const *time_interval; + /** + * Number of time intervals + */ + uintptr_t num_time_interval; +} lb_get_funds_options_t; + +/** + * Options for the fund analysis / trend / comparison request. + */ +typedef struct lb_get_fund_analysis_options_t { + /** + * Analysis period (can be null) + */ + const int32_t *period; +} lb_get_fund_analysis_options_t; + +/** + * Paging options (page / size). + */ +typedef struct lb_fund_page_options_t { + /** + * Page number (can be null) + */ + const int32_t *page; + /** + * Page size (can be null) + */ + const int32_t *size; +} lb_fund_page_options_t; + +/** + * Net-value range options (relative months / years before now). + */ +typedef struct lb_fund_nav_range_options_t { + /** + * Number of months before now (can be null) + */ + const int32_t *month_before; + /** + * Number of years before now (can be null) + */ + const int32_t *year_before; +} lb_fund_nav_range_options_t; + +/** + * Options for the fund holdings request. + */ +typedef struct lb_get_fund_holdings_options_t { + /** + * Scene (can be null) + */ + const int32_t *scene; +} lb_get_fund_holdings_options_t; + +/** + * Options for the fund stock-holdings (reverse) request. + */ +typedef struct lb_get_fund_stock_holdings_options_t { + /** + * Maximum number of stocks to return (can be null) + */ + const int32_t *limit; +} lb_get_fund_stock_holdings_options_t; + +/** + * Options for the fund positions overview request. + */ +typedef struct lb_fund_positions_options_t { + /** + * Account channel (can be null) + */ + const char *account_channel; + /** + * Account id (can be null) + */ + const int64_t *aaid; +} lb_fund_positions_options_t; + +/** + * Options for a single fund position detail request. + */ +typedef struct lb_get_fund_position_options_t { + /** + * Account channel (can be null) + */ + const char *account_channel; + /** + * Account id (can be null) + */ + const int64_t *aaid; + /** + * Range start (can be null) + */ + const char *start; + /** + * Range end (can be null) + */ + const char *end; +} lb_get_fund_position_options_t; + +/** + * Options for a single fund position cumulative-profit request. + */ +typedef struct lb_get_fund_position_profits_options_t { + /** + * Account channel (can be null) + */ + const char *account_channel; + /** + * Account id (can be null) + */ + const int64_t *aaid; + /** + * Range start (can be null) + */ + const char *start; + /** + * Range end (can be null) + */ + const char *end; + /** + * Page number (can be null) + */ + const int32_t *page; + /** + * Page size (can be null) + */ + const int32_t *size; +} lb_get_fund_position_profits_options_t; + +/** + * Options for a single fund position dividend request. + */ +typedef struct lb_get_fund_position_dividends_options_t { + /** + * Account channel (can be null) + */ + const char *account_channel; + /** + * Account id (can be null) + */ + const int64_t *aaid; + /** + * Currency (can be null) + */ + const char *currency; + /** + * Range start (unix seconds) (can be null) + */ + const int64_t *start; + /** + * Range end (unix seconds) (can be null) + */ + const int64_t *end; + /** + * Page number (can be null) + */ + const int32_t *page; + /** + * Page size (can be null) + */ + const int32_t *size; +} lb_get_fund_position_dividends_options_t; + +/** + * Options for the fund orders list request. + */ +typedef struct lb_get_fund_orders_options_t { + /** + * Filter by fund counter ids (can be null) + */ + const char *const *counter_ids; + /** + * Number of fund counter ids + */ + uintptr_t num_counter_ids; + /** + * Filter by actions (comma-separated) (can be null) + */ + const char *actions; + /** + * Filter by states (comma-separated) (can be null) + */ + const char *states; + /** + * Filter by currency (can be null) + */ + const char *currency; + /** + * Range start (unix seconds) (can be null) + */ + const int64_t *start; + /** + * Range end (unix seconds) (can be null) + */ + const int64_t *end; + /** + * Page number (can be null) + */ + const int32_t *page; + /** + * Page size (can be null) + */ + const int32_t *size; +} lb_get_fund_orders_options_t; + +/** + * Options for the fund transactions (cash-flow) list request. + */ +typedef struct lb_get_fund_transactions_options_t { + /** + * Account channel (can be null) + */ + const char *account_channel; + /** + * Business type (can be null) + */ + const char *business_type; + /** + * Category (can be null) + */ + const char *category; + /** + * Currencies (comma-separated) (can be null) + */ + const char *currencies; + /** + * Range start (unix seconds) (can be null) + */ + const int64_t *start; + /** + * Range end (unix seconds) (can be null) + */ + const int64_t *end; + /** + * Page number (can be null) + */ + const int32_t *page; + /** + * Page size (can be null) + */ + const int32_t *size; +} lb_get_fund_transactions_options_t; + +/** + * Options for validating a fund order. + */ +typedef struct lb_validate_fund_order_options_t { + /** + * Fund counter id + */ + const char *counter_id; + /** + * Action (buy/sell) + */ + const char *action; + /** + * Currency + */ + const char *currency; + /** + * Amount (for amount-based orders) (can be null) + */ + const char *amount; + /** + * Units (for unit-based orders) (can be null) + */ + const char *units; + /** + * Dividend option (can be null) + */ + const int32_t *dividend_option; + /** + * Fund source (can be null) + */ + const int32_t *fund_source; + /** + * Account channel (can be null) + */ + const char *account_channel; +} lb_validate_fund_order_options_t; + +/** + * Options for submitting a fund order. + */ +typedef struct lb_submit_fund_order_options_t { + /** + * Fund counter id + */ + const char *counter_id; + /** + * Action (buy/sell) + */ + const char *action; + /** + * Currency + */ + const char *currency; + /** + * Amount (for amount-based orders) (can be null) + */ + const char *amount; + /** + * Units (for unit-based orders) (can be null) + */ + const char *units; + /** + * Dividend option (can be null) + */ + const int32_t *dividend_option; + /** + * Fee (can be null) + */ + const char *fee; + /** + * Whether to sell all (can be null) + */ + const bool *is_sell_all; + /** + * Remark (can be null) + */ + const char *remark; + /** + * Trade method (can be null) + */ + const int32_t *trade_method; +} lb_submit_fund_order_options_t; + /** * Grid trading rule — parameters for submit / replace. */ @@ -5724,9 +6074,9 @@ typedef struct lb_cash_flow_t { */ typedef struct lb_fund_position_t { /** - * Fund ISIN code + * Fund counter id (the ISIN is the last `/`-separated segment) */ - const char *symbol; + const char *counter_id; /** * Current equity */ @@ -6744,148 +7094,1966 @@ typedef struct lb_trigger_order_t { */ const char *currency; /** - * Latest quote price (can be null) + * Latest quote price (can be null) + */ + const struct lb_decimal_t *last_done; + /** + * Last updated time (unix timestamp, maybe null) + */ + const int64_t *updated_at; + /** + * Time in force (`0` = Day, `1` = GTC, `6` = GTD) + */ + int32_t time_in_force; + /** + * Expiry date (`YYYY-MM-DD`, GTD) + */ + const char *gtd; + /** + * Trigger time (unix timestamp, maybe null) + */ + const int64_t *trigger_at; + /** + * Conditional trigger status + */ + int32_t trigger_status; +} lb_trigger_order_t; + +/** + * A price-step (bid-size) rule entry from the symbol-info response. + */ +typedef struct lb_grid_bid_size_t { + /** + * Range start price (inclusive) (can be null) + */ + const struct lb_decimal_t *str_proceed; + /** + * Range end price (can be null) + */ + const struct lb_decimal_t *end_proceed; + /** + * Price step within the range (can be null) + */ + const struct lb_decimal_t *bid_size; +} lb_grid_bid_size_t; + +/** + * Channel / authorization info nested in the symbol-info response. + */ +typedef struct lb_grid_channel_info_t { + /** + * Whether the strategy compliance authorization has been granted + */ + bool strategy_granted; + /** + * Whether the RTH toggle is supported + */ + bool support_rth; + /** + * Trading currency + */ + const char *currency; + /** + * Supported settlement currencies + */ + const char *const *settlement_currency; + /** + * Number of supported settlement currencies + */ + uintptr_t num_settlement_currency; +} lb_grid_channel_info_t; + +/** + * Security (symbol) info used to build a grid order. + */ +typedef struct lb_grid_symbol_info_t { + /** + * Security name + */ + const char *name; + /** + * Latest quote price (can be null) + */ + const struct lb_decimal_t *last_done; + /** + * Board lot size (can be null) + */ + const struct lb_decimal_t *lot_size; + /** + * Buy-side board lot size (can be null) + */ + const struct lb_decimal_t *buy_lot_size; + /** + * Sell-side board lot size (can be null) + */ + const struct lb_decimal_t *sell_lot_size; + /** + * Price-step (bid-size) rule table + */ + const struct lb_grid_bid_size_t *bid_sizes; + /** + * Number of bid-size entries + */ + uintptr_t num_bid_sizes; + /** + * Channel / authorization info (strategy grant, RTH, currencies) + */ + struct lb_grid_channel_info_t channel_info; +} lb_grid_symbol_info_t; + +/** + * Response for get grid trading orders (list) request + */ +typedef struct lb_grid_orders_response_t { + /** + * Grid orders + */ + const struct lb_grid_order_t *grid_order; + /** + * Number of grid orders + */ + uintptr_t num_grid_order; + /** + * Whether there are more pages + */ + bool has_more; +} lb_grid_orders_response_t; + +/** + * Response for get grid trading trigger history request + */ +typedef struct lb_grid_trigger_history_response_t { + /** + * Trigger history entries + */ + const struct lb_trigger_order_t *trigger_orders; + /** + * Number of trigger history entries + */ + uintptr_t num_trigger_orders; + /** + * Whether there are more pages + */ + bool has_more; +} lb_grid_trigger_history_response_t; + +/** + * Fund analysis (level 1). + */ +typedef struct lb_fund_analysis_t { + /** + * Actual period + */ + int32_t actual_period; + /** + * Cost level (JSON string) + */ + const char *cost_level; + /** + * Return ability (JSON string) + */ + const char *return_ability; + /** + * Risk ability (JSON string) + */ + const char *risk_ability; + /** + * Updated at + */ + const char *updated_at; + /** + * Value for money (JSON string) + */ + const char *value_for_money; + /** + * Whether visible + */ + bool visible; +} lb_fund_analysis_t; + +/** + * Fund analysis detail (level 2). + */ +typedef struct lb_fund_analysis_detail_t { + /** + * Actual period + */ + int32_t actual_period; + /** + * Available periods + */ + const int32_t *available_periods; + /** + * Number of available periods + */ + uintptr_t num_available_periods; + /** + * Cost level (JSON string) + */ + const char *cost_level; + /** + * Return ability (JSON string) + */ + const char *return_ability; + /** + * Risk ability (JSON string) + */ + const char *risk_ability; + /** + * Updated at + */ + const char *updated_at; + /** + * Value for money (JSON string) + */ + const char *value_for_money; + /** + * Whether visible + */ + bool visible; +} lb_fund_analysis_detail_t; + +/** + * A fund annual return entry. + */ +typedef struct lb_fund_annual_return_t { + /** + * Change percent + */ + const char *change_percent; + /** + * Year + */ + int32_t year; +} lb_fund_annual_return_t; + +/** + * A single holding entry inside a fund's asset allocation. + */ +typedef struct lb_fund_asset_allocation_item_t { + /** + * Security code + */ + const char *code; + /** + * Security counter id + */ + const char *counter_id; + /** + * Name + */ + const char *name; + /** + * Position ratio + */ + const char *position_ratio; +} lb_fund_asset_allocation_item_t; + +/** + * A fund's asset allocation. + */ +typedef struct lb_fund_asset_allocation_t { + /** + * Asset type + */ + int32_t asset_type; + /** + * Allocation entries + */ + const struct lb_fund_asset_allocation_item_t *lists; + /** + * Number of allocation entries + */ + uintptr_t num_lists; + /** + * Report date + */ + const char *report_date; +} lb_fund_asset_allocation_t; + +/** + * A fund entry in the fund list. + */ +typedef struct lb_fund_brief_t { + /** + * Asset class + */ + int32_t asset_class; + /** + * Asset class name + */ + const char *asset_class_name; + /** + * Fund code + */ + const char *code; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Currency + */ + const char *currency; + /** + * Description + */ + const char *description; + /** + * Earning rate + */ + const char *earning_rate; + /** + * Whether the user is holding this fund + */ + bool holding; + /** + * ISIN + */ + const char *isin; + /** + * Fund name + */ + const char *name; + /** + * Product + */ + const char *product; + /** + * Minimum purchase amount + */ + const char *purchase_amount; + /** + * Recommendation text + */ + const char *recommendation_text; + /** + * Risk level + */ + int32_t risk_level; + /** + * Risk level name + */ + const char *risk_level_name; + /** + * Time interval of the earning rate + */ + const char *time_interval; + /** + * Unit value + */ + const char *unit_value; +} lb_fund_brief_t; + +/** + * A dated value point. + */ +typedef struct lb_fund_dated_value_t { + /** + * Date (unix seconds) + */ + int64_t date; + /** + * Value + */ + const char *value; +} lb_fund_dated_value_t; + +/** + * Fund detail. + */ +typedef struct lb_fund_detail_t { + /** + * Additional purchase amount + */ + const char *additional_purchase_amount; + /** + * Affirm day + */ + int32_t affirm_day; + /** + * Amount affirm day + */ + const char *amount_affirm_day; + /** + * Asset allocation, or null if unavailable + */ + const struct lb_fund_asset_allocation_t *asset_allocation; + /** + * Asset class + */ + int32_t asset_class; + /** + * Asset class name + */ + const char *asset_class_name; + /** + * Bill purchase rate + */ + const char *bill_purchase_rate; + /** + * Channel + */ + const char *channel; + /** + * Close period + */ + const char *close_period; + /** + * Fund code + */ + const char *code; + /** + * Currency + */ + const char *currency; + /** + * Cut off time + */ + const char *cut_off_time; + /** + * Whether it is a derivative + */ + bool derivatives; + /** + * Done day + */ + int32_t done_day; + /** + * Excess return fee + */ + const char *excess_return_fee; + /** + * GST rate + */ + const char *gst_rate; + /** + * Introduction + */ + const char *introduce; + /** + * Whether it is a cash-plus fund + */ + bool is_cash_plus; + /** + * Whether it is a complex product + */ + bool is_complex; + /** + * Whether it is a new cash-plus fund + */ + bool is_new_cash_plus; + /** + * Whether it is a Yinghebao fund + */ + bool is_yinghebao; + /** + * ISIN + */ + const char *isin; + /** + * Management rate + */ + const char *manage_rate; + /** + * Manager + */ + const char *manager; + /** + * Minimum holding cash + */ + const char *min_hold_cash; + /** + * Minimum holding share + */ + const char *min_hold_share; + /** + * Minimum sell share + */ + const char *min_sell_share; + /** + * Month raise day + */ + const char *month_raise_day; + /** + * Fund name + */ + const char *name; + /** + * Net value deadline + */ + const char *nav_deadline; + /** + * Whether it is no-load + */ + bool no_load; + /** + * Open date + */ + const char *open_date; + /** + * Open period + */ + const char *open_period; + /** + * Product + */ + const char *product; + /** + * Product information locals + */ + const char *product_information_locals; + /** + * Profile + */ + const char *profile; + /** + * Whether purchasable + */ + int32_t purchasable; + /** + * Purchase affirm day + */ + const char *purchase_affirm_day; + /** + * Minimum purchase amount + */ + const char *purchase_amount; + /** + * Purchase rate + */ + const char *purchase_rate; + /** + * Rating + */ + int32_t rating; + /** + * Whether redeemable + */ + int32_t redeemable; + /** + * Redemption advance day + */ + const char *redemption_advance_day; + /** + * Redemption amount + */ + const char *redemption_amount; + /** + * Redemption close period text + */ + const char *redemption_close_period_shows; + /** + * Redemption done day + */ + const char *redemption_done_day; + /** + * Redemption open day text + */ + const char *redemption_open_day_shows; + /** + * Risk level + */ + int32_t risk_level; + /** + * Risk level name + */ + const char *risk_level_name; + /** + * Verify status + */ + int32_t verify_status; + /** + * Whether it is a virtual currency fund + */ + bool virtual_currency; + /** + * Year to date yield + */ + const char *year_to_date_yield; + /** + * Year to date yield type + */ + int32_t ytd_yield_type; +} lb_fund_detail_t; + +/** + * A cash dividend record for a held fund. + */ +typedef struct lb_fund_dividend_t { + /** + * Amount + */ + const char *amount; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Currency + */ + const char *currency; + /** + * Date (unix seconds) + */ + int64_t date; + /** + * Dividend method + */ + const char *div_method; + /** + * Fund name + */ + const char *name; +} lb_fund_dividend_t; + +/** + * The user's dividend records for a held fund. + */ +typedef struct lb_fund_dividends_t { + /** + * Currency + */ + const char *currency; + /** + * Dividend records + */ + const struct lb_fund_dividend_t *div_cash_infos; + /** + * Number of dividend records + */ + uintptr_t num_div_cash_infos; + /** + * Latest dividend date (unix seconds) + */ + int64_t lastest_date; + /** + * Total cash dividend + */ + const char *total_div_cash; +} lb_fund_dividends_t; + +/** + * Fund list filter options. Each list holds JSON-encoded option objects. + */ +typedef struct lb_fund_filters_t { + /** + * Asset class options (JSON strings) + */ + const char *const *asset_class; + /** + * Number of asset class options + */ + uintptr_t num_asset_class; + /** + * Company options (JSON strings) + */ + const char *const *company; + /** + * Number of company options + */ + uintptr_t num_company; + /** + * Currency options (JSON strings) + */ + const char *const *currency; + /** + * Number of currency options + */ + uintptr_t num_currency; + /** + * Industry category options (JSON strings) + */ + const char *const *industry_category_name; + /** + * Number of industry category options + */ + uintptr_t num_industry_category_name; + /** + * Risk level options (JSON strings) + */ + const char *const *risk_level; + /** + * Number of risk level options + */ + uintptr_t num_risk_level; +} lb_fund_filters_t; + +/** + * A single fund holding (top-10 holdings). + */ +typedef struct lb_fund_holding_t { + /** + * Bond type + */ + const char *bond_type; + /** + * Bond type name + */ + const char *bond_type_name; + /** + * Country name + */ + const char *country_name; + /** + * Holding type + */ + const char *holding_type; + /** + * Industry name + */ + const char *industry_name; + /** + * Market value + */ + const char *market_value; + /** + * Maturity date + */ + const char *maturity_date; + /** + * Name + */ + const char *name; + /** + * Share change + */ + const char *share_change; + /** + * Share change percent + */ + const char *share_change_percent; + /** + * Shares + */ + const char *shares; + /** + * Weighting + */ + const char *weighting; +} lb_fund_holding_t; + +/** + * A fund's top-10 holdings. + */ +typedef struct lb_fund_holdings_t { + /** + * Holding entries + */ + const struct lb_fund_holding_t *holdings; + /** + * Number of holding entries + */ + uintptr_t num_holdings; + /** + * Report date + */ + const char *report_date; + /** + * Total weighting + */ + const char *weighting; +} lb_fund_holdings_t; + +/** + * A named contrast performance series. + */ +typedef struct lb_fund_named_contrast_t { + /** + * Series name + */ + const char *name; + /** + * Performance points (JSON strings) + */ + const char *const *performances; + /** + * Number of performance points + */ + uintptr_t num_performances; +} lb_fund_named_contrast_t; + +/** + * A fund net-asset-value data point (latest / historical). + */ +typedef struct lb_fund_nav_value_t { + /** + * Net value change + */ + const char *change; + /** + * Net value change percent + */ + const char *change_percent; + /** + * Formatted change percent + */ + const char *change_percent_format; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Fund name + */ + const char *counter_name; + /** + * Currency + */ + const char *currency; + /** + * Formatted date + */ + const char *date_format; + /** + * ISIN + */ + const char *isin; + /** + * Last update time (unix seconds) + */ + int64_t last_update_time; + /** + * Net value + */ + const char *value; + /** + * Formatted net value + */ + const char *value_format; +} lb_fund_nav_value_t; + +/** + * A fund order (list view). + */ +typedef struct lb_fund_order_t { + /** + * Action (buy/sell) + */ + const char *action; + /** + * Amount + */ + const char *amount; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Created at (unix seconds) + */ + int64_t created_at; + /** + * Currency + */ + const char *currency; + /** + * Fund name + */ + const char *fund_name; + /** + * Order id + */ + int64_t id; + /** + * Whether it is an auto (DCA) order + */ + bool is_auto; + /** + * Net worth + */ + const char *net_worth; + /** + * Product type + */ + const char *product_type; + /** + * State + */ + const char *state; + /** + * State description + */ + const char *state_desc; + /** + * Units + */ + const char *units; +} lb_fund_order_t; + +/** + * A keyword block in a fund order detail. + */ +typedef struct lb_fund_order_keyword_t { + /** + * Content + */ + const char *content; + /** + * Group + */ + const char *group; + /** + * Key + */ + const char *key; + /** + * Line strategy + */ + const char *line_strategy; + /** + * Title + */ + const char *title; +} lb_fund_order_keyword_t; + +/** + * The full information of a fund order. + */ +typedef struct lb_fund_order_info_t { + /** + * Account id + */ + int64_t aaid; + /** + * Account channel + */ + const char *account_channel; + /** + * Action (buy/sell) + */ + const char *action; + /** + * Amount + */ + const char *amount; + /** + * Channel + */ + const char *channel; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Created at (unix seconds) + */ + int64_t created_at; + /** + * Currency + */ + const char *currency; + /** + * Dividend option + */ + const char *dividend_option; + /** + * Equity time (unix seconds) + */ + int64_t eq_at; + /** + * Fee + */ + const char *fee; + /** + * Fund name + */ + const char *fund_name; + /** + * Fund source + */ + const char *fund_source; + /** + * Histories + */ + const char *histories; + /** + * Order id + */ + int64_t id; + /** + * Message + */ + const char *message; + /** + * Net worth + */ + const char *net_worth; + /** + * Price time (unix seconds) + */ + int64_t price_at; + /** + * Processed at (unix seconds) + */ + int64_t processed_at; + /** + * Product type + */ + const char *product_type; + /** + * Whether repurchaseable + */ + bool repurchaseable; + /** + * Sale proceeds + */ + const char *sale_proceeds; + /** + * Sales charge + */ + const char *sales_charge; + /** + * Sales price + */ + const char *sales_price; + /** + * Sales unit + */ + const char *sales_unit; + /** + * State + */ + const char *state; + /** + * State description + */ + const char *state_desc; + /** + * Status + */ + int32_t status; + /** + * Extended status + */ + int32_t status_ex; + /** + * T+ description + */ + const char *t_description; + /** + * Time partition + */ + const char *time_partition; + /** + * Total amount + */ + const char *total_amount; + /** + * Transaction at (unix seconds) + */ + int64_t transaction_at; + /** + * Units + */ + const char *units; + /** + * Withdraw at (unix seconds) + */ + int64_t withdraw_at; + /** + * Whether withdrawable + */ + bool withdrawable; +} lb_fund_order_info_t; + +/** + * A processing stage in a fund order detail. + */ +typedef struct lb_fund_order_stage_t { + /** + * Description + */ + const char *desc; + /** + * Key + */ + const char *key; + /** + * Link + */ + const char *link; + /** + * Link text + */ + const char *link_text; + /** + * Progress + */ + const char *progress; + /** + * Stage + */ + const char *stage; +} lb_fund_order_stage_t; + +/** + * Fund order detail. + */ +typedef struct lb_fund_order_detail_t { + /** + * Keyword blocks + */ + const struct lb_fund_order_keyword_t *keywords; + /** + * Number of keyword blocks + */ + uintptr_t num_keywords; + /** + * The order + */ + const struct lb_fund_order_info_t *order; + /** + * Processing stages + */ + const struct lb_fund_order_stage_t *stages; + /** + * Number of processing stages + */ + uintptr_t num_stages; +} lb_fund_order_detail_t; + +/** + * The result of submitting a fund order. + */ +typedef struct lb_fund_order_submit_response_t { + /** + * Action (buy/sell) + */ + const char *action; + /** + * Amount + */ + const char *amount; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Created at (unix seconds) + */ + int64_t created_at; + /** + * Fund name + */ + const char *fund_name; + /** + * Order id + */ + int64_t id; + /** + * Message + */ + const char *msg; + /** + * Status + */ + int32_t status; + /** + * Units + */ + const char *units; +} lb_fund_order_submit_response_t; + +/** + * The result of validating a fund order. + */ +typedef struct lb_fund_order_validation_t { + /** + * Auth token to carry into submit + */ + const char *auth_token; + /** + * Risk-assessment eval address + */ + const char *eval_address; + /** + * Fund risk level + */ + int32_t fund_risk_level; + /** + * Message + */ + const char *msg; + /** + * User PI status + */ + int32_t user_pi; + /** + * User risk level + */ + int32_t user_risk_level; +} lb_fund_order_validation_t; + +/** + * A fund's detailed performance figures. + */ +typedef struct lb_fund_performance_t { + /** + * Annualized return (5y) + */ + const char *annualized_return_five; + /** + * Annualized return (1y) + */ + const char *annualized_return_one; + /** + * Annualized return (10y) + */ + const char *annualized_return_ten; + /** + * Annualized return (3y) + */ + const char *annualized_return_three; + /** + * Annualized return (2y) + */ + const char *annualized_return_two; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Fund name + */ + const char *fund_name; + /** + * Rank (5y) + */ + int32_t performance_rank_five_years; + /** + * Rank (1d) + */ + int32_t performance_rank_one_day; + /** + * Rank (1m) + */ + int32_t performance_rank_one_month; + /** + * Rank (1w) + */ + int32_t performance_rank_one_week; + /** + * Rank (1y) + */ + int32_t performance_rank_one_year; + /** + * Rank (6m) + */ + int32_t performance_rank_six_months; + /** + * Rank (10y) + */ + int32_t performance_rank_ten_years; + /** + * Rank (3m) + */ + int32_t performance_rank_three_months; + /** + * Rank (3y) + */ + int32_t performance_rank_three_years; + /** + * Rank (2y) + */ + int32_t performance_rank_two_years; + /** + * Rank (ytd) + */ + int32_t performance_rank_ytd; + /** + * Return (5y) + */ + const char *performance_return_five_years; + /** + * Return (1d) + */ + const char *performance_return_one_day; + /** + * Return (1m) + */ + const char *performance_return_one_month; + /** + * Return (1w) + */ + const char *performance_return_one_week; + /** + * Return (1y) + */ + const char *performance_return_one_year; + /** + * Return (6m) + */ + const char *performance_return_six_months; + /** + * Return (10y) + */ + const char *performance_return_ten_years; + /** + * Return (3m) + */ + const char *performance_return_three_months; + /** + * Return (3y) + */ + const char *performance_return_three_years; + /** + * Return (2y) + */ + const char *performance_return_two_years; + /** + * Return (ytd) + */ + const char *performance_return_ytd; + /** + * Total peers (5y) + */ + int32_t performance_total_five_years; + /** + * Total peers (1d) + */ + int32_t performance_total_one_day; + /** + * Total peers (1m) + */ + int32_t performance_total_one_month; + /** + * Total peers (1w) + */ + int32_t performance_total_one_week; + /** + * Total peers (1y) + */ + int32_t performance_total_one_year; + /** + * Total peers (6m) + */ + int32_t performance_total_six_months; + /** + * Total peers (10y) + */ + int32_t performance_total_ten_years; + /** + * Total peers (3m) + */ + int32_t performance_total_three_months; + /** + * Total peers (3y) + */ + int32_t performance_total_three_years; + /** + * Total peers (2y) + */ + int32_t performance_total_two_years; + /** + * Total peers (ytd) + */ + int32_t performance_total_ytd; + /** + * Seven days annualized + */ + const char *seven_days_annualized; + /** + * Ten thousand price + */ + const char *ten_thousand_price; + /** + * Update time (unix seconds) + */ + int64_t update_time; +} lb_fund_performance_t; + +/** + * Fund performance comparison. + */ +typedef struct lb_fund_performance_comparison_t { + /** + * Contrast performance series + */ + const struct lb_fund_named_contrast_t *contrast_performances; + /** + * Number of contrast performance series + */ + uintptr_t num_contrast_performances; + /** + * Fund performances (JSON strings) + */ + const char *const *fund_performances; + /** + * Number of fund performances + */ + uintptr_t num_fund_performances; +} lb_fund_performance_comparison_t; + +/** + * A recent performance point used by the hot-fund list. + */ +typedef struct lb_fund_performance_point_t { + /** + * Date + */ + const char *date; + /** + * Last done value + */ + const char *last_done; +} lb_fund_performance_point_t; + +/** + * Detail values of a single fund position. + */ +typedef struct lb_fund_position_detail_values_t { + /** + * Amount + */ + const char *amount; + /** + * Currency + */ + const char *currency; + /** + * Holding cost + */ + const char *holding_cost; + /** + * Holding profit + */ + const char *holding_profit; + /** + * Holding profit rate + */ + const char *holding_profit_rate; + /** + * Holding units + */ + const char *holding_units; + /** + * Holding value + */ + const char *holding_value; + /** + * Pending buy value + */ + const char *pending_buy_value; + /** + * Pending sell value + */ + const char *pending_sell_value; + /** + * Accumulated profit (to date) + */ + const char *profit_amount_accum_td; + /** + * Accumulated profit rate (to date) + */ + const char *profit_amount_accum_td_rate; + /** + * Recent profit + */ + const char *recent_profit; + /** + * Recent trading day (unix seconds) + */ + int64_t recent_tradingday; + /** + * Recent unit value + */ + const char *recent_unit_value; + /** + * Sold pending-confirm units + */ + const char *sold_pending_confirm_units; +} lb_fund_position_detail_values_t; + +/** + * A fund unit-value point (position view). + */ +typedef struct lb_fund_unit_value_t { + /** + * Date (unix seconds) + */ + int64_t date; + /** + * Day increase rate + */ + const char *day_increase_rate; + /** + * Total value + */ + const char *total_value; + /** + * Unit value + */ + const char *unit_value; +} lb_fund_unit_value_t; + +/** + * Detail of a single fund position. + */ +typedef struct lb_fund_position_detail_t { + /** + * Detail values + */ + const struct lb_fund_position_detail_values_t *detail_values; + /** + * Accumulated profit series + */ + const struct lb_fund_dated_value_t *sum_profit; + /** + * Number of accumulated-profit points + */ + uintptr_t num_sum_profit; + /** + * Unit value series + */ + const struct lb_fund_unit_value_t *ut_value; + /** + * Number of unit-value points + */ + uintptr_t num_ut_value; +} lb_fund_position_detail_t; + +/** + * A single fund position held by the user. + */ +typedef struct lb_fund_position_item_t { + /** + * Holding amount + */ + const char *amount; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Currency + */ + const char *currency; + /** + * Frozen units + */ + const char *freeze_units; + /** + * Holding profit + */ + const char *holding_profit; + /** + * Holding units + */ + const char *holding_units; + /** + * Fund name + */ + const char *name; + /** + * Recent profit + */ + const char *recent_profit; + /** + * Recent trading day (unix seconds) + */ + int64_t recent_trading_day; + /** + * Accumulated recent profit + */ + const char *sum_recent_profit; +} lb_fund_position_item_t; + +/** + * A held-fund net-value point (position view). + */ +typedef struct lb_fund_position_nav_t { + /** + * Net value change + */ + const char *change; + /** + * Net value change percent + */ + const char *change_percent; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Fund name + */ + const char *counter_name; + /** + * Last update time (unix seconds) + */ + int64_t last_update_time; + /** + * Net value + */ + const char *value; +} lb_fund_position_nav_t; + +/** + * Performance figures for a held fund. + */ +typedef struct lb_fund_position_performance_t { + /** + * Annualized return (5y) + */ + const char *annualized_return_five; + /** + * Annualized return (1y) + */ + const char *annualized_return_one; + /** + * Annualized return (10y) + */ + const char *annualized_return_ten; + /** + * Annualized return (3y) + */ + const char *annualized_return_three; + /** + * Annualized return (2y) + */ + const char *annualized_return_two; + /** + * Fund counter id + */ + const char *counter_id; + /** + * Fund name + */ + const char *fund_name; + /** + * Return (5y) + */ + const char *performance_return_five_years; + /** + * Return (1d) + */ + const char *performance_return_one_day; + /** + * Return (1m) + */ + const char *performance_return_one_month; + /** + * Return (1w) + */ + const char *performance_return_one_week; + /** + * Return (1y) + */ + const char *performance_return_one_year; + /** + * Return (6m) + */ + const char *performance_return_six_months; + /** + * Return (10y) + */ + const char *performance_return_ten_years; + /** + * Return (3m) + */ + const char *performance_return_three_months; + /** + * Return (3y) + */ + const char *performance_return_three_years; + /** + * Return (2y) + */ + const char *performance_return_two_years; + /** + * Return (ytd) + */ + const char *performance_return_ytd; + /** + * Update time (unix seconds) + */ + int64_t update_time; +} lb_fund_position_performance_t; + +/** + * The user's cumulative profit for a held fund. + */ +typedef struct lb_fund_position_profits_t { + /** + * Currency + */ + const char *currency; + /** + * Profit series + */ + const struct lb_fund_dated_value_t *history_value; + /** + * Number of profit points + */ + uintptr_t num_history_value; + /** + * Last update time (unix seconds) + */ + int64_t last_update_time; + /** + * Total profit + */ + const char *sum_profit; +} lb_fund_position_profits_t; + +/** + * The user's fund positions overview. + */ +typedef struct lb_fund_positions_t { + /** + * Account channel + */ + const char *account_channel; + /** + * Position entries + */ + const struct lb_fund_position_item_t *list; + /** + * Number of position entries + */ + uintptr_t num_list; + /** + * Pending buy orders amount + */ + const char *pending_buy_orders; + /** + * Recent trading day (unix seconds) + */ + int64_t recent_trading_day; + /** + * Sold pending credit orders amount + */ + const char *sold_pending_credit_orders; +} lb_fund_positions_t; + +/** + * A fund quarterly return entry. + */ +typedef struct lb_fund_quarterly_return_t { + /** + * Change percent + */ + const char *change_percent; + /** + * Quarter + */ + int32_t quarter; + /** + * Year + */ + int32_t year; +} lb_fund_quarterly_return_t; + +/** + * A stock held by the fund (reverse lookup). + */ +typedef struct lb_fund_stock_holding_t { + /** + * Stock code + */ + const char *code; + /** + * Stock counter id + */ + const char *counter_id; + /** + * Currency + */ + const char *currency; + /** + * Stock name + */ + const char *name; + /** + * Position ratio + */ + const char *position_ratio; + /** + * Report date + */ + const char *report_date; +} lb_fund_stock_holding_t; + +/** + * A fund transaction / cash-flow record. + */ +typedef struct lb_fund_transaction_t { + /** + * Amount + */ + const char *amount; + /** + * Category + */ + const char *category; + /** + * Created at (unix seconds) + */ + int64_t created_at; + /** + * Currency + */ + const char *currency; + /** + * Description + */ + const char *description; + /** + * Detail created at (unix seconds) + */ + int64_t detail_created_at; + /** + * Detail type + */ + const char *detail_type; + /** + * Done at (unix seconds) + */ + int64_t done_at; + /** + * Quantity description + */ + const char *quantity_description; + /** + * Redirect page */ - const struct lb_decimal_t *last_done; + const char *redirect_page; /** - * Last updated time (unix timestamp, maybe null) + * Redirect page (v2) */ - const int64_t *updated_at; + const char *redirect_page_v2; /** - * Time in force (`0` = Day, `1` = GTC, `6` = GTD) + * Reference number */ - int32_t time_in_force; + const char *ref_no; /** - * Expiry date (`YYYY-MM-DD`, GTD) + * Stock quantity */ - const char *gtd; + const char *stock_quantity; /** - * Trigger time (unix timestamp, maybe null) + * Transaction type */ - const int64_t *trigger_at; + const char *tx_type; /** - * Conditional trigger status + * Type name */ - int32_t trigger_status; -} lb_trigger_order_t; + const char *type_name; +} lb_fund_transaction_t; /** - * A price-step (bid-size) rule entry from the symbol-info response. + * A benchmark contrast series in a fund trend chart. */ -typedef struct lb_grid_bid_size_t { +typedef struct lb_fund_trend_contrast_t { /** - * Range start price (inclusive) (can be null) + * Benchmark name */ - const struct lb_decimal_t *str_proceed; + const char *benchmark_name; /** - * Range end price (can be null) + * Performance points (JSON strings) */ - const struct lb_decimal_t *end_proceed; + const char *const *performances; /** - * Price step within the range (can be null) + * Number of performance points */ - const struct lb_decimal_t *bid_size; -} lb_grid_bid_size_t; + uintptr_t num_performances; +} lb_fund_trend_contrast_t; /** - * Channel / authorization info nested in the symbol-info response. + * Fund trend chart. */ -typedef struct lb_grid_channel_info_t { +typedef struct lb_fund_trend_t { /** - * Whether the strategy compliance authorization has been granted + * Actual period */ - bool strategy_granted; + int32_t actual_period; /** - * Whether the RTH toggle is supported + * Available periods */ - bool support_rth; + const int32_t *available_periods; /** - * Trading currency + * Number of available periods */ - const char *currency; + uintptr_t num_available_periods; /** - * Supported settlement currencies + * Category average performances (JSON strings) */ - const char *const *settlement_currency; + const char *const *category_average_performances; /** - * Number of supported settlement currencies + * Number of category average performances */ - uintptr_t num_settlement_currency; -} lb_grid_channel_info_t; + uintptr_t num_category_average_performances; + /** + * Benchmark contrast performances, or null if unavailable + */ + const struct lb_fund_trend_contrast_t *contrast_performances; + /** + * Fund performances (JSON strings) + */ + const char *const *fund_performances; + /** + * Number of fund performances + */ + uintptr_t num_fund_performances; +} lb_fund_trend_t; /** - * Security (symbol) info used to build a grid order. + * A hot-selling fund entry. */ -typedef struct lb_grid_symbol_info_t { - /** - * Security name - */ - const char *name; +typedef struct lb_hot_fund_t { /** - * Latest quote price (can be null) + * Asset class */ - const struct lb_decimal_t *last_done; + int32_t asset_class; /** - * Board lot size (can be null) + * Asset class name */ - const struct lb_decimal_t *lot_size; + const char *asset_class_name; /** - * Buy-side board lot size (can be null) + * Fund counter id */ - const struct lb_decimal_t *buy_lot_size; + const char *counter_id; /** - * Sell-side board lot size (can be null) + * Currency */ - const struct lb_decimal_t *sell_lot_size; + const char *currency; /** - * Price-step (bid-size) rule table + * Earning rate */ - const struct lb_grid_bid_size_t *bid_sizes; + const char *earning_rate; /** - * Number of bid-size entries + * Recent performance points */ - uintptr_t num_bid_sizes; + const struct lb_fund_performance_point_t *fund_performances; /** - * Channel / authorization info (strategy grant, RTH, currencies) + * Number of recent performance points */ - struct lb_grid_channel_info_t channel_info; -} lb_grid_symbol_info_t; - -/** - * Response for get grid trading orders (list) request - */ -typedef struct lb_grid_orders_response_t { + uintptr_t num_fund_performances; /** - * Grid orders + * Fund name */ - const struct lb_grid_order_t *grid_order; + const char *name; /** - * Number of grid orders + * Minimum purchase amount */ - uintptr_t num_grid_order; + const char *purchase_amount; /** - * Whether there are more pages + * Recommendation text */ - bool has_more; -} lb_grid_orders_response_t; - -/** - * Response for get grid trading trigger history request - */ -typedef struct lb_grid_trigger_history_response_t { + const char *recommendation_text; /** - * Trigger history entries + * Risk level */ - const struct lb_trigger_order_t *trigger_orders; + int32_t risk_level; /** - * Number of trigger history entries + * Risk level name */ - uintptr_t num_trigger_orders; + const char *risk_level_name; /** - * Whether there are more pages + * Time interval of the earning rate */ - bool has_more; -} lb_grid_trigger_history_response_t; + const char *time_interval; +} lb_hot_fund_t; /** * Options for estimate maximum purchase quantity @@ -12508,6 +14676,288 @@ int64_t lb_error_code(const struct lb_error_t *error); enum lb_error_kind_t lb_error_kind(const struct lb_error_t *error); +const struct lb_fund_context_t *lb_fund_context_new(const struct lb_config_t *config); + +void lb_fund_context_retain(const struct lb_fund_context_t *ctx); + +void lb_fund_context_release(const struct lb_fund_context_t *ctx); + +/** + * Get the hot-selling fund list + */ +void lb_fund_context_hot_funds(const struct lb_fund_context_t *ctx, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the fund list + * + * @param[in] opts Options for get funds request (can be null) + */ +void lb_fund_context_funds(const struct lb_fund_context_t *ctx, + const struct lb_get_funds_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the fund list filter options + */ +void lb_fund_context_filters(const struct lb_fund_context_t *ctx, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund detail + */ +void lb_fund_context_detail(const struct lb_fund_context_t *ctx, + const char *counter_id, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund analysis (level 1) + * + * @param[in] opts Options for the analysis request (can be null) + */ +void lb_fund_context_analysis(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_analysis_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund analysis detail (level 2) + * + * @param[in] opts Options for the analysis request (can be null) + */ +void lb_fund_context_analysis_detail(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_analysis_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund trend chart + * + * @param[in] opts Options for the trend request (can be null) + */ +void lb_fund_context_trend(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_analysis_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund annual returns + * + * @param[in] opts Paging options (can be null) + */ +void lb_fund_context_annual_returns(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_fund_page_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund quarterly returns + * + * @param[in] opts Paging options (can be null) + */ +void lb_fund_context_quarterly_returns(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_fund_page_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund performance figures + */ +void lb_fund_context_performance(const struct lb_fund_context_t *ctx, + const char *counter_id, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund performance comparison + * + * @param[in] opts Options for the comparison request (can be null) + */ +void lb_fund_context_performance_comparison(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_analysis_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund latest net value + */ +void lb_fund_context_nav(const struct lb_fund_context_t *ctx, + const char *counter_id, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund historical net value (paged) + * + * @param[in] opts Paging options (can be null) + */ +void lb_fund_context_nav_history(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_fund_page_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get fund historical net value by relative time range + * + * @param[in] opts Net-value range options (can be null) + */ +void lb_fund_context_nav_range(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_fund_nav_range_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get a fund's top-10 holdings + * + * @param[in] opts Options for the holdings request (can be null) + */ +void lb_fund_context_holdings(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_holdings_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the stocks held by a fund (reverse lookup) + * + * @param[in] opts Options for the stock-holdings request (can be null) + */ +void lb_fund_context_stock_holdings(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_stock_holdings_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the user's fund positions overview + * + * @param[in] opts Options for the positions request (can be null) + */ +void lb_fund_context_positions(const struct lb_fund_context_t *ctx, + const struct lb_fund_positions_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the user's single fund position detail + * + * @param[in] opts Options for the position request (can be null) + */ +void lb_fund_context_position(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_position_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the performance figures of a held fund + */ +void lb_fund_context_position_performance(const struct lb_fund_context_t *ctx, + const char *counter_id, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the cumulative-profit series of a held fund + * + * @param[in] opts Options for the profits request (can be null) + */ +void lb_fund_context_position_profits(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_position_profits_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the net-value history of a held fund + * + * @param[in] opts Net-value range options (can be null) + */ +void lb_fund_context_position_nav(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_fund_nav_range_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the dividend records of a held fund + * + * @param[in] opts Options for the dividends request (can be null) + */ +void lb_fund_context_position_dividends(const struct lb_fund_context_t *ctx, + const char *counter_id, + const struct lb_get_fund_position_dividends_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the user's fund orders + * + * @param[in] opts Options for the orders request (can be null) + */ +void lb_fund_context_orders(const struct lb_fund_context_t *ctx, + const struct lb_get_fund_orders_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Get a fund order detail + */ +void lb_fund_context_order(const struct lb_fund_context_t *ctx, + int64_t order_id, + lb_async_callback_t callback, + void *userdata); + +/** + * Get the user's fund transactions (cash-flow records) + * + * @param[in] opts Options for the transactions request (can be null) + */ +void lb_fund_context_transactions(const struct lb_fund_context_t *ctx, + const struct lb_get_fund_transactions_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Validate a fund order before submitting + * + * @param[in] opts Options for the validate request + */ +void lb_fund_context_validate_order(const struct lb_fund_context_t *ctx, + const struct lb_validate_fund_order_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Submit a fund order (buy / sell) + * + * @param[in] opts Options for the submit request + */ +void lb_fund_context_submit_order(const struct lb_fund_context_t *ctx, + const struct lb_submit_fund_order_options_t *opts, + lb_async_callback_t callback, + void *userdata); + +/** + * Cancel (withdraw) a fund order + */ +void lb_fund_context_cancel_order(const struct lb_fund_context_t *ctx, + int64_t order_id, + lb_async_callback_t callback, + void *userdata); + const struct lb_fundamental_context_t *lb_fundamental_context_new(const struct lb_config_t *config); void lb_fundamental_context_retain(const struct lb_fundamental_context_t *ctx); diff --git a/c/src/fund_context/context.rs b/c/src/fund_context/context.rs new file mode 100644 index 000000000..a7579186c --- /dev/null +++ b/c/src/fund_context/context.rs @@ -0,0 +1,870 @@ +use std::{ffi::c_void, os::raw::c_char, sync::Arc}; + +use longbridge::{ + FundContext, + fund::{ + FundNavRangeOptions, FundPageOptions, GetFundAnalysisOptions, GetFundHoldingsOptions, + GetFundOrdersOptions, GetFundPositionDividendsOptions, GetFundPositionOptions, + GetFundPositionProfitsOptions, GetFundPositionsOptions, GetFundStockHoldingsOptions, + GetFundTransactionsOptions, GetFundsOptions, SubmitFundOrderOptions, + ValidateFundOrderOptions, + }, +}; + +use crate::{ + async_call::{CAsyncCallback, execute_async}, + config::CConfig, + fund_context::types::{ + CFundAnalysisDetailOwned, CFundAnalysisOwned, CFundAnnualReturnOwned, CFundBriefOwned, + CFundDetailOwned, CFundDividendsOwned, CFundFiltersOwned, CFundHoldingsOwned, + CFundNavRangeOptions, CFundNavValueOwned, CFundOrderDetailOwned, CFundOrderOwned, + CFundOrderSubmitResponseOwned, CFundOrderValidationOwned, CFundPageOptions, + CFundPerformanceComparisonOwned, CFundPerformanceOwned, CFundPositionDetailOwned, + CFundPositionNavOwned, CFundPositionPerformanceOwned, CFundPositionProfitsOwned, + CFundPositionsOptions, CFundPositionsOwned, CFundQuarterlyReturnOwned, + CFundStockHoldingOwned, CFundTransactionOwned, CFundTrendOwned, CGetFundAnalysisOptions, + CGetFundHoldingsOptions, CGetFundOrdersOptions, CGetFundPositionDividendsOptions, + CGetFundPositionOptions, CGetFundPositionProfitsOptions, CGetFundStockHoldingsOptions, + CGetFundTransactionsOptions, CGetFundsOptions, CHotFundOwned, CSubmitFundOrderOptions, + CValidateFundOrderOptions, + }, + types::{CCow, CVec, cstr_array_to_rust, cstr_to_rust, slice_from_raw_parts}, +}; + +pub struct CFundContext { + ctx: FundContext, +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_new(config: *const CConfig) -> *const CFundContext { + Arc::into_raw(Arc::new(CFundContext { + ctx: FundContext::new(Arc::new((*config).0.clone())), + })) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_retain(ctx: *const CFundContext) { + Arc::increment_strong_count(ctx); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_release(ctx: *const CFundContext) { + let _ = Arc::from_raw(ctx); +} + +// ── option builders ───────────────────────────────────────────────────────── + +unsafe fn build_get_funds_options(opts: *const CGetFundsOptions) -> GetFundsOptions { + let mut o = GetFundsOptions::new(); + if opts.is_null() { + return o; + } + if !(*opts).filter.is_null() + && let Ok(value) = serde_json::from_str(&cstr_to_rust((*opts).filter)) + { + o = o.filter(value); + } + let quick_ids = slice_from_raw_parts((*opts).quick_ids, (*opts).num_quick_ids).to_vec(); + if !quick_ids.is_empty() { + o = o.quick_ids(quick_ids); + } + let time_interval = cstr_array_to_rust((*opts).time_interval, (*opts).num_time_interval); + if !time_interval.is_empty() { + o = o.time_interval(time_interval); + } + o +} + +unsafe fn build_analysis_options(opts: *const CGetFundAnalysisOptions) -> GetFundAnalysisOptions { + let mut o = GetFundAnalysisOptions::new(); + if !opts.is_null() && !(*opts).period.is_null() { + o = o.period(*(*opts).period); + } + o +} + +unsafe fn build_page_options(opts: *const CFundPageOptions) -> FundPageOptions { + let mut o = FundPageOptions::new(); + if !opts.is_null() { + if !(*opts).page.is_null() { + o = o.page(*(*opts).page); + } + if !(*opts).size.is_null() { + o = o.size(*(*opts).size); + } + } + o +} + +unsafe fn build_nav_range_options(opts: *const CFundNavRangeOptions) -> FundNavRangeOptions { + let mut o = FundNavRangeOptions::new(); + if !opts.is_null() { + if !(*opts).month_before.is_null() { + o = o.month_before(*(*opts).month_before); + } + if !(*opts).year_before.is_null() { + o = o.year_before(*(*opts).year_before); + } + } + o +} + +unsafe fn build_holdings_options(opts: *const CGetFundHoldingsOptions) -> GetFundHoldingsOptions { + let mut o = GetFundHoldingsOptions::new(); + if !opts.is_null() && !(*opts).scene.is_null() { + o = o.scene(*(*opts).scene); + } + o +} + +unsafe fn build_stock_holdings_options( + opts: *const CGetFundStockHoldingsOptions, +) -> GetFundStockHoldingsOptions { + let mut o = GetFundStockHoldingsOptions::new(); + if !opts.is_null() && !(*opts).limit.is_null() { + o = o.limit(*(*opts).limit); + } + o +} + +unsafe fn build_positions_options(opts: *const CFundPositionsOptions) -> GetFundPositionsOptions { + let mut o = GetFundPositionsOptions::new(); + if !opts.is_null() { + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + if !(*opts).aaid.is_null() { + o = o.aaid(*(*opts).aaid); + } + } + o +} + +unsafe fn build_position_options(opts: *const CGetFundPositionOptions) -> GetFundPositionOptions { + let mut o = GetFundPositionOptions::new(); + if !opts.is_null() { + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + if !(*opts).aaid.is_null() { + o = o.aaid(*(*opts).aaid); + } + if !(*opts).start.is_null() { + o = o.start(cstr_to_rust((*opts).start)); + } + if !(*opts).end.is_null() { + o = o.end(cstr_to_rust((*opts).end)); + } + } + o +} + +unsafe fn build_position_profits_options( + opts: *const CGetFundPositionProfitsOptions, +) -> GetFundPositionProfitsOptions { + let mut o = GetFundPositionProfitsOptions::new(); + if !opts.is_null() { + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + if !(*opts).aaid.is_null() { + o = o.aaid(*(*opts).aaid); + } + if !(*opts).start.is_null() { + o = o.start(cstr_to_rust((*opts).start)); + } + if !(*opts).end.is_null() { + o = o.end(cstr_to_rust((*opts).end)); + } + if !(*opts).page.is_null() { + o = o.page(*(*opts).page); + } + if !(*opts).size.is_null() { + o = o.size(*(*opts).size); + } + } + o +} + +unsafe fn build_position_dividends_options( + opts: *const CGetFundPositionDividendsOptions, +) -> GetFundPositionDividendsOptions { + let mut o = GetFundPositionDividendsOptions::new(); + if !opts.is_null() { + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + if !(*opts).aaid.is_null() { + o = o.aaid(*(*opts).aaid); + } + if !(*opts).currency.is_null() { + o = o.currency(cstr_to_rust((*opts).currency)); + } + if !(*opts).start.is_null() { + o = o.start(*(*opts).start); + } + if !(*opts).end.is_null() { + o = o.end(*(*opts).end); + } + if !(*opts).page.is_null() { + o = o.page(*(*opts).page); + } + if !(*opts).size.is_null() { + o = o.size(*(*opts).size); + } + } + o +} + +unsafe fn build_orders_options(opts: *const CGetFundOrdersOptions) -> GetFundOrdersOptions { + let mut o = GetFundOrdersOptions::new(); + if opts.is_null() { + return o; + } + let counter_ids = cstr_array_to_rust((*opts).counter_ids, (*opts).num_counter_ids); + if !counter_ids.is_empty() { + o = o.counter_ids(counter_ids); + } + if !(*opts).actions.is_null() { + o = o.actions(cstr_to_rust((*opts).actions)); + } + if !(*opts).states.is_null() { + o = o.states(cstr_to_rust((*opts).states)); + } + if !(*opts).currency.is_null() { + o = o.currency(cstr_to_rust((*opts).currency)); + } + if !(*opts).start.is_null() { + o = o.start(*(*opts).start); + } + if !(*opts).end.is_null() { + o = o.end(*(*opts).end); + } + if !(*opts).page.is_null() { + o = o.page(*(*opts).page); + } + if !(*opts).size.is_null() { + o = o.size(*(*opts).size); + } + o +} + +unsafe fn build_transactions_options( + opts: *const CGetFundTransactionsOptions, +) -> GetFundTransactionsOptions { + let mut o = GetFundTransactionsOptions::new(); + if opts.is_null() { + return o; + } + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + if !(*opts).business_type.is_null() { + o = o.business_type(cstr_to_rust((*opts).business_type)); + } + if !(*opts).category.is_null() { + o = o.category(cstr_to_rust((*opts).category)); + } + if !(*opts).currencies.is_null() { + o = o.currencies(cstr_to_rust((*opts).currencies)); + } + if !(*opts).start.is_null() { + o = o.start(*(*opts).start); + } + if !(*opts).end.is_null() { + o = o.end(*(*opts).end); + } + if !(*opts).page.is_null() { + o = o.page(*(*opts).page); + } + if !(*opts).size.is_null() { + o = o.size(*(*opts).size); + } + o +} + +unsafe fn build_validate_order_options( + opts: *const CValidateFundOrderOptions, +) -> ValidateFundOrderOptions { + let mut o = ValidateFundOrderOptions::new( + cstr_to_rust((*opts).counter_id), + cstr_to_rust((*opts).action), + cstr_to_rust((*opts).currency), + ); + if !(*opts).amount.is_null() { + o = o.amount(cstr_to_rust((*opts).amount)); + } + if !(*opts).units.is_null() { + o = o.units(cstr_to_rust((*opts).units)); + } + if !(*opts).dividend_option.is_null() { + o = o.dividend_option(*(*opts).dividend_option); + } + if !(*opts).fund_source.is_null() { + o = o.fund_source(*(*opts).fund_source); + } + if !(*opts).account_channel.is_null() { + o = o.account_channel(cstr_to_rust((*opts).account_channel)); + } + o +} + +unsafe fn build_submit_order_options( + opts: *const CSubmitFundOrderOptions, +) -> SubmitFundOrderOptions { + let mut o = SubmitFundOrderOptions::new( + cstr_to_rust((*opts).counter_id), + cstr_to_rust((*opts).action), + cstr_to_rust((*opts).currency), + ); + if !(*opts).amount.is_null() { + o = o.amount(cstr_to_rust((*opts).amount)); + } + if !(*opts).units.is_null() { + o = o.units(cstr_to_rust((*opts).units)); + } + if !(*opts).dividend_option.is_null() { + o = o.dividend_option(*(*opts).dividend_option); + } + if !(*opts).fee.is_null() { + o = o.fee(cstr_to_rust((*opts).fee)); + } + if !(*opts).is_sell_all.is_null() { + o = o.is_sell_all(*(*opts).is_sell_all); + } + if !(*opts).remark.is_null() { + o = o.remark(cstr_to_rust((*opts).remark)); + } + if !(*opts).trade_method.is_null() { + o = o.trade_method(*(*opts).trade_method); + } + o +} + +// ── catalog / market data ─────────────────────────────────────────────────── + +/// Get the hot-selling fund list +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_hot_funds( + ctx: *const CFundContext, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.hot_funds().await?.into(); + Ok(rows) + }); +} + +/// Get the fund list +/// +/// @param[in] opts Options for get funds request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_funds( + ctx: *const CFundContext, + opts: *const CGetFundsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_get_funds_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.funds(opts).await?.into(); + Ok(rows) + }); +} + +/// Get the fund list filter options +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_filters( + ctx: *const CFundContext, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.filters().await?); + Ok(resp) + }); +} + +/// Get fund detail +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_detail( + ctx: *const CFundContext, + counter_id: *const c_char, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.detail(counter_id).await?); + Ok(resp) + }); +} + +/// Get fund analysis (level 1) +/// +/// @param[in] opts Options for the analysis request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_analysis( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundAnalysisOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_analysis_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.analysis(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get fund analysis detail (level 2) +/// +/// @param[in] opts Options for the analysis request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_analysis_detail( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundAnalysisOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_analysis_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.analysis_detail(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get fund trend chart +/// +/// @param[in] opts Options for the trend request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_trend( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundAnalysisOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_analysis_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.trend(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get fund annual returns +/// +/// @param[in] opts Paging options (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_annual_returns( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CFundPageOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_page_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = + ctx_inner.annual_returns(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +/// Get fund quarterly returns +/// +/// @param[in] opts Paging options (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_quarterly_returns( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CFundPageOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_page_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = + ctx_inner.quarterly_returns(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +/// Get fund performance figures +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_performance( + ctx: *const CFundContext, + counter_id: *const c_char, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.performance(counter_id).await?.into(); + Ok(rows) + }); +} + +/// Get fund performance comparison +/// +/// @param[in] opts Options for the comparison request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_performance_comparison( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundAnalysisOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_analysis_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.performance_comparison(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get fund latest net value +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_nav( + ctx: *const CFundContext, + counter_id: *const c_char, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.nav(counter_id).await?.into(); + Ok(rows) + }); +} + +/// Get fund historical net value (paged) +/// +/// @param[in] opts Paging options (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_nav_history( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CFundPageOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_page_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.nav_history(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +/// Get fund historical net value by relative time range +/// +/// @param[in] opts Net-value range options (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_nav_range( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CFundNavRangeOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_nav_range_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.nav_range(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +/// Get a fund's top-10 holdings +/// +/// @param[in] opts Options for the holdings request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_holdings( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundHoldingsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_holdings_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.holdings(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get the stocks held by a fund (reverse lookup) +/// +/// @param[in] opts Options for the stock-holdings request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_stock_holdings( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundStockHoldingsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_stock_holdings_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = + ctx_inner.stock_holdings(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +// ── user fund positions ───────────────────────────────────────────────────── + +/// Get the user's fund positions overview +/// +/// @param[in] opts Options for the positions request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_positions( + ctx: *const CFundContext, + opts: *const CFundPositionsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_positions_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.positions(opts).await?); + Ok(resp) + }); +} + +/// Get the user's single fund position detail +/// +/// @param[in] opts Options for the position request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_position( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundPositionOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_position_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.position(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get the performance figures of a held fund +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_position_performance( + ctx: *const CFundContext, + counter_id: *const c_char, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = + ctx_inner.position_performance(counter_id).await?.into(); + Ok(rows) + }); +} + +/// Get the cumulative-profit series of a held fund +/// +/// @param[in] opts Options for the profits request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_position_profits( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundPositionProfitsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_position_profits_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.position_profits(counter_id, opts).await?); + Ok(resp) + }); +} + +/// Get the net-value history of a held fund +/// +/// @param[in] opts Net-value range options (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_position_nav( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CFundNavRangeOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_nav_range_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = + ctx_inner.position_nav(counter_id, opts).await?.into(); + Ok(rows) + }); +} + +/// Get the dividend records of a held fund +/// +/// @param[in] opts Options for the dividends request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_position_dividends( + ctx: *const CFundContext, + counter_id: *const c_char, + opts: *const CGetFundPositionDividendsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let counter_id = cstr_to_rust(counter_id); + let opts = build_position_dividends_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.position_dividends(counter_id, opts).await?); + Ok(resp) + }); +} + +// ── fund orders & trading ─────────────────────────────────────────────────── + +/// Get the user's fund orders +/// +/// @param[in] opts Options for the orders request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_orders( + ctx: *const CFundContext, + opts: *const CGetFundOrdersOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_orders_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.orders(opts).await?.into(); + Ok(rows) + }); +} + +/// Get a fund order detail +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_order( + ctx: *const CFundContext, + order_id: i64, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = CCow::new(ctx_inner.order(order_id).await?); + Ok(resp) + }); +} + +/// Get the user's fund transactions (cash-flow records) +/// +/// @param[in] opts Options for the transactions request (can be null) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_transactions( + ctx: *const CFundContext, + opts: *const CGetFundTransactionsOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_transactions_options(opts); + execute_async(callback, ctx, userdata, async move { + let rows: CVec = ctx_inner.transactions(opts).await?.into(); + Ok(rows) + }); +} + +/// Validate a fund order before submitting +/// +/// @param[in] opts Options for the validate request +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_validate_order( + ctx: *const CFundContext, + opts: *const CValidateFundOrderOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_validate_order_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.validate_order(opts).await?); + Ok(resp) + }); +} + +/// Submit a fund order (buy / sell) +/// +/// @param[in] opts Options for the submit request +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_submit_order( + ctx: *const CFundContext, + opts: *const CSubmitFundOrderOptions, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + let opts = build_submit_order_options(opts); + execute_async(callback, ctx, userdata, async move { + let resp: CCow = + CCow::new(ctx_inner.submit_order(opts).await?); + Ok(resp) + }); +} + +/// Cancel (withdraw) a fund order +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lb_fund_context_cancel_order( + ctx: *const CFundContext, + order_id: i64, + callback: CAsyncCallback, + userdata: *mut c_void, +) { + let ctx_inner = (*ctx).ctx.clone(); + execute_async(callback, ctx, userdata, async move { + ctx_inner.cancel_order(order_id).await?; + Ok(()) + }); +} diff --git a/c/src/fund_context/mod.rs b/c/src/fund_context/mod.rs new file mode 100644 index 000000000..9960ea28a --- /dev/null +++ b/c/src/fund_context/mod.rs @@ -0,0 +1,2 @@ +mod context; +pub(crate) mod types; diff --git a/c/src/fund_context/types.rs b/c/src/fund_context/types.rs new file mode 100644 index 000000000..a27bd7ab3 --- /dev/null +++ b/c/src/fund_context/types.rs @@ -0,0 +1,3208 @@ +use std::os::raw::c_char; + +use longbridge::fund::{ + FundAnalysis, FundAnalysisDetail, FundAnnualReturn, FundAssetAllocation, + FundAssetAllocationItem, FundBrief, FundDatedValue, FundDetail, FundDividend, FundDividends, + FundFilters, FundHolding, FundHoldings, FundNamedContrast, FundNavValue, FundOrder, + FundOrderDetail, FundOrderInfo, FundOrderKeyword, FundOrderStage, FundOrderSubmitResponse, + FundOrderValidation, FundPerformance, FundPerformanceComparison, FundPerformancePoint, + FundPosition, FundPositionDetail, FundPositionDetailValues, FundPositionNav, + FundPositionPerformance, FundPositionProfits, FundPositions, FundQuarterlyReturn, + FundStockHolding, FundTransaction, FundTrend, FundTrendContrast, FundUnitValue, HotFund, +}; +use serde_json::Value; + +use crate::types::{COption, CString, CVec, ToFFI}; + +// Helpers for `serde_json::Value` ("any") fields ------------------------------ + +/// Serialize a single JSON value to a JSON-encoded C string. +fn value_to_cstring(value: &Value) -> CString { + serde_json::to_string(value).unwrap_or_default().into() +} + +/// Serialize a list of JSON values to a `CVec` of JSON-encoded C strings. +fn values_to_cvec(values: Vec) -> CVec { + values + .into_iter() + .map(|v| serde_json::to_string(&v).unwrap_or_default()) + .collect::>() + .into() +} + +// ── FundNavValue ──────────────────────────────────────────────────────────── + +/// A fund net-asset-value data point (latest / historical). +#[repr(C)] +pub struct CFundNavValue { + /// Net value change + pub change: *const c_char, + /// Net value change percent + pub change_percent: *const c_char, + /// Formatted change percent + pub change_percent_format: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Fund name + pub counter_name: *const c_char, + /// Currency + pub currency: *const c_char, + /// Formatted date + pub date_format: *const c_char, + /// ISIN + pub isin: *const c_char, + /// Last update time (unix seconds) + pub last_update_time: i64, + /// Net value + pub value: *const c_char, + /// Formatted net value + pub value_format: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundNavValueOwned { + change: CString, + change_percent: CString, + change_percent_format: CString, + counter_id: CString, + counter_name: CString, + currency: CString, + date_format: CString, + isin: CString, + last_update_time: i64, + value: CString, + value_format: CString, +} + +impl From for CFundNavValueOwned { + fn from(v: FundNavValue) -> Self { + CFundNavValueOwned { + change: v.change.into(), + change_percent: v.change_percent.into(), + change_percent_format: v.change_percent_format.into(), + counter_id: v.counter_id.into(), + counter_name: v.counter_name.into(), + currency: v.currency.into(), + date_format: v.date_format.into(), + isin: v.isin.into(), + last_update_time: v.last_update_time, + value: v.value.into(), + value_format: v.value_format.into(), + } + } +} + +impl ToFFI for CFundNavValueOwned { + type FFIType = CFundNavValue; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundNavValue { + change: self.change.to_ffi_type(), + change_percent: self.change_percent.to_ffi_type(), + change_percent_format: self.change_percent_format.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + counter_name: self.counter_name.to_ffi_type(), + currency: self.currency.to_ffi_type(), + date_format: self.date_format.to_ffi_type(), + isin: self.isin.to_ffi_type(), + last_update_time: self.last_update_time, + value: self.value.to_ffi_type(), + value_format: self.value_format.to_ffi_type(), + } + } +} + +// ── FundPerformancePoint ──────────────────────────────────────────────────── + +/// A recent performance point used by the hot-fund list. +#[repr(C)] +pub struct CFundPerformancePoint { + /// Date + pub date: *const c_char, + /// Last done value + pub last_done: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPerformancePointOwned { + date: CString, + last_done: CString, +} + +impl From for CFundPerformancePointOwned { + fn from(p: FundPerformancePoint) -> Self { + CFundPerformancePointOwned { + date: p.date.into(), + last_done: p.last_done.into(), + } + } +} + +impl ToFFI for CFundPerformancePointOwned { + type FFIType = CFundPerformancePoint; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPerformancePoint { + date: self.date.to_ffi_type(), + last_done: self.last_done.to_ffi_type(), + } + } +} + +// ── HotFund ───────────────────────────────────────────────────────────────── + +/// A hot-selling fund entry. +#[repr(C)] +pub struct CHotFund { + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Currency + pub currency: *const c_char, + /// Earning rate + pub earning_rate: *const c_char, + /// Recent performance points + pub fund_performances: *const CFundPerformancePoint, + /// Number of recent performance points + pub num_fund_performances: usize, + /// Fund name + pub name: *const c_char, + /// Minimum purchase amount + pub purchase_amount: *const c_char, + /// Recommendation text + pub recommendation_text: *const c_char, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: *const c_char, + /// Time interval of the earning rate + pub time_interval: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CHotFundOwned { + asset_class: i32, + asset_class_name: CString, + counter_id: CString, + currency: CString, + earning_rate: CString, + fund_performances: CVec, + name: CString, + purchase_amount: CString, + recommendation_text: CString, + risk_level: i32, + risk_level_name: CString, + time_interval: CString, +} + +impl From for CHotFundOwned { + fn from(f: HotFund) -> Self { + CHotFundOwned { + asset_class: f.asset_class, + asset_class_name: f.asset_class_name.into(), + counter_id: f.counter_id.into(), + currency: f.currency.into(), + earning_rate: f.earning_rate.into(), + fund_performances: f.fund_performances.into(), + name: f.name.into(), + purchase_amount: f.purchase_amount.into(), + recommendation_text: f.recommendation_text.into(), + risk_level: f.risk_level, + risk_level_name: f.risk_level_name.into(), + time_interval: f.time_interval.into(), + } + } +} + +impl ToFFI for CHotFundOwned { + type FFIType = CHotFund; + + fn to_ffi_type(&self) -> Self::FFIType { + CHotFund { + asset_class: self.asset_class, + asset_class_name: self.asset_class_name.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + currency: self.currency.to_ffi_type(), + earning_rate: self.earning_rate.to_ffi_type(), + fund_performances: self.fund_performances.to_ffi_type(), + num_fund_performances: self.fund_performances.len(), + name: self.name.to_ffi_type(), + purchase_amount: self.purchase_amount.to_ffi_type(), + recommendation_text: self.recommendation_text.to_ffi_type(), + risk_level: self.risk_level, + risk_level_name: self.risk_level_name.to_ffi_type(), + time_interval: self.time_interval.to_ffi_type(), + } + } +} + +// ── FundBrief ─────────────────────────────────────────────────────────────── + +/// A fund entry in the fund list. +#[repr(C)] +pub struct CFundBrief { + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: *const c_char, + /// Fund code + pub code: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Currency + pub currency: *const c_char, + /// Description + pub description: *const c_char, + /// Earning rate + pub earning_rate: *const c_char, + /// Whether the user is holding this fund + pub holding: bool, + /// ISIN + pub isin: *const c_char, + /// Fund name + pub name: *const c_char, + /// Product + pub product: *const c_char, + /// Minimum purchase amount + pub purchase_amount: *const c_char, + /// Recommendation text + pub recommendation_text: *const c_char, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: *const c_char, + /// Time interval of the earning rate + pub time_interval: *const c_char, + /// Unit value + pub unit_value: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundBriefOwned { + asset_class: i32, + asset_class_name: CString, + code: CString, + counter_id: CString, + currency: CString, + description: CString, + earning_rate: CString, + holding: bool, + isin: CString, + name: CString, + product: CString, + purchase_amount: CString, + recommendation_text: CString, + risk_level: i32, + risk_level_name: CString, + time_interval: CString, + unit_value: CString, +} + +impl From for CFundBriefOwned { + fn from(f: FundBrief) -> Self { + CFundBriefOwned { + asset_class: f.asset_class, + asset_class_name: f.asset_class_name.into(), + code: f.code.into(), + counter_id: f.counter_id.into(), + currency: f.currency.into(), + description: f.description.into(), + earning_rate: f.earning_rate.into(), + holding: f.holding, + isin: f.isin.into(), + name: f.name.into(), + product: f.product.into(), + purchase_amount: f.purchase_amount.into(), + recommendation_text: f.recommendation_text.into(), + risk_level: f.risk_level, + risk_level_name: f.risk_level_name.into(), + time_interval: f.time_interval.into(), + unit_value: f.unit_value.into(), + } + } +} + +impl ToFFI for CFundBriefOwned { + type FFIType = CFundBrief; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundBrief { + asset_class: self.asset_class, + asset_class_name: self.asset_class_name.to_ffi_type(), + code: self.code.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + currency: self.currency.to_ffi_type(), + description: self.description.to_ffi_type(), + earning_rate: self.earning_rate.to_ffi_type(), + holding: self.holding, + isin: self.isin.to_ffi_type(), + name: self.name.to_ffi_type(), + product: self.product.to_ffi_type(), + purchase_amount: self.purchase_amount.to_ffi_type(), + recommendation_text: self.recommendation_text.to_ffi_type(), + risk_level: self.risk_level, + risk_level_name: self.risk_level_name.to_ffi_type(), + time_interval: self.time_interval.to_ffi_type(), + unit_value: self.unit_value.to_ffi_type(), + } + } +} + +// ── FundFilters ───────────────────────────────────────────────────────────── + +/// Fund list filter options. Each list holds JSON-encoded option objects. +#[repr(C)] +pub struct CFundFilters { + /// Asset class options (JSON strings) + pub asset_class: *const *const c_char, + /// Number of asset class options + pub num_asset_class: usize, + /// Company options (JSON strings) + pub company: *const *const c_char, + /// Number of company options + pub num_company: usize, + /// Currency options (JSON strings) + pub currency: *const *const c_char, + /// Number of currency options + pub num_currency: usize, + /// Industry category options (JSON strings) + pub industry_category_name: *const *const c_char, + /// Number of industry category options + pub num_industry_category_name: usize, + /// Risk level options (JSON strings) + pub risk_level: *const *const c_char, + /// Number of risk level options + pub num_risk_level: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundFiltersOwned { + asset_class: CVec, + company: CVec, + currency: CVec, + industry_category_name: CVec, + risk_level: CVec, +} + +impl From for CFundFiltersOwned { + fn from(f: FundFilters) -> Self { + CFundFiltersOwned { + asset_class: values_to_cvec(f.asset_class), + company: values_to_cvec(f.company), + currency: values_to_cvec(f.currency), + industry_category_name: values_to_cvec(f.industry_category_name), + risk_level: values_to_cvec(f.risk_level), + } + } +} + +impl ToFFI for CFundFiltersOwned { + type FFIType = CFundFilters; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundFilters { + asset_class: self.asset_class.to_ffi_type(), + num_asset_class: self.asset_class.len(), + company: self.company.to_ffi_type(), + num_company: self.company.len(), + currency: self.currency.to_ffi_type(), + num_currency: self.currency.len(), + industry_category_name: self.industry_category_name.to_ffi_type(), + num_industry_category_name: self.industry_category_name.len(), + risk_level: self.risk_level.to_ffi_type(), + num_risk_level: self.risk_level.len(), + } + } +} + +// ── FundAssetAllocation ───────────────────────────────────────────────────── + +/// A single holding entry inside a fund's asset allocation. +#[repr(C)] +pub struct CFundAssetAllocationItem { + /// Security code + pub code: *const c_char, + /// Security counter id + pub counter_id: *const c_char, + /// Name + pub name: *const c_char, + /// Position ratio + pub position_ratio: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundAssetAllocationItemOwned { + code: CString, + counter_id: CString, + name: CString, + position_ratio: CString, +} + +impl From for CFundAssetAllocationItemOwned { + fn from(i: FundAssetAllocationItem) -> Self { + CFundAssetAllocationItemOwned { + code: i.code.into(), + counter_id: i.counter_id.into(), + name: i.name.into(), + position_ratio: i.position_ratio.into(), + } + } +} + +impl ToFFI for CFundAssetAllocationItemOwned { + type FFIType = CFundAssetAllocationItem; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundAssetAllocationItem { + code: self.code.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + name: self.name.to_ffi_type(), + position_ratio: self.position_ratio.to_ffi_type(), + } + } +} + +/// A fund's asset allocation. +#[repr(C)] +pub struct CFundAssetAllocation { + /// Asset type + pub asset_type: i32, + /// Allocation entries + pub lists: *const CFundAssetAllocationItem, + /// Number of allocation entries + pub num_lists: usize, + /// Report date + pub report_date: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundAssetAllocationOwned { + asset_type: i32, + lists: CVec, + report_date: CString, +} + +impl From for CFundAssetAllocationOwned { + fn from(a: FundAssetAllocation) -> Self { + CFundAssetAllocationOwned { + asset_type: a.asset_type, + lists: a.lists.into(), + report_date: a.report_date.into(), + } + } +} + +impl ToFFI for CFundAssetAllocationOwned { + type FFIType = CFundAssetAllocation; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundAssetAllocation { + asset_type: self.asset_type, + lists: self.lists.to_ffi_type(), + num_lists: self.lists.len(), + report_date: self.report_date.to_ffi_type(), + } + } +} + +// ── FundDetail ────────────────────────────────────────────────────────────── + +/// Fund detail. +#[repr(C)] +pub struct CFundDetail { + /// Additional purchase amount + pub additional_purchase_amount: *const c_char, + /// Affirm day + pub affirm_day: i32, + /// Amount affirm day + pub amount_affirm_day: *const c_char, + /// Asset allocation, or null if unavailable + pub asset_allocation: *const CFundAssetAllocation, + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: *const c_char, + /// Bill purchase rate + pub bill_purchase_rate: *const c_char, + /// Channel + pub channel: *const c_char, + /// Close period + pub close_period: *const c_char, + /// Fund code + pub code: *const c_char, + /// Currency + pub currency: *const c_char, + /// Cut off time + pub cut_off_time: *const c_char, + /// Whether it is a derivative + pub derivatives: bool, + /// Done day + pub done_day: i32, + /// Excess return fee + pub excess_return_fee: *const c_char, + /// GST rate + pub gst_rate: *const c_char, + /// Introduction + pub introduce: *const c_char, + /// Whether it is a cash-plus fund + pub is_cash_plus: bool, + /// Whether it is a complex product + pub is_complex: bool, + /// Whether it is a new cash-plus fund + pub is_new_cash_plus: bool, + /// Whether it is a Yinghebao fund + pub is_yinghebao: bool, + /// ISIN + pub isin: *const c_char, + /// Management rate + pub manage_rate: *const c_char, + /// Manager + pub manager: *const c_char, + /// Minimum holding cash + pub min_hold_cash: *const c_char, + /// Minimum holding share + pub min_hold_share: *const c_char, + /// Minimum sell share + pub min_sell_share: *const c_char, + /// Month raise day + pub month_raise_day: *const c_char, + /// Fund name + pub name: *const c_char, + /// Net value deadline + pub nav_deadline: *const c_char, + /// Whether it is no-load + pub no_load: bool, + /// Open date + pub open_date: *const c_char, + /// Open period + pub open_period: *const c_char, + /// Product + pub product: *const c_char, + /// Product information locals + pub product_information_locals: *const c_char, + /// Profile + pub profile: *const c_char, + /// Whether purchasable + pub purchasable: i32, + /// Purchase affirm day + pub purchase_affirm_day: *const c_char, + /// Minimum purchase amount + pub purchase_amount: *const c_char, + /// Purchase rate + pub purchase_rate: *const c_char, + /// Rating + pub rating: i32, + /// Whether redeemable + pub redeemable: i32, + /// Redemption advance day + pub redemption_advance_day: *const c_char, + /// Redemption amount + pub redemption_amount: *const c_char, + /// Redemption close period text + pub redemption_close_period_shows: *const c_char, + /// Redemption done day + pub redemption_done_day: *const c_char, + /// Redemption open day text + pub redemption_open_day_shows: *const c_char, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: *const c_char, + /// Verify status + pub verify_status: i32, + /// Whether it is a virtual currency fund + pub virtual_currency: bool, + /// Year to date yield + pub year_to_date_yield: *const c_char, + /// Year to date yield type + pub ytd_yield_type: i32, +} + +#[derive(Debug)] +pub(crate) struct CFundDetailOwned { + additional_purchase_amount: CString, + affirm_day: i32, + amount_affirm_day: CString, + asset_allocation: COption, + asset_class: i32, + asset_class_name: CString, + bill_purchase_rate: CString, + channel: CString, + close_period: CString, + code: CString, + currency: CString, + cut_off_time: CString, + derivatives: bool, + done_day: i32, + excess_return_fee: CString, + gst_rate: CString, + introduce: CString, + is_cash_plus: bool, + is_complex: bool, + is_new_cash_plus: bool, + is_yinghebao: bool, + isin: CString, + manage_rate: CString, + manager: CString, + min_hold_cash: CString, + min_hold_share: CString, + min_sell_share: CString, + month_raise_day: CString, + name: CString, + nav_deadline: CString, + no_load: bool, + open_date: CString, + open_period: CString, + product: CString, + product_information_locals: CString, + profile: CString, + purchasable: i32, + purchase_affirm_day: CString, + purchase_amount: CString, + purchase_rate: CString, + rating: i32, + redeemable: i32, + redemption_advance_day: CString, + redemption_amount: CString, + redemption_close_period_shows: CString, + redemption_done_day: CString, + redemption_open_day_shows: CString, + risk_level: i32, + risk_level_name: CString, + verify_status: i32, + virtual_currency: bool, + year_to_date_yield: CString, + ytd_yield_type: i32, +} + +impl From for CFundDetailOwned { + fn from(d: FundDetail) -> Self { + CFundDetailOwned { + additional_purchase_amount: d.additional_purchase_amount.into(), + affirm_day: d.affirm_day, + amount_affirm_day: d.amount_affirm_day.into(), + asset_allocation: d.asset_allocation.into(), + asset_class: d.asset_class, + asset_class_name: d.asset_class_name.into(), + bill_purchase_rate: d.bill_purchase_rate.into(), + channel: d.channel.into(), + close_period: d.close_period.into(), + code: d.code.into(), + currency: d.currency.into(), + cut_off_time: d.cut_off_time.into(), + derivatives: d.derivatives, + done_day: d.done_day, + excess_return_fee: d.excess_return_fee.into(), + gst_rate: d.gst_rate.into(), + introduce: d.introduce.into(), + is_cash_plus: d.is_cash_plus, + is_complex: d.is_complex, + is_new_cash_plus: d.is_new_cash_plus, + is_yinghebao: d.is_yinghebao, + isin: d.isin.into(), + manage_rate: d.manage_rate.into(), + manager: d.manager.into(), + min_hold_cash: d.min_hold_cash.into(), + min_hold_share: d.min_hold_share.into(), + min_sell_share: d.min_sell_share.into(), + month_raise_day: d.month_raise_day.into(), + name: d.name.into(), + nav_deadline: d.nav_deadline.into(), + no_load: d.no_load, + open_date: d.open_date.into(), + open_period: d.open_period.into(), + product: d.product.into(), + product_information_locals: d.product_information_locals.into(), + profile: d.profile.into(), + purchasable: d.purchasable, + purchase_affirm_day: d.purchase_affirm_day.into(), + purchase_amount: d.purchase_amount.into(), + purchase_rate: d.purchase_rate.into(), + rating: d.rating, + redeemable: d.redeemable, + redemption_advance_day: d.redemption_advance_day.into(), + redemption_amount: d.redemption_amount.into(), + redemption_close_period_shows: d.redemption_close_period_shows.into(), + redemption_done_day: d.redemption_done_day.into(), + redemption_open_day_shows: d.redemption_open_day_shows.into(), + risk_level: d.risk_level, + risk_level_name: d.risk_level_name.into(), + verify_status: d.verify_status, + virtual_currency: d.virtual_currency, + year_to_date_yield: d.year_to_date_yield.into(), + ytd_yield_type: d.ytd_yield_type, + } + } +} + +impl ToFFI for CFundDetailOwned { + type FFIType = CFundDetail; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundDetail { + additional_purchase_amount: self.additional_purchase_amount.to_ffi_type(), + affirm_day: self.affirm_day, + amount_affirm_day: self.amount_affirm_day.to_ffi_type(), + asset_allocation: self.asset_allocation.to_ffi_type(), + asset_class: self.asset_class, + asset_class_name: self.asset_class_name.to_ffi_type(), + bill_purchase_rate: self.bill_purchase_rate.to_ffi_type(), + channel: self.channel.to_ffi_type(), + close_period: self.close_period.to_ffi_type(), + code: self.code.to_ffi_type(), + currency: self.currency.to_ffi_type(), + cut_off_time: self.cut_off_time.to_ffi_type(), + derivatives: self.derivatives, + done_day: self.done_day, + excess_return_fee: self.excess_return_fee.to_ffi_type(), + gst_rate: self.gst_rate.to_ffi_type(), + introduce: self.introduce.to_ffi_type(), + is_cash_plus: self.is_cash_plus, + is_complex: self.is_complex, + is_new_cash_plus: self.is_new_cash_plus, + is_yinghebao: self.is_yinghebao, + isin: self.isin.to_ffi_type(), + manage_rate: self.manage_rate.to_ffi_type(), + manager: self.manager.to_ffi_type(), + min_hold_cash: self.min_hold_cash.to_ffi_type(), + min_hold_share: self.min_hold_share.to_ffi_type(), + min_sell_share: self.min_sell_share.to_ffi_type(), + month_raise_day: self.month_raise_day.to_ffi_type(), + name: self.name.to_ffi_type(), + nav_deadline: self.nav_deadline.to_ffi_type(), + no_load: self.no_load, + open_date: self.open_date.to_ffi_type(), + open_period: self.open_period.to_ffi_type(), + product: self.product.to_ffi_type(), + product_information_locals: self.product_information_locals.to_ffi_type(), + profile: self.profile.to_ffi_type(), + purchasable: self.purchasable, + purchase_affirm_day: self.purchase_affirm_day.to_ffi_type(), + purchase_amount: self.purchase_amount.to_ffi_type(), + purchase_rate: self.purchase_rate.to_ffi_type(), + rating: self.rating, + redeemable: self.redeemable, + redemption_advance_day: self.redemption_advance_day.to_ffi_type(), + redemption_amount: self.redemption_amount.to_ffi_type(), + redemption_close_period_shows: self.redemption_close_period_shows.to_ffi_type(), + redemption_done_day: self.redemption_done_day.to_ffi_type(), + redemption_open_day_shows: self.redemption_open_day_shows.to_ffi_type(), + risk_level: self.risk_level, + risk_level_name: self.risk_level_name.to_ffi_type(), + verify_status: self.verify_status, + virtual_currency: self.virtual_currency, + year_to_date_yield: self.year_to_date_yield.to_ffi_type(), + ytd_yield_type: self.ytd_yield_type, + } + } +} + +// ── FundAnalysis / FundAnalysisDetail ─────────────────────────────────────── + +/// Fund analysis (level 1). +#[repr(C)] +pub struct CFundAnalysis { + /// Actual period + pub actual_period: i32, + /// Cost level (JSON string) + pub cost_level: *const c_char, + /// Return ability (JSON string) + pub return_ability: *const c_char, + /// Risk ability (JSON string) + pub risk_ability: *const c_char, + /// Updated at + pub updated_at: *const c_char, + /// Value for money (JSON string) + pub value_for_money: *const c_char, + /// Whether visible + pub visible: bool, +} + +#[derive(Debug)] +pub(crate) struct CFundAnalysisOwned { + actual_period: i32, + cost_level: CString, + return_ability: CString, + risk_ability: CString, + updated_at: CString, + value_for_money: CString, + visible: bool, +} + +impl From for CFundAnalysisOwned { + fn from(a: FundAnalysis) -> Self { + CFundAnalysisOwned { + actual_period: a.actual_period, + cost_level: value_to_cstring(&a.cost_level), + return_ability: value_to_cstring(&a.return_ability), + risk_ability: value_to_cstring(&a.risk_ability), + updated_at: a.updated_at.into(), + value_for_money: value_to_cstring(&a.value_for_money), + visible: a.visible, + } + } +} + +impl ToFFI for CFundAnalysisOwned { + type FFIType = CFundAnalysis; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundAnalysis { + actual_period: self.actual_period, + cost_level: self.cost_level.to_ffi_type(), + return_ability: self.return_ability.to_ffi_type(), + risk_ability: self.risk_ability.to_ffi_type(), + updated_at: self.updated_at.to_ffi_type(), + value_for_money: self.value_for_money.to_ffi_type(), + visible: self.visible, + } + } +} + +/// Fund analysis detail (level 2). +#[repr(C)] +pub struct CFundAnalysisDetail { + /// Actual period + pub actual_period: i32, + /// Available periods + pub available_periods: *const i32, + /// Number of available periods + pub num_available_periods: usize, + /// Cost level (JSON string) + pub cost_level: *const c_char, + /// Return ability (JSON string) + pub return_ability: *const c_char, + /// Risk ability (JSON string) + pub risk_ability: *const c_char, + /// Updated at + pub updated_at: *const c_char, + /// Value for money (JSON string) + pub value_for_money: *const c_char, + /// Whether visible + pub visible: bool, +} + +#[derive(Debug)] +pub(crate) struct CFundAnalysisDetailOwned { + actual_period: i32, + available_periods: CVec, + cost_level: CString, + return_ability: CString, + risk_ability: CString, + updated_at: CString, + value_for_money: CString, + visible: bool, +} + +impl From for CFundAnalysisDetailOwned { + fn from(a: FundAnalysisDetail) -> Self { + CFundAnalysisDetailOwned { + actual_period: a.actual_period, + available_periods: a.available_periods.into(), + cost_level: value_to_cstring(&a.cost_level), + return_ability: value_to_cstring(&a.return_ability), + risk_ability: value_to_cstring(&a.risk_ability), + updated_at: a.updated_at.into(), + value_for_money: value_to_cstring(&a.value_for_money), + visible: a.visible, + } + } +} + +impl ToFFI for CFundAnalysisDetailOwned { + type FFIType = CFundAnalysisDetail; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundAnalysisDetail { + actual_period: self.actual_period, + available_periods: self.available_periods.to_ffi_type(), + num_available_periods: self.available_periods.len(), + cost_level: self.cost_level.to_ffi_type(), + return_ability: self.return_ability.to_ffi_type(), + risk_ability: self.risk_ability.to_ffi_type(), + updated_at: self.updated_at.to_ffi_type(), + value_for_money: self.value_for_money.to_ffi_type(), + visible: self.visible, + } + } +} + +// ── FundTrend ─────────────────────────────────────────────────────────────── + +/// A benchmark contrast series in a fund trend chart. +#[repr(C)] +pub struct CFundTrendContrast { + /// Benchmark name + pub benchmark_name: *const c_char, + /// Performance points (JSON strings) + pub performances: *const *const c_char, + /// Number of performance points + pub num_performances: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundTrendContrastOwned { + benchmark_name: CString, + performances: CVec, +} + +impl From for CFundTrendContrastOwned { + fn from(c: FundTrendContrast) -> Self { + CFundTrendContrastOwned { + benchmark_name: c.benchmark_name.into(), + performances: values_to_cvec(c.performances), + } + } +} + +impl ToFFI for CFundTrendContrastOwned { + type FFIType = CFundTrendContrast; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundTrendContrast { + benchmark_name: self.benchmark_name.to_ffi_type(), + performances: self.performances.to_ffi_type(), + num_performances: self.performances.len(), + } + } +} + +/// Fund trend chart. +#[repr(C)] +pub struct CFundTrend { + /// Actual period + pub actual_period: i32, + /// Available periods + pub available_periods: *const i32, + /// Number of available periods + pub num_available_periods: usize, + /// Category average performances (JSON strings) + pub category_average_performances: *const *const c_char, + /// Number of category average performances + pub num_category_average_performances: usize, + /// Benchmark contrast performances, or null if unavailable + pub contrast_performances: *const CFundTrendContrast, + /// Fund performances (JSON strings) + pub fund_performances: *const *const c_char, + /// Number of fund performances + pub num_fund_performances: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundTrendOwned { + actual_period: i32, + available_periods: CVec, + category_average_performances: CVec, + contrast_performances: COption, + fund_performances: CVec, +} + +impl From for CFundTrendOwned { + fn from(t: FundTrend) -> Self { + CFundTrendOwned { + actual_period: t.actual_period, + available_periods: t.available_periods.into(), + category_average_performances: values_to_cvec(t.category_average_performances), + contrast_performances: t.contrast_performances.into(), + fund_performances: values_to_cvec(t.fund_performances), + } + } +} + +impl ToFFI for CFundTrendOwned { + type FFIType = CFundTrend; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundTrend { + actual_period: self.actual_period, + available_periods: self.available_periods.to_ffi_type(), + num_available_periods: self.available_periods.len(), + category_average_performances: self.category_average_performances.to_ffi_type(), + num_category_average_performances: self.category_average_performances.len(), + contrast_performances: self.contrast_performances.to_ffi_type(), + fund_performances: self.fund_performances.to_ffi_type(), + num_fund_performances: self.fund_performances.len(), + } + } +} + +// ── FundPerformanceComparison ─────────────────────────────────────────────── + +/// A named contrast performance series. +#[repr(C)] +pub struct CFundNamedContrast { + /// Series name + pub name: *const c_char, + /// Performance points (JSON strings) + pub performances: *const *const c_char, + /// Number of performance points + pub num_performances: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundNamedContrastOwned { + name: CString, + performances: CVec, +} + +impl From for CFundNamedContrastOwned { + fn from(c: FundNamedContrast) -> Self { + CFundNamedContrastOwned { + name: c.name.into(), + performances: values_to_cvec(c.performances), + } + } +} + +impl ToFFI for CFundNamedContrastOwned { + type FFIType = CFundNamedContrast; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundNamedContrast { + name: self.name.to_ffi_type(), + performances: self.performances.to_ffi_type(), + num_performances: self.performances.len(), + } + } +} + +/// Fund performance comparison. +#[repr(C)] +pub struct CFundPerformanceComparison { + /// Contrast performance series + pub contrast_performances: *const CFundNamedContrast, + /// Number of contrast performance series + pub num_contrast_performances: usize, + /// Fund performances (JSON strings) + pub fund_performances: *const *const c_char, + /// Number of fund performances + pub num_fund_performances: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundPerformanceComparisonOwned { + contrast_performances: CVec, + fund_performances: CVec, +} + +impl From for CFundPerformanceComparisonOwned { + fn from(c: FundPerformanceComparison) -> Self { + CFundPerformanceComparisonOwned { + contrast_performances: c.contrast_performances.into(), + fund_performances: values_to_cvec(c.fund_performances), + } + } +} + +impl ToFFI for CFundPerformanceComparisonOwned { + type FFIType = CFundPerformanceComparison; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPerformanceComparison { + contrast_performances: self.contrast_performances.to_ffi_type(), + num_contrast_performances: self.contrast_performances.len(), + fund_performances: self.fund_performances.to_ffi_type(), + num_fund_performances: self.fund_performances.len(), + } + } +} + +// ── FundAnnualReturn / FundQuarterlyReturn ────────────────────────────────── + +/// A fund annual return entry. +#[repr(C)] +pub struct CFundAnnualReturn { + /// Change percent + pub change_percent: *const c_char, + /// Year + pub year: i32, +} + +#[derive(Debug)] +pub(crate) struct CFundAnnualReturnOwned { + change_percent: CString, + year: i32, +} + +impl From for CFundAnnualReturnOwned { + fn from(r: FundAnnualReturn) -> Self { + CFundAnnualReturnOwned { + change_percent: r.change_percent.into(), + year: r.year, + } + } +} + +impl ToFFI for CFundAnnualReturnOwned { + type FFIType = CFundAnnualReturn; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundAnnualReturn { + change_percent: self.change_percent.to_ffi_type(), + year: self.year, + } + } +} + +/// A fund quarterly return entry. +#[repr(C)] +pub struct CFundQuarterlyReturn { + /// Change percent + pub change_percent: *const c_char, + /// Quarter + pub quarter: i32, + /// Year + pub year: i32, +} + +#[derive(Debug)] +pub(crate) struct CFundQuarterlyReturnOwned { + change_percent: CString, + quarter: i32, + year: i32, +} + +impl From for CFundQuarterlyReturnOwned { + fn from(r: FundQuarterlyReturn) -> Self { + CFundQuarterlyReturnOwned { + change_percent: r.change_percent.into(), + quarter: r.quarter, + year: r.year, + } + } +} + +impl ToFFI for CFundQuarterlyReturnOwned { + type FFIType = CFundQuarterlyReturn; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundQuarterlyReturn { + change_percent: self.change_percent.to_ffi_type(), + quarter: self.quarter, + year: self.year, + } + } +} + +// ── FundPerformance ───────────────────────────────────────────────────────── + +/// A fund's detailed performance figures. +#[repr(C)] +pub struct CFundPerformance { + /// Annualized return (5y) + pub annualized_return_five: *const c_char, + /// Annualized return (1y) + pub annualized_return_one: *const c_char, + /// Annualized return (10y) + pub annualized_return_ten: *const c_char, + /// Annualized return (3y) + pub annualized_return_three: *const c_char, + /// Annualized return (2y) + pub annualized_return_two: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Fund name + pub fund_name: *const c_char, + /// Rank (5y) + pub performance_rank_five_years: i32, + /// Rank (1d) + pub performance_rank_one_day: i32, + /// Rank (1m) + pub performance_rank_one_month: i32, + /// Rank (1w) + pub performance_rank_one_week: i32, + /// Rank (1y) + pub performance_rank_one_year: i32, + /// Rank (6m) + pub performance_rank_six_months: i32, + /// Rank (10y) + pub performance_rank_ten_years: i32, + /// Rank (3m) + pub performance_rank_three_months: i32, + /// Rank (3y) + pub performance_rank_three_years: i32, + /// Rank (2y) + pub performance_rank_two_years: i32, + /// Rank (ytd) + pub performance_rank_ytd: i32, + /// Return (5y) + pub performance_return_five_years: *const c_char, + /// Return (1d) + pub performance_return_one_day: *const c_char, + /// Return (1m) + pub performance_return_one_month: *const c_char, + /// Return (1w) + pub performance_return_one_week: *const c_char, + /// Return (1y) + pub performance_return_one_year: *const c_char, + /// Return (6m) + pub performance_return_six_months: *const c_char, + /// Return (10y) + pub performance_return_ten_years: *const c_char, + /// Return (3m) + pub performance_return_three_months: *const c_char, + /// Return (3y) + pub performance_return_three_years: *const c_char, + /// Return (2y) + pub performance_return_two_years: *const c_char, + /// Return (ytd) + pub performance_return_ytd: *const c_char, + /// Total peers (5y) + pub performance_total_five_years: i32, + /// Total peers (1d) + pub performance_total_one_day: i32, + /// Total peers (1m) + pub performance_total_one_month: i32, + /// Total peers (1w) + pub performance_total_one_week: i32, + /// Total peers (1y) + pub performance_total_one_year: i32, + /// Total peers (6m) + pub performance_total_six_months: i32, + /// Total peers (10y) + pub performance_total_ten_years: i32, + /// Total peers (3m) + pub performance_total_three_months: i32, + /// Total peers (3y) + pub performance_total_three_years: i32, + /// Total peers (2y) + pub performance_total_two_years: i32, + /// Total peers (ytd) + pub performance_total_ytd: i32, + /// Seven days annualized + pub seven_days_annualized: *const c_char, + /// Ten thousand price + pub ten_thousand_price: *const c_char, + /// Update time (unix seconds) + pub update_time: i64, +} + +#[derive(Debug)] +pub(crate) struct CFundPerformanceOwned { + annualized_return_five: CString, + annualized_return_one: CString, + annualized_return_ten: CString, + annualized_return_three: CString, + annualized_return_two: CString, + counter_id: CString, + fund_name: CString, + performance_rank_five_years: i32, + performance_rank_one_day: i32, + performance_rank_one_month: i32, + performance_rank_one_week: i32, + performance_rank_one_year: i32, + performance_rank_six_months: i32, + performance_rank_ten_years: i32, + performance_rank_three_months: i32, + performance_rank_three_years: i32, + performance_rank_two_years: i32, + performance_rank_ytd: i32, + performance_return_five_years: CString, + performance_return_one_day: CString, + performance_return_one_month: CString, + performance_return_one_week: CString, + performance_return_one_year: CString, + performance_return_six_months: CString, + performance_return_ten_years: CString, + performance_return_three_months: CString, + performance_return_three_years: CString, + performance_return_two_years: CString, + performance_return_ytd: CString, + performance_total_five_years: i32, + performance_total_one_day: i32, + performance_total_one_month: i32, + performance_total_one_week: i32, + performance_total_one_year: i32, + performance_total_six_months: i32, + performance_total_ten_years: i32, + performance_total_three_months: i32, + performance_total_three_years: i32, + performance_total_two_years: i32, + performance_total_ytd: i32, + seven_days_annualized: CString, + ten_thousand_price: CString, + update_time: i64, +} + +impl From for CFundPerformanceOwned { + fn from(p: FundPerformance) -> Self { + CFundPerformanceOwned { + annualized_return_five: p.annualized_return_five.into(), + annualized_return_one: p.annualized_return_one.into(), + annualized_return_ten: p.annualized_return_ten.into(), + annualized_return_three: p.annualized_return_three.into(), + annualized_return_two: p.annualized_return_two.into(), + counter_id: p.counter_id.into(), + fund_name: p.fund_name.into(), + performance_rank_five_years: p.performance_rank_five_years, + performance_rank_one_day: p.performance_rank_one_day, + performance_rank_one_month: p.performance_rank_one_month, + performance_rank_one_week: p.performance_rank_one_week, + performance_rank_one_year: p.performance_rank_one_year, + performance_rank_six_months: p.performance_rank_six_months, + performance_rank_ten_years: p.performance_rank_ten_years, + performance_rank_three_months: p.performance_rank_three_months, + performance_rank_three_years: p.performance_rank_three_years, + performance_rank_two_years: p.performance_rank_two_years, + performance_rank_ytd: p.performance_rank_ytd, + performance_return_five_years: p.performance_return_five_years.into(), + performance_return_one_day: p.performance_return_one_day.into(), + performance_return_one_month: p.performance_return_one_month.into(), + performance_return_one_week: p.performance_return_one_week.into(), + performance_return_one_year: p.performance_return_one_year.into(), + performance_return_six_months: p.performance_return_six_months.into(), + performance_return_ten_years: p.performance_return_ten_years.into(), + performance_return_three_months: p.performance_return_three_months.into(), + performance_return_three_years: p.performance_return_three_years.into(), + performance_return_two_years: p.performance_return_two_years.into(), + performance_return_ytd: p.performance_return_ytd.into(), + performance_total_five_years: p.performance_total_five_years, + performance_total_one_day: p.performance_total_one_day, + performance_total_one_month: p.performance_total_one_month, + performance_total_one_week: p.performance_total_one_week, + performance_total_one_year: p.performance_total_one_year, + performance_total_six_months: p.performance_total_six_months, + performance_total_ten_years: p.performance_total_ten_years, + performance_total_three_months: p.performance_total_three_months, + performance_total_three_years: p.performance_total_three_years, + performance_total_two_years: p.performance_total_two_years, + performance_total_ytd: p.performance_total_ytd, + seven_days_annualized: p.seven_days_annualized.into(), + ten_thousand_price: p.ten_thousand_price.into(), + update_time: p.update_time, + } + } +} + +impl ToFFI for CFundPerformanceOwned { + type FFIType = CFundPerformance; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPerformance { + annualized_return_five: self.annualized_return_five.to_ffi_type(), + annualized_return_one: self.annualized_return_one.to_ffi_type(), + annualized_return_ten: self.annualized_return_ten.to_ffi_type(), + annualized_return_three: self.annualized_return_three.to_ffi_type(), + annualized_return_two: self.annualized_return_two.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + fund_name: self.fund_name.to_ffi_type(), + performance_rank_five_years: self.performance_rank_five_years, + performance_rank_one_day: self.performance_rank_one_day, + performance_rank_one_month: self.performance_rank_one_month, + performance_rank_one_week: self.performance_rank_one_week, + performance_rank_one_year: self.performance_rank_one_year, + performance_rank_six_months: self.performance_rank_six_months, + performance_rank_ten_years: self.performance_rank_ten_years, + performance_rank_three_months: self.performance_rank_three_months, + performance_rank_three_years: self.performance_rank_three_years, + performance_rank_two_years: self.performance_rank_two_years, + performance_rank_ytd: self.performance_rank_ytd, + performance_return_five_years: self.performance_return_five_years.to_ffi_type(), + performance_return_one_day: self.performance_return_one_day.to_ffi_type(), + performance_return_one_month: self.performance_return_one_month.to_ffi_type(), + performance_return_one_week: self.performance_return_one_week.to_ffi_type(), + performance_return_one_year: self.performance_return_one_year.to_ffi_type(), + performance_return_six_months: self.performance_return_six_months.to_ffi_type(), + performance_return_ten_years: self.performance_return_ten_years.to_ffi_type(), + performance_return_three_months: self.performance_return_three_months.to_ffi_type(), + performance_return_three_years: self.performance_return_three_years.to_ffi_type(), + performance_return_two_years: self.performance_return_two_years.to_ffi_type(), + performance_return_ytd: self.performance_return_ytd.to_ffi_type(), + performance_total_five_years: self.performance_total_five_years, + performance_total_one_day: self.performance_total_one_day, + performance_total_one_month: self.performance_total_one_month, + performance_total_one_week: self.performance_total_one_week, + performance_total_one_year: self.performance_total_one_year, + performance_total_six_months: self.performance_total_six_months, + performance_total_ten_years: self.performance_total_ten_years, + performance_total_three_months: self.performance_total_three_months, + performance_total_three_years: self.performance_total_three_years, + performance_total_two_years: self.performance_total_two_years, + performance_total_ytd: self.performance_total_ytd, + seven_days_annualized: self.seven_days_annualized.to_ffi_type(), + ten_thousand_price: self.ten_thousand_price.to_ffi_type(), + update_time: self.update_time, + } + } +} + +// ── FundHoldings ──────────────────────────────────────────────────────────── + +/// A single fund holding (top-10 holdings). +#[repr(C)] +pub struct CFundHolding { + /// Bond type + pub bond_type: *const c_char, + /// Bond type name + pub bond_type_name: *const c_char, + /// Country name + pub country_name: *const c_char, + /// Holding type + pub holding_type: *const c_char, + /// Industry name + pub industry_name: *const c_char, + /// Market value + pub market_value: *const c_char, + /// Maturity date + pub maturity_date: *const c_char, + /// Name + pub name: *const c_char, + /// Share change + pub share_change: *const c_char, + /// Share change percent + pub share_change_percent: *const c_char, + /// Shares + pub shares: *const c_char, + /// Weighting + pub weighting: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundHoldingOwned { + bond_type: CString, + bond_type_name: CString, + country_name: CString, + holding_type: CString, + industry_name: CString, + market_value: CString, + maturity_date: CString, + name: CString, + share_change: CString, + share_change_percent: CString, + shares: CString, + weighting: CString, +} + +impl From for CFundHoldingOwned { + fn from(h: FundHolding) -> Self { + CFundHoldingOwned { + bond_type: h.bond_type.into(), + bond_type_name: h.bond_type_name.into(), + country_name: h.country_name.into(), + holding_type: h.holding_type.into(), + industry_name: h.industry_name.into(), + market_value: h.market_value.into(), + maturity_date: h.maturity_date.into(), + name: h.name.into(), + share_change: h.share_change.into(), + share_change_percent: h.share_change_percent.into(), + shares: h.shares.into(), + weighting: h.weighting.into(), + } + } +} + +impl ToFFI for CFundHoldingOwned { + type FFIType = CFundHolding; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundHolding { + bond_type: self.bond_type.to_ffi_type(), + bond_type_name: self.bond_type_name.to_ffi_type(), + country_name: self.country_name.to_ffi_type(), + holding_type: self.holding_type.to_ffi_type(), + industry_name: self.industry_name.to_ffi_type(), + market_value: self.market_value.to_ffi_type(), + maturity_date: self.maturity_date.to_ffi_type(), + name: self.name.to_ffi_type(), + share_change: self.share_change.to_ffi_type(), + share_change_percent: self.share_change_percent.to_ffi_type(), + shares: self.shares.to_ffi_type(), + weighting: self.weighting.to_ffi_type(), + } + } +} + +/// A fund's top-10 holdings. +#[repr(C)] +pub struct CFundHoldings { + /// Holding entries + pub holdings: *const CFundHolding, + /// Number of holding entries + pub num_holdings: usize, + /// Report date + pub report_date: *const c_char, + /// Total weighting + pub weighting: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundHoldingsOwned { + holdings: CVec, + report_date: CString, + weighting: CString, +} + +impl From for CFundHoldingsOwned { + fn from(h: FundHoldings) -> Self { + CFundHoldingsOwned { + holdings: h.holdings.into(), + report_date: h.report_date.into(), + weighting: h.weighting.into(), + } + } +} + +impl ToFFI for CFundHoldingsOwned { + type FFIType = CFundHoldings; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundHoldings { + holdings: self.holdings.to_ffi_type(), + num_holdings: self.holdings.len(), + report_date: self.report_date.to_ffi_type(), + weighting: self.weighting.to_ffi_type(), + } + } +} + +// ── FundStockHolding ──────────────────────────────────────────────────────── + +/// A stock held by the fund (reverse lookup). +#[repr(C)] +pub struct CFundStockHolding { + /// Stock code + pub code: *const c_char, + /// Stock counter id + pub counter_id: *const c_char, + /// Currency + pub currency: *const c_char, + /// Stock name + pub name: *const c_char, + /// Position ratio + pub position_ratio: *const c_char, + /// Report date + pub report_date: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundStockHoldingOwned { + code: CString, + counter_id: CString, + currency: CString, + name: CString, + position_ratio: CString, + report_date: CString, +} + +impl From for CFundStockHoldingOwned { + fn from(h: FundStockHolding) -> Self { + CFundStockHoldingOwned { + code: h.code.into(), + counter_id: h.counter_id.into(), + currency: h.currency.into(), + name: h.name.into(), + position_ratio: h.position_ratio.into(), + report_date: h.report_date.into(), + } + } +} + +impl ToFFI for CFundStockHoldingOwned { + type FFIType = CFundStockHolding; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundStockHolding { + code: self.code.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + currency: self.currency.to_ffi_type(), + name: self.name.to_ffi_type(), + position_ratio: self.position_ratio.to_ffi_type(), + report_date: self.report_date.to_ffi_type(), + } + } +} + +// ── FundPositions ─────────────────────────────────────────────────────────── + +/// A single fund position held by the user. +#[repr(C)] +pub struct CFundPositionItem { + /// Holding amount + pub amount: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Currency + pub currency: *const c_char, + /// Frozen units + pub freeze_units: *const c_char, + /// Holding profit + pub holding_profit: *const c_char, + /// Holding units + pub holding_units: *const c_char, + /// Fund name + pub name: *const c_char, + /// Recent profit + pub recent_profit: *const c_char, + /// Recent trading day (unix seconds) + pub recent_trading_day: i64, + /// Accumulated recent profit + pub sum_recent_profit: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionItemOwned { + amount: CString, + counter_id: CString, + currency: CString, + freeze_units: CString, + holding_profit: CString, + holding_units: CString, + name: CString, + recent_profit: CString, + recent_trading_day: i64, + sum_recent_profit: CString, +} + +impl From for CFundPositionItemOwned { + fn from(p: FundPosition) -> Self { + CFundPositionItemOwned { + amount: p.amount.into(), + counter_id: p.counter_id.into(), + currency: p.currency.into(), + freeze_units: p.freeze_units.into(), + holding_profit: p.holding_profit.into(), + holding_units: p.holding_units.into(), + name: p.name.into(), + recent_profit: p.recent_profit.into(), + recent_trading_day: p.recent_trading_day, + sum_recent_profit: p.sum_recent_profit.into(), + } + } +} + +impl ToFFI for CFundPositionItemOwned { + type FFIType = CFundPositionItem; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionItem { + amount: self.amount.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + currency: self.currency.to_ffi_type(), + freeze_units: self.freeze_units.to_ffi_type(), + holding_profit: self.holding_profit.to_ffi_type(), + holding_units: self.holding_units.to_ffi_type(), + name: self.name.to_ffi_type(), + recent_profit: self.recent_profit.to_ffi_type(), + recent_trading_day: self.recent_trading_day, + sum_recent_profit: self.sum_recent_profit.to_ffi_type(), + } + } +} + +/// The user's fund positions overview. +#[repr(C)] +pub struct CFundPositions { + /// Account channel + pub account_channel: *const c_char, + /// Position entries + pub list: *const CFundPositionItem, + /// Number of position entries + pub num_list: usize, + /// Pending buy orders amount + pub pending_buy_orders: *const c_char, + /// Recent trading day (unix seconds) + pub recent_trading_day: i64, + /// Sold pending credit orders amount + pub sold_pending_credit_orders: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionsOwned { + account_channel: CString, + list: CVec, + pending_buy_orders: CString, + recent_trading_day: i64, + sold_pending_credit_orders: CString, +} + +impl From for CFundPositionsOwned { + fn from(p: FundPositions) -> Self { + CFundPositionsOwned { + account_channel: p.account_channel.into(), + list: p.list.into(), + pending_buy_orders: p.pending_buy_orders.into(), + recent_trading_day: p.recent_trading_day, + sold_pending_credit_orders: p.sold_pending_credit_orders.into(), + } + } +} + +impl ToFFI for CFundPositionsOwned { + type FFIType = CFundPositions; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositions { + account_channel: self.account_channel.to_ffi_type(), + list: self.list.to_ffi_type(), + num_list: self.list.len(), + pending_buy_orders: self.pending_buy_orders.to_ffi_type(), + recent_trading_day: self.recent_trading_day, + sold_pending_credit_orders: self.sold_pending_credit_orders.to_ffi_type(), + } + } +} + +// ── FundPositionDetail ────────────────────────────────────────────────────── + +/// A dated value point. +#[repr(C)] +pub struct CFundDatedValue { + /// Date (unix seconds) + pub date: i64, + /// Value + pub value: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundDatedValueOwned { + date: i64, + value: CString, +} + +impl From for CFundDatedValueOwned { + fn from(v: FundDatedValue) -> Self { + CFundDatedValueOwned { + date: v.date, + value: v.value.into(), + } + } +} + +impl ToFFI for CFundDatedValueOwned { + type FFIType = CFundDatedValue; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundDatedValue { + date: self.date, + value: self.value.to_ffi_type(), + } + } +} + +/// A fund unit-value point (position view). +#[repr(C)] +pub struct CFundUnitValue { + /// Date (unix seconds) + pub date: i64, + /// Day increase rate + pub day_increase_rate: *const c_char, + /// Total value + pub total_value: *const c_char, + /// Unit value + pub unit_value: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundUnitValueOwned { + date: i64, + day_increase_rate: CString, + total_value: CString, + unit_value: CString, +} + +impl From for CFundUnitValueOwned { + fn from(v: FundUnitValue) -> Self { + CFundUnitValueOwned { + date: v.date, + day_increase_rate: v.day_increase_rate.into(), + total_value: v.total_value.into(), + unit_value: v.unit_value.into(), + } + } +} + +impl ToFFI for CFundUnitValueOwned { + type FFIType = CFundUnitValue; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundUnitValue { + date: self.date, + day_increase_rate: self.day_increase_rate.to_ffi_type(), + total_value: self.total_value.to_ffi_type(), + unit_value: self.unit_value.to_ffi_type(), + } + } +} + +/// Detail values of a single fund position. +#[repr(C)] +pub struct CFundPositionDetailValues { + /// Amount + pub amount: *const c_char, + /// Currency + pub currency: *const c_char, + /// Holding cost + pub holding_cost: *const c_char, + /// Holding profit + pub holding_profit: *const c_char, + /// Holding profit rate + pub holding_profit_rate: *const c_char, + /// Holding units + pub holding_units: *const c_char, + /// Holding value + pub holding_value: *const c_char, + /// Pending buy value + pub pending_buy_value: *const c_char, + /// Pending sell value + pub pending_sell_value: *const c_char, + /// Accumulated profit (to date) + pub profit_amount_accum_td: *const c_char, + /// Accumulated profit rate (to date) + pub profit_amount_accum_td_rate: *const c_char, + /// Recent profit + pub recent_profit: *const c_char, + /// Recent trading day (unix seconds) + pub recent_tradingday: i64, + /// Recent unit value + pub recent_unit_value: *const c_char, + /// Sold pending-confirm units + pub sold_pending_confirm_units: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionDetailValuesOwned { + amount: CString, + currency: CString, + holding_cost: CString, + holding_profit: CString, + holding_profit_rate: CString, + holding_units: CString, + holding_value: CString, + pending_buy_value: CString, + pending_sell_value: CString, + profit_amount_accum_td: CString, + profit_amount_accum_td_rate: CString, + recent_profit: CString, + recent_tradingday: i64, + recent_unit_value: CString, + sold_pending_confirm_units: CString, +} + +impl From for CFundPositionDetailValuesOwned { + fn from(v: FundPositionDetailValues) -> Self { + CFundPositionDetailValuesOwned { + amount: v.amount.into(), + currency: v.currency.into(), + holding_cost: v.holding_cost.into(), + holding_profit: v.holding_profit.into(), + holding_profit_rate: v.holding_profit_rate.into(), + holding_units: v.holding_units.into(), + holding_value: v.holding_value.into(), + pending_buy_value: v.pending_buy_value.into(), + pending_sell_value: v.pending_sell_value.into(), + profit_amount_accum_td: v.profit_amount_accum_td.into(), + profit_amount_accum_td_rate: v.profit_amount_accum_td_rate.into(), + recent_profit: v.recent_profit.into(), + recent_tradingday: v.recent_tradingday, + recent_unit_value: v.recent_unit_value.into(), + sold_pending_confirm_units: v.sold_pending_confirm_units.into(), + } + } +} + +impl ToFFI for CFundPositionDetailValuesOwned { + type FFIType = CFundPositionDetailValues; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionDetailValues { + amount: self.amount.to_ffi_type(), + currency: self.currency.to_ffi_type(), + holding_cost: self.holding_cost.to_ffi_type(), + holding_profit: self.holding_profit.to_ffi_type(), + holding_profit_rate: self.holding_profit_rate.to_ffi_type(), + holding_units: self.holding_units.to_ffi_type(), + holding_value: self.holding_value.to_ffi_type(), + pending_buy_value: self.pending_buy_value.to_ffi_type(), + pending_sell_value: self.pending_sell_value.to_ffi_type(), + profit_amount_accum_td: self.profit_amount_accum_td.to_ffi_type(), + profit_amount_accum_td_rate: self.profit_amount_accum_td_rate.to_ffi_type(), + recent_profit: self.recent_profit.to_ffi_type(), + recent_tradingday: self.recent_tradingday, + recent_unit_value: self.recent_unit_value.to_ffi_type(), + sold_pending_confirm_units: self.sold_pending_confirm_units.to_ffi_type(), + } + } +} + +/// Detail of a single fund position. +#[repr(C)] +pub struct CFundPositionDetail { + /// Detail values + pub detail_values: *const CFundPositionDetailValues, + /// Accumulated profit series + pub sum_profit: *const CFundDatedValue, + /// Number of accumulated-profit points + pub num_sum_profit: usize, + /// Unit value series + pub ut_value: *const CFundUnitValue, + /// Number of unit-value points + pub num_ut_value: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionDetailOwned { + detail_values: COption, + sum_profit: CVec, + ut_value: CVec, +} + +impl From for CFundPositionDetailOwned { + fn from(d: FundPositionDetail) -> Self { + CFundPositionDetailOwned { + detail_values: d.detail_values.into(), + sum_profit: d.sum_profit.into(), + ut_value: d.ut_value.into(), + } + } +} + +impl ToFFI for CFundPositionDetailOwned { + type FFIType = CFundPositionDetail; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionDetail { + detail_values: self.detail_values.to_ffi_type(), + sum_profit: self.sum_profit.to_ffi_type(), + num_sum_profit: self.sum_profit.len(), + ut_value: self.ut_value.to_ffi_type(), + num_ut_value: self.ut_value.len(), + } + } +} + +// ── FundPositionPerformance ───────────────────────────────────────────────── + +/// Performance figures for a held fund. +#[repr(C)] +pub struct CFundPositionPerformance { + /// Annualized return (5y) + pub annualized_return_five: *const c_char, + /// Annualized return (1y) + pub annualized_return_one: *const c_char, + /// Annualized return (10y) + pub annualized_return_ten: *const c_char, + /// Annualized return (3y) + pub annualized_return_three: *const c_char, + /// Annualized return (2y) + pub annualized_return_two: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Fund name + pub fund_name: *const c_char, + /// Return (5y) + pub performance_return_five_years: *const c_char, + /// Return (1d) + pub performance_return_one_day: *const c_char, + /// Return (1m) + pub performance_return_one_month: *const c_char, + /// Return (1w) + pub performance_return_one_week: *const c_char, + /// Return (1y) + pub performance_return_one_year: *const c_char, + /// Return (6m) + pub performance_return_six_months: *const c_char, + /// Return (10y) + pub performance_return_ten_years: *const c_char, + /// Return (3m) + pub performance_return_three_months: *const c_char, + /// Return (3y) + pub performance_return_three_years: *const c_char, + /// Return (2y) + pub performance_return_two_years: *const c_char, + /// Return (ytd) + pub performance_return_ytd: *const c_char, + /// Update time (unix seconds) + pub update_time: i64, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionPerformanceOwned { + annualized_return_five: CString, + annualized_return_one: CString, + annualized_return_ten: CString, + annualized_return_three: CString, + annualized_return_two: CString, + counter_id: CString, + fund_name: CString, + performance_return_five_years: CString, + performance_return_one_day: CString, + performance_return_one_month: CString, + performance_return_one_week: CString, + performance_return_one_year: CString, + performance_return_six_months: CString, + performance_return_ten_years: CString, + performance_return_three_months: CString, + performance_return_three_years: CString, + performance_return_two_years: CString, + performance_return_ytd: CString, + update_time: i64, +} + +impl From for CFundPositionPerformanceOwned { + fn from(p: FundPositionPerformance) -> Self { + CFundPositionPerformanceOwned { + annualized_return_five: p.annualized_return_five.into(), + annualized_return_one: p.annualized_return_one.into(), + annualized_return_ten: p.annualized_return_ten.into(), + annualized_return_three: p.annualized_return_three.into(), + annualized_return_two: p.annualized_return_two.into(), + counter_id: p.counter_id.into(), + fund_name: p.fund_name.into(), + performance_return_five_years: p.performance_return_five_years.into(), + performance_return_one_day: p.performance_return_one_day.into(), + performance_return_one_month: p.performance_return_one_month.into(), + performance_return_one_week: p.performance_return_one_week.into(), + performance_return_one_year: p.performance_return_one_year.into(), + performance_return_six_months: p.performance_return_six_months.into(), + performance_return_ten_years: p.performance_return_ten_years.into(), + performance_return_three_months: p.performance_return_three_months.into(), + performance_return_three_years: p.performance_return_three_years.into(), + performance_return_two_years: p.performance_return_two_years.into(), + performance_return_ytd: p.performance_return_ytd.into(), + update_time: p.update_time, + } + } +} + +impl ToFFI for CFundPositionPerformanceOwned { + type FFIType = CFundPositionPerformance; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionPerformance { + annualized_return_five: self.annualized_return_five.to_ffi_type(), + annualized_return_one: self.annualized_return_one.to_ffi_type(), + annualized_return_ten: self.annualized_return_ten.to_ffi_type(), + annualized_return_three: self.annualized_return_three.to_ffi_type(), + annualized_return_two: self.annualized_return_two.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + fund_name: self.fund_name.to_ffi_type(), + performance_return_five_years: self.performance_return_five_years.to_ffi_type(), + performance_return_one_day: self.performance_return_one_day.to_ffi_type(), + performance_return_one_month: self.performance_return_one_month.to_ffi_type(), + performance_return_one_week: self.performance_return_one_week.to_ffi_type(), + performance_return_one_year: self.performance_return_one_year.to_ffi_type(), + performance_return_six_months: self.performance_return_six_months.to_ffi_type(), + performance_return_ten_years: self.performance_return_ten_years.to_ffi_type(), + performance_return_three_months: self.performance_return_three_months.to_ffi_type(), + performance_return_three_years: self.performance_return_three_years.to_ffi_type(), + performance_return_two_years: self.performance_return_two_years.to_ffi_type(), + performance_return_ytd: self.performance_return_ytd.to_ffi_type(), + update_time: self.update_time, + } + } +} + +// ── FundPositionProfits ───────────────────────────────────────────────────── + +/// The user's cumulative profit for a held fund. +#[repr(C)] +pub struct CFundPositionProfits { + /// Currency + pub currency: *const c_char, + /// Profit series + pub history_value: *const CFundDatedValue, + /// Number of profit points + pub num_history_value: usize, + /// Last update time (unix seconds) + pub last_update_time: i64, + /// Total profit + pub sum_profit: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionProfitsOwned { + currency: CString, + history_value: CVec, + last_update_time: i64, + sum_profit: CString, +} + +impl From for CFundPositionProfitsOwned { + fn from(p: FundPositionProfits) -> Self { + CFundPositionProfitsOwned { + currency: p.currency.into(), + history_value: p.history_value.into(), + last_update_time: p.last_update_time, + sum_profit: p.sum_profit.into(), + } + } +} + +impl ToFFI for CFundPositionProfitsOwned { + type FFIType = CFundPositionProfits; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionProfits { + currency: self.currency.to_ffi_type(), + history_value: self.history_value.to_ffi_type(), + num_history_value: self.history_value.len(), + last_update_time: self.last_update_time, + sum_profit: self.sum_profit.to_ffi_type(), + } + } +} + +// ── FundPositionNav ───────────────────────────────────────────────────────── + +/// A held-fund net-value point (position view). +#[repr(C)] +pub struct CFundPositionNav { + /// Net value change + pub change: *const c_char, + /// Net value change percent + pub change_percent: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Fund name + pub counter_name: *const c_char, + /// Last update time (unix seconds) + pub last_update_time: i64, + /// Net value + pub value: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundPositionNavOwned { + change: CString, + change_percent: CString, + counter_id: CString, + counter_name: CString, + last_update_time: i64, + value: CString, +} + +impl From for CFundPositionNavOwned { + fn from(v: FundPositionNav) -> Self { + CFundPositionNavOwned { + change: v.change.into(), + change_percent: v.change_percent.into(), + counter_id: v.counter_id.into(), + counter_name: v.counter_name.into(), + last_update_time: v.last_update_time, + value: v.value.into(), + } + } +} + +impl ToFFI for CFundPositionNavOwned { + type FFIType = CFundPositionNav; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundPositionNav { + change: self.change.to_ffi_type(), + change_percent: self.change_percent.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + counter_name: self.counter_name.to_ffi_type(), + last_update_time: self.last_update_time, + value: self.value.to_ffi_type(), + } + } +} + +// ── FundDividends ─────────────────────────────────────────────────────────── + +/// A cash dividend record for a held fund. +#[repr(C)] +pub struct CFundDividend { + /// Amount + pub amount: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Currency + pub currency: *const c_char, + /// Date (unix seconds) + pub date: i64, + /// Dividend method + pub div_method: *const c_char, + /// Fund name + pub name: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundDividendOwned { + amount: CString, + counter_id: CString, + currency: CString, + date: i64, + div_method: CString, + name: CString, +} + +impl From for CFundDividendOwned { + fn from(d: FundDividend) -> Self { + CFundDividendOwned { + amount: d.amount.into(), + counter_id: d.counter_id.into(), + currency: d.currency.into(), + date: d.date, + div_method: d.div_method.into(), + name: d.name.into(), + } + } +} + +impl ToFFI for CFundDividendOwned { + type FFIType = CFundDividend; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundDividend { + amount: self.amount.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + currency: self.currency.to_ffi_type(), + date: self.date, + div_method: self.div_method.to_ffi_type(), + name: self.name.to_ffi_type(), + } + } +} + +/// The user's dividend records for a held fund. +#[repr(C)] +pub struct CFundDividends { + /// Currency + pub currency: *const c_char, + /// Dividend records + pub div_cash_infos: *const CFundDividend, + /// Number of dividend records + pub num_div_cash_infos: usize, + /// Latest dividend date (unix seconds) + pub lastest_date: i64, + /// Total cash dividend + pub total_div_cash: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundDividendsOwned { + currency: CString, + div_cash_infos: CVec, + lastest_date: i64, + total_div_cash: CString, +} + +impl From for CFundDividendsOwned { + fn from(d: FundDividends) -> Self { + CFundDividendsOwned { + currency: d.currency.into(), + div_cash_infos: d.div_cash_infos.into(), + lastest_date: d.lastest_date, + total_div_cash: d.total_div_cash.into(), + } + } +} + +impl ToFFI for CFundDividendsOwned { + type FFIType = CFundDividends; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundDividends { + currency: self.currency.to_ffi_type(), + div_cash_infos: self.div_cash_infos.to_ffi_type(), + num_div_cash_infos: self.div_cash_infos.len(), + lastest_date: self.lastest_date, + total_div_cash: self.total_div_cash.to_ffi_type(), + } + } +} + +// ── FundOrder ─────────────────────────────────────────────────────────────── + +/// A fund order (list view). +#[repr(C)] +pub struct CFundOrder { + /// Action (buy/sell) + pub action: *const c_char, + /// Amount + pub amount: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Created at (unix seconds) + pub created_at: i64, + /// Currency + pub currency: *const c_char, + /// Fund name + pub fund_name: *const c_char, + /// Order id + pub id: i64, + /// Whether it is an auto (DCA) order + pub is_auto: bool, + /// Net worth + pub net_worth: *const c_char, + /// Product type + pub product_type: *const c_char, + /// State + pub state: *const c_char, + /// State description + pub state_desc: *const c_char, + /// Units + pub units: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderOwned { + action: CString, + amount: CString, + counter_id: CString, + created_at: i64, + currency: CString, + fund_name: CString, + id: i64, + is_auto: bool, + net_worth: CString, + product_type: CString, + state: CString, + state_desc: CString, + units: CString, +} + +impl From for CFundOrderOwned { + fn from(o: FundOrder) -> Self { + CFundOrderOwned { + action: o.action.into(), + amount: o.amount.into(), + counter_id: o.counter_id.into(), + created_at: o.created_at, + currency: o.currency.into(), + fund_name: o.fund_name.into(), + id: o.id, + is_auto: o.is_auto, + net_worth: o.net_worth.into(), + product_type: o.product_type.into(), + state: o.state.into(), + state_desc: o.state_desc.into(), + units: o.units.into(), + } + } +} + +impl ToFFI for CFundOrderOwned { + type FFIType = CFundOrder; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrder { + action: self.action.to_ffi_type(), + amount: self.amount.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + created_at: self.created_at, + currency: self.currency.to_ffi_type(), + fund_name: self.fund_name.to_ffi_type(), + id: self.id, + is_auto: self.is_auto, + net_worth: self.net_worth.to_ffi_type(), + product_type: self.product_type.to_ffi_type(), + state: self.state.to_ffi_type(), + state_desc: self.state_desc.to_ffi_type(), + units: self.units.to_ffi_type(), + } + } +} + +// ── FundOrderDetail ───────────────────────────────────────────────────────── + +/// A keyword block in a fund order detail. +#[repr(C)] +pub struct CFundOrderKeyword { + /// Content + pub content: *const c_char, + /// Group + pub group: *const c_char, + /// Key + pub key: *const c_char, + /// Line strategy + pub line_strategy: *const c_char, + /// Title + pub title: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderKeywordOwned { + content: CString, + group: CString, + key: CString, + line_strategy: CString, + title: CString, +} + +impl From for CFundOrderKeywordOwned { + fn from(k: FundOrderKeyword) -> Self { + CFundOrderKeywordOwned { + content: k.content.into(), + group: k.group.into(), + key: k.key.into(), + line_strategy: k.line_strategy.into(), + title: k.title.into(), + } + } +} + +impl ToFFI for CFundOrderKeywordOwned { + type FFIType = CFundOrderKeyword; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderKeyword { + content: self.content.to_ffi_type(), + group: self.group.to_ffi_type(), + key: self.key.to_ffi_type(), + line_strategy: self.line_strategy.to_ffi_type(), + title: self.title.to_ffi_type(), + } + } +} + +/// A processing stage in a fund order detail. +#[repr(C)] +pub struct CFundOrderStage { + /// Description + pub desc: *const c_char, + /// Key + pub key: *const c_char, + /// Link + pub link: *const c_char, + /// Link text + pub link_text: *const c_char, + /// Progress + pub progress: *const c_char, + /// Stage + pub stage: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderStageOwned { + desc: CString, + key: CString, + link: CString, + link_text: CString, + progress: CString, + stage: CString, +} + +impl From for CFundOrderStageOwned { + fn from(s: FundOrderStage) -> Self { + CFundOrderStageOwned { + desc: s.desc.into(), + key: s.key.into(), + link: s.link.into(), + link_text: s.link_text.into(), + progress: s.progress.into(), + stage: s.stage.into(), + } + } +} + +impl ToFFI for CFundOrderStageOwned { + type FFIType = CFundOrderStage; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderStage { + desc: self.desc.to_ffi_type(), + key: self.key.to_ffi_type(), + link: self.link.to_ffi_type(), + link_text: self.link_text.to_ffi_type(), + progress: self.progress.to_ffi_type(), + stage: self.stage.to_ffi_type(), + } + } +} + +/// The full information of a fund order. +#[repr(C)] +pub struct CFundOrderInfo { + /// Account id + pub aaid: i64, + /// Account channel + pub account_channel: *const c_char, + /// Action (buy/sell) + pub action: *const c_char, + /// Amount + pub amount: *const c_char, + /// Channel + pub channel: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Created at (unix seconds) + pub created_at: i64, + /// Currency + pub currency: *const c_char, + /// Dividend option + pub dividend_option: *const c_char, + /// Equity time (unix seconds) + pub eq_at: i64, + /// Fee + pub fee: *const c_char, + /// Fund name + pub fund_name: *const c_char, + /// Fund source + pub fund_source: *const c_char, + /// Histories + pub histories: *const c_char, + /// Order id + pub id: i64, + /// Message + pub message: *const c_char, + /// Net worth + pub net_worth: *const c_char, + /// Price time (unix seconds) + pub price_at: i64, + /// Processed at (unix seconds) + pub processed_at: i64, + /// Product type + pub product_type: *const c_char, + /// Whether repurchaseable + pub repurchaseable: bool, + /// Sale proceeds + pub sale_proceeds: *const c_char, + /// Sales charge + pub sales_charge: *const c_char, + /// Sales price + pub sales_price: *const c_char, + /// Sales unit + pub sales_unit: *const c_char, + /// State + pub state: *const c_char, + /// State description + pub state_desc: *const c_char, + /// Status + pub status: i32, + /// Extended status + pub status_ex: i32, + /// T+ description + pub t_description: *const c_char, + /// Time partition + pub time_partition: *const c_char, + /// Total amount + pub total_amount: *const c_char, + /// Transaction at (unix seconds) + pub transaction_at: i64, + /// Units + pub units: *const c_char, + /// Withdraw at (unix seconds) + pub withdraw_at: i64, + /// Whether withdrawable + pub withdrawable: bool, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderInfoOwned { + aaid: i64, + account_channel: CString, + action: CString, + amount: CString, + channel: CString, + counter_id: CString, + created_at: i64, + currency: CString, + dividend_option: CString, + eq_at: i64, + fee: CString, + fund_name: CString, + fund_source: CString, + histories: CString, + id: i64, + message: CString, + net_worth: CString, + price_at: i64, + processed_at: i64, + product_type: CString, + repurchaseable: bool, + sale_proceeds: CString, + sales_charge: CString, + sales_price: CString, + sales_unit: CString, + state: CString, + state_desc: CString, + status: i32, + status_ex: i32, + t_description: CString, + time_partition: CString, + total_amount: CString, + transaction_at: i64, + units: CString, + withdraw_at: i64, + withdrawable: bool, +} + +impl From for CFundOrderInfoOwned { + fn from(o: FundOrderInfo) -> Self { + CFundOrderInfoOwned { + aaid: o.aaid, + account_channel: o.account_channel.into(), + action: o.action.into(), + amount: o.amount.into(), + channel: o.channel.into(), + counter_id: o.counter_id.into(), + created_at: o.created_at, + currency: o.currency.into(), + dividend_option: o.dividend_option.into(), + eq_at: o.eq_at, + fee: o.fee.into(), + fund_name: o.fund_name.into(), + fund_source: o.fund_source.into(), + histories: o.histories.into(), + id: o.id, + message: o.message.into(), + net_worth: o.net_worth.into(), + price_at: o.price_at, + processed_at: o.processed_at, + product_type: o.product_type.into(), + repurchaseable: o.repurchaseable, + sale_proceeds: o.sale_proceeds.into(), + sales_charge: o.sales_charge.into(), + sales_price: o.sales_price.into(), + sales_unit: o.sales_unit.into(), + state: o.state.into(), + state_desc: o.state_desc.into(), + status: o.status, + status_ex: o.status_ex, + t_description: o.t_description.into(), + time_partition: o.time_partition.into(), + total_amount: o.total_amount.into(), + transaction_at: o.transaction_at, + units: o.units.into(), + withdraw_at: o.withdraw_at, + withdrawable: o.withdrawable, + } + } +} + +impl ToFFI for CFundOrderInfoOwned { + type FFIType = CFundOrderInfo; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderInfo { + aaid: self.aaid, + account_channel: self.account_channel.to_ffi_type(), + action: self.action.to_ffi_type(), + amount: self.amount.to_ffi_type(), + channel: self.channel.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + created_at: self.created_at, + currency: self.currency.to_ffi_type(), + dividend_option: self.dividend_option.to_ffi_type(), + eq_at: self.eq_at, + fee: self.fee.to_ffi_type(), + fund_name: self.fund_name.to_ffi_type(), + fund_source: self.fund_source.to_ffi_type(), + histories: self.histories.to_ffi_type(), + id: self.id, + message: self.message.to_ffi_type(), + net_worth: self.net_worth.to_ffi_type(), + price_at: self.price_at, + processed_at: self.processed_at, + product_type: self.product_type.to_ffi_type(), + repurchaseable: self.repurchaseable, + sale_proceeds: self.sale_proceeds.to_ffi_type(), + sales_charge: self.sales_charge.to_ffi_type(), + sales_price: self.sales_price.to_ffi_type(), + sales_unit: self.sales_unit.to_ffi_type(), + state: self.state.to_ffi_type(), + state_desc: self.state_desc.to_ffi_type(), + status: self.status, + status_ex: self.status_ex, + t_description: self.t_description.to_ffi_type(), + time_partition: self.time_partition.to_ffi_type(), + total_amount: self.total_amount.to_ffi_type(), + transaction_at: self.transaction_at, + units: self.units.to_ffi_type(), + withdraw_at: self.withdraw_at, + withdrawable: self.withdrawable, + } + } +} + +/// Fund order detail. +#[repr(C)] +pub struct CFundOrderDetail { + /// Keyword blocks + pub keywords: *const CFundOrderKeyword, + /// Number of keyword blocks + pub num_keywords: usize, + /// The order + pub order: *const CFundOrderInfo, + /// Processing stages + pub stages: *const CFundOrderStage, + /// Number of processing stages + pub num_stages: usize, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderDetailOwned { + keywords: CVec, + order: COption, + stages: CVec, +} + +impl From for CFundOrderDetailOwned { + fn from(d: FundOrderDetail) -> Self { + CFundOrderDetailOwned { + keywords: d.keywords.into(), + order: d.order.into(), + stages: d.stages.into(), + } + } +} + +impl ToFFI for CFundOrderDetailOwned { + type FFIType = CFundOrderDetail; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderDetail { + keywords: self.keywords.to_ffi_type(), + num_keywords: self.keywords.len(), + order: self.order.to_ffi_type(), + stages: self.stages.to_ffi_type(), + num_stages: self.stages.len(), + } + } +} + +// ── FundTransaction ───────────────────────────────────────────────────────── + +/// A fund transaction / cash-flow record. +#[repr(C)] +pub struct CFundTransaction { + /// Amount + pub amount: *const c_char, + /// Category + pub category: *const c_char, + /// Created at (unix seconds) + pub created_at: i64, + /// Currency + pub currency: *const c_char, + /// Description + pub description: *const c_char, + /// Detail created at (unix seconds) + pub detail_created_at: i64, + /// Detail type + pub detail_type: *const c_char, + /// Done at (unix seconds) + pub done_at: i64, + /// Quantity description + pub quantity_description: *const c_char, + /// Redirect page + pub redirect_page: *const c_char, + /// Redirect page (v2) + pub redirect_page_v2: *const c_char, + /// Reference number + pub ref_no: *const c_char, + /// Stock quantity + pub stock_quantity: *const c_char, + /// Transaction type + pub tx_type: *const c_char, + /// Type name + pub type_name: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundTransactionOwned { + amount: CString, + category: CString, + created_at: i64, + currency: CString, + description: CString, + detail_created_at: i64, + detail_type: CString, + done_at: i64, + quantity_description: CString, + redirect_page: CString, + redirect_page_v2: CString, + ref_no: CString, + stock_quantity: CString, + tx_type: CString, + type_name: CString, +} + +impl From for CFundTransactionOwned { + fn from(t: FundTransaction) -> Self { + CFundTransactionOwned { + amount: t.amount.into(), + category: t.category.into(), + created_at: t.created_at, + currency: t.currency.into(), + description: t.description.into(), + detail_created_at: t.detail_created_at, + detail_type: t.detail_type.into(), + done_at: t.done_at, + quantity_description: t.quantity_description.into(), + redirect_page: t.redirect_page.into(), + redirect_page_v2: t.redirect_page_v2.into(), + ref_no: t.ref_no.into(), + stock_quantity: t.stock_quantity.into(), + tx_type: t.tx_type.into(), + type_name: t.type_name.into(), + } + } +} + +impl ToFFI for CFundTransactionOwned { + type FFIType = CFundTransaction; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundTransaction { + amount: self.amount.to_ffi_type(), + category: self.category.to_ffi_type(), + created_at: self.created_at, + currency: self.currency.to_ffi_type(), + description: self.description.to_ffi_type(), + detail_created_at: self.detail_created_at, + detail_type: self.detail_type.to_ffi_type(), + done_at: self.done_at, + quantity_description: self.quantity_description.to_ffi_type(), + redirect_page: self.redirect_page.to_ffi_type(), + redirect_page_v2: self.redirect_page_v2.to_ffi_type(), + ref_no: self.ref_no.to_ffi_type(), + stock_quantity: self.stock_quantity.to_ffi_type(), + tx_type: self.tx_type.to_ffi_type(), + type_name: self.type_name.to_ffi_type(), + } + } +} + +// ── FundOrderValidation ───────────────────────────────────────────────────── + +/// The result of validating a fund order. +#[repr(C)] +pub struct CFundOrderValidation { + /// Auth token to carry into submit + pub auth_token: *const c_char, + /// Risk-assessment eval address + pub eval_address: *const c_char, + /// Fund risk level + pub fund_risk_level: i32, + /// Message + pub msg: *const c_char, + /// User PI status + pub user_pi: i32, + /// User risk level + pub user_risk_level: i32, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderValidationOwned { + auth_token: CString, + eval_address: CString, + fund_risk_level: i32, + msg: CString, + user_pi: i32, + user_risk_level: i32, +} + +impl From for CFundOrderValidationOwned { + fn from(v: FundOrderValidation) -> Self { + CFundOrderValidationOwned { + auth_token: v.auth_token.into(), + eval_address: v.eval_address.into(), + fund_risk_level: v.fund_risk_level, + msg: v.msg.into(), + user_pi: v.user_pi, + user_risk_level: v.user_risk_level, + } + } +} + +impl ToFFI for CFundOrderValidationOwned { + type FFIType = CFundOrderValidation; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderValidation { + auth_token: self.auth_token.to_ffi_type(), + eval_address: self.eval_address.to_ffi_type(), + fund_risk_level: self.fund_risk_level, + msg: self.msg.to_ffi_type(), + user_pi: self.user_pi, + user_risk_level: self.user_risk_level, + } + } +} + +// ── FundOrderSubmitResponse ───────────────────────────────────────────────── + +/// The result of submitting a fund order. +#[repr(C)] +pub struct CFundOrderSubmitResponse { + /// Action (buy/sell) + pub action: *const c_char, + /// Amount + pub amount: *const c_char, + /// Fund counter id + pub counter_id: *const c_char, + /// Created at (unix seconds) + pub created_at: i64, + /// Fund name + pub fund_name: *const c_char, + /// Order id + pub id: i64, + /// Message + pub msg: *const c_char, + /// Status + pub status: i32, + /// Units + pub units: *const c_char, +} + +#[derive(Debug)] +pub(crate) struct CFundOrderSubmitResponseOwned { + action: CString, + amount: CString, + counter_id: CString, + created_at: i64, + fund_name: CString, + id: i64, + msg: CString, + status: i32, + units: CString, +} + +impl From for CFundOrderSubmitResponseOwned { + fn from(r: FundOrderSubmitResponse) -> Self { + CFundOrderSubmitResponseOwned { + action: r.action.into(), + amount: r.amount.into(), + counter_id: r.counter_id.into(), + created_at: r.created_at, + fund_name: r.fund_name.into(), + id: r.id, + msg: r.msg.into(), + status: r.status, + units: r.units.into(), + } + } +} + +impl ToFFI for CFundOrderSubmitResponseOwned { + type FFIType = CFundOrderSubmitResponse; + + fn to_ffi_type(&self) -> Self::FFIType { + CFundOrderSubmitResponse { + action: self.action.to_ffi_type(), + amount: self.amount.to_ffi_type(), + counter_id: self.counter_id.to_ffi_type(), + created_at: self.created_at, + fund_name: self.fund_name.to_ffi_type(), + id: self.id, + msg: self.msg.to_ffi_type(), + status: self.status, + units: self.units.to_ffi_type(), + } + } +} + +// ── Request option C structs ──────────────────────────────────────────────── + +/// Options for the fund list request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundsOptions { + /// Server-defined filter object as a JSON string (can be null) + pub filter: *const c_char, + /// Quick-filter ids (can be null) + pub quick_ids: *const i64, + /// Number of quick-filter ids + pub num_quick_ids: usize, + /// Earning-rate time intervals (can be null) + pub time_interval: *const *const c_char, + /// Number of time intervals + pub num_time_interval: usize, +} + +/// Options for the fund analysis / trend / comparison request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundAnalysisOptions { + /// Analysis period (can be null) + pub period: *const i32, +} + +/// Paging options (page / size). +#[derive(Debug)] +#[repr(C)] +pub struct CFundPageOptions { + /// Page number (can be null) + pub page: *const i32, + /// Page size (can be null) + pub size: *const i32, +} + +/// Net-value range options (relative months / years before now). +#[derive(Debug)] +#[repr(C)] +pub struct CFundNavRangeOptions { + /// Number of months before now (can be null) + pub month_before: *const i32, + /// Number of years before now (can be null) + pub year_before: *const i32, +} + +/// Options for the fund holdings request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundHoldingsOptions { + /// Scene (can be null) + pub scene: *const i32, +} + +/// Options for the fund stock-holdings (reverse) request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundStockHoldingsOptions { + /// Maximum number of stocks to return (can be null) + pub limit: *const i32, +} + +/// Options for the fund positions overview request. +#[derive(Debug)] +#[repr(C)] +pub struct CFundPositionsOptions { + /// Account channel (can be null) + pub account_channel: *const c_char, + /// Account id (can be null) + pub aaid: *const i64, +} + +/// Options for a single fund position detail request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundPositionOptions { + /// Account channel (can be null) + pub account_channel: *const c_char, + /// Account id (can be null) + pub aaid: *const i64, + /// Range start (can be null) + pub start: *const c_char, + /// Range end (can be null) + pub end: *const c_char, +} + +/// Options for a single fund position cumulative-profit request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundPositionProfitsOptions { + /// Account channel (can be null) + pub account_channel: *const c_char, + /// Account id (can be null) + pub aaid: *const i64, + /// Range start (can be null) + pub start: *const c_char, + /// Range end (can be null) + pub end: *const c_char, + /// Page number (can be null) + pub page: *const i32, + /// Page size (can be null) + pub size: *const i32, +} + +/// Options for a single fund position dividend request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundPositionDividendsOptions { + /// Account channel (can be null) + pub account_channel: *const c_char, + /// Account id (can be null) + pub aaid: *const i64, + /// Currency (can be null) + pub currency: *const c_char, + /// Range start (unix seconds) (can be null) + pub start: *const i64, + /// Range end (unix seconds) (can be null) + pub end: *const i64, + /// Page number (can be null) + pub page: *const i32, + /// Page size (can be null) + pub size: *const i32, +} + +/// Options for the fund orders list request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundOrdersOptions { + /// Filter by fund counter ids (can be null) + pub counter_ids: *const *const c_char, + /// Number of fund counter ids + pub num_counter_ids: usize, + /// Filter by actions (comma-separated) (can be null) + pub actions: *const c_char, + /// Filter by states (comma-separated) (can be null) + pub states: *const c_char, + /// Filter by currency (can be null) + pub currency: *const c_char, + /// Range start (unix seconds) (can be null) + pub start: *const i64, + /// Range end (unix seconds) (can be null) + pub end: *const i64, + /// Page number (can be null) + pub page: *const i32, + /// Page size (can be null) + pub size: *const i32, +} + +/// Options for the fund transactions (cash-flow) list request. +#[derive(Debug)] +#[repr(C)] +pub struct CGetFundTransactionsOptions { + /// Account channel (can be null) + pub account_channel: *const c_char, + /// Business type (can be null) + pub business_type: *const c_char, + /// Category (can be null) + pub category: *const c_char, + /// Currencies (comma-separated) (can be null) + pub currencies: *const c_char, + /// Range start (unix seconds) (can be null) + pub start: *const i64, + /// Range end (unix seconds) (can be null) + pub end: *const i64, + /// Page number (can be null) + pub page: *const i32, + /// Page size (can be null) + pub size: *const i32, +} + +/// Options for validating a fund order. +#[derive(Debug)] +#[repr(C)] +pub struct CValidateFundOrderOptions { + /// Fund counter id + pub counter_id: *const c_char, + /// Action (buy/sell) + pub action: *const c_char, + /// Currency + pub currency: *const c_char, + /// Amount (for amount-based orders) (can be null) + pub amount: *const c_char, + /// Units (for unit-based orders) (can be null) + pub units: *const c_char, + /// Dividend option (can be null) + pub dividend_option: *const i32, + /// Fund source (can be null) + pub fund_source: *const i32, + /// Account channel (can be null) + pub account_channel: *const c_char, +} + +/// Options for submitting a fund order. +#[derive(Debug)] +#[repr(C)] +pub struct CSubmitFundOrderOptions { + /// Fund counter id + pub counter_id: *const c_char, + /// Action (buy/sell) + pub action: *const c_char, + /// Currency + pub currency: *const c_char, + /// Amount (for amount-based orders) (can be null) + pub amount: *const c_char, + /// Units (for unit-based orders) (can be null) + pub units: *const c_char, + /// Dividend option (can be null) + pub dividend_option: *const i32, + /// Fee (can be null) + pub fee: *const c_char, + /// Whether to sell all (can be null) + pub is_sell_all: *const bool, + /// Remark (can be null) + pub remark: *const c_char, + /// Trade method (can be null) + pub trade_method: *const i32, +} diff --git a/c/src/lib.rs b/c/src/lib.rs index 30d28dc09..40bdc6490 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -10,6 +10,7 @@ mod config; mod content_context; mod dca_context; mod error; +mod fund_context; mod fundamental_context; mod grid_context; mod http_client; diff --git a/c/src/trade_context/types.rs b/c/src/trade_context/types.rs index 6557e7e98..eb14ae2bf 100644 --- a/c/src/trade_context/types.rs +++ b/c/src/trade_context/types.rs @@ -1786,8 +1786,8 @@ impl ToFFI for CFundPositionChannelOwned { /// Fund position #[repr(C)] pub struct CFundPosition { - /// Fund ISIN code - pub symbol: *const c_char, + /// Fund counter id (the ISIN is the last `/`-separated segment) + pub counter_id: *const c_char, /// Current equity pub current_net_asset_value: *const CDecimal, /// Current equity time @@ -1803,7 +1803,7 @@ pub struct CFundPosition { } pub(crate) struct CFundPositionOwned { - symbol: CString, + counter_id: CString, current_net_asset_value: CDecimal, net_asset_value_day: i64, symbol_name: CString, @@ -1815,7 +1815,7 @@ pub(crate) struct CFundPositionOwned { impl From for CFundPositionOwned { fn from(position: FundPosition) -> Self { let FundPosition { - symbol, + counter_id, current_net_asset_value, net_asset_value_day, symbol_name, @@ -1824,7 +1824,7 @@ impl From for CFundPositionOwned { holding_units, } = position; Self { - symbol: symbol.into(), + counter_id: counter_id.into(), current_net_asset_value: current_net_asset_value.into(), net_asset_value_day: net_asset_value_day.unix_timestamp(), symbol_name: symbol_name.into(), @@ -1840,7 +1840,7 @@ impl ToFFI for CFundPositionOwned { fn to_ffi_type(&self) -> Self::FFIType { let CFundPositionOwned { - symbol, + counter_id, current_net_asset_value, net_asset_value_day, symbol_name, @@ -1849,7 +1849,7 @@ impl ToFFI for CFundPositionOwned { holding_units, } = self; CFundPosition { - symbol: symbol.to_ffi_type(), + counter_id: counter_id.to_ffi_type(), current_net_asset_value: current_net_asset_value.to_ffi_type(), net_asset_value_day: *net_asset_value_day, symbol_name: symbol_name.to_ffi_type(), diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 89cfa535d..585b20fed 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES src/types.cpp src/quote_context.cpp src/trade_context.cpp + src/fund_context.cpp src/oauth.cpp ) add_library( diff --git a/cpp/include/fund_context.hpp b/cpp/include/fund_context.hpp new file mode 100644 index 000000000..b53b9686c --- /dev/null +++ b/cpp/include/fund_context.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include "async_result.hpp" +#include "callback.hpp" +#include "config.hpp" +#include "types.hpp" +#include + +typedef struct lb_fund_context_t lb_fund_context_t; + +namespace longbridge { +namespace fund { + +/// Fund (mutual fund) channel context. +class FundContext +{ +private: + const lb_fund_context_t* ctx_; + +public: + FundContext(); + FundContext(const lb_fund_context_t* ctx); + FundContext(const FundContext& ctx); + FundContext(FundContext&& ctx); + ~FundContext(); + + FundContext& operator=(const FundContext& ctx); + + /// Create a FundContext from a Config. + static FundContext create(const Config& config); + + // ── fund catalog / market data ───────────────────────────────────────────── + + /// Get the hot-selling fund list + void hot_funds( + AsyncCallback> callback) const; + + /// Get the fund list + void funds(const std::optional& opts, + AsyncCallback> callback) const; + + /// Get the fund list filter options + void filters(AsyncCallback callback) const; + + /// Get fund detail + void detail(const std::string& counter_id, + AsyncCallback callback) const; + + /// Get fund analysis (level 1) + void analysis(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get fund analysis detail (level 2) + void analysis_detail( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get fund trend chart + void trend(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get fund annual returns + void annual_returns( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + /// Get fund quarterly returns + void quarterly_returns( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + /// Get fund performance figures + void performance( + const std::string& counter_id, + AsyncCallback> callback) const; + + /// Get fund performance comparison + void performance_comparison( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get fund latest net value + void nav(const std::string& counter_id, + AsyncCallback> callback) const; + + /// Get fund historical net value (paged) + void nav_history( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + /// Get fund historical net value by relative time range + void nav_range( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + /// Get a fund's top-10 holdings + void holdings(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get the stocks held by a fund (reverse lookup) + void stock_holdings( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + // ── user fund positions ──────────────────────────────────────────────────── + + /// Get the user's fund positions overview + void positions(const std::optional& opts, + AsyncCallback callback) const; + + /// Get the user's single fund position detail + void position(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get the performance figures of a held fund + void position_performance( + const std::string& counter_id, + AsyncCallback> callback) + const; + + /// Get the cumulative-profit series of a held fund + void position_profits( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + /// Get the net-value history of a held fund + void position_nav( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const; + + /// Get the dividend records of a held fund + void position_dividends( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const; + + // ── fund orders & trading ─────────────────────────────────────────────────── + + /// Get the user's fund orders + void orders(const std::optional& opts, + AsyncCallback> callback) const; + + /// Get a fund order detail + void order(int64_t order_id, + AsyncCallback callback) const; + + /// Get the user's fund transactions (cash-flow records) + void transactions( + const std::optional& opts, + AsyncCallback> callback) const; + + /// Validate a fund order before submitting + void validate_order( + const ValidateFundOrderOptions& opts, + AsyncCallback callback) const; + + /// Submit a fund order (buy / sell) + void submit_order( + const SubmitFundOrderOptions& opts, + AsyncCallback callback) const; + + /// Cancel (withdraw) a fund order + void cancel_order(int64_t order_id, + AsyncCallback callback) const; +}; + +} // namespace fund +} // namespace longbridge diff --git a/cpp/include/longbridge.hpp b/cpp/include/longbridge.hpp index d5bf92a78..617efc035 100644 --- a/cpp/include/longbridge.hpp +++ b/cpp/include/longbridge.hpp @@ -7,6 +7,7 @@ #include "alert_context.hpp" #include "dca_context.hpp" #include "grid_context.hpp" +#include "fund_context.hpp" #include "sharelist_context.hpp" #include "calendar_context.hpp" #include "fundamental_context.hpp" diff --git a/cpp/include/types.hpp b/cpp/include/types.hpp index 9fb71b237..f4114ae69 100644 --- a/cpp/include/types.hpp +++ b/cpp/include/types.hpp @@ -2164,8 +2164,8 @@ struct GetStockPositionsOptions /// Fund position struct FundPosition { - /// Fund ISIN code - std::string symbol; + /// Fund counter id (the ISIN is the last `/`-separated segment) + std::string counter_id; /// Current equity Decimal current_net_asset_value; /// Current equity time @@ -5366,4 +5366,1149 @@ struct ConversationStreamEvent } // namespace agent +namespace fund { + +// ── Request options ────────────────────────────────────────────────────────── + +/// Options for the fund list request. +struct GetFundsOptions +{ + /// Server-defined filter object as a raw JSON string + std::optional filter; + /// Quick-filter ids + std::vector quick_ids; + /// Earning-rate time intervals + std::vector time_interval; +}; + +/// Options for the fund analysis / trend / comparison request. +struct GetFundAnalysisOptions +{ + /// Analysis period + std::optional period; +}; + +/// Paging options (page / size). +struct FundPageOptions +{ + /// Page number + std::optional page; + /// Page size + std::optional size; +}; + +/// Net-value range options (relative months / years before now). +struct FundNavRangeOptions +{ + /// Number of months before now + std::optional month_before; + /// Number of years before now + std::optional year_before; +}; + +/// Options for the fund holdings request. +struct GetFundHoldingsOptions +{ + /// Scene + std::optional scene; +}; + +/// Options for the fund stock-holdings (reverse) request. +struct GetFundStockHoldingsOptions +{ + /// Maximum number of stocks to return + std::optional limit; +}; + +/// Options for the fund positions overview request. +struct GetFundPositionsOptions +{ + /// Account channel + std::optional account_channel; + /// Account id + std::optional aaid; +}; + +/// Options for a single fund position detail request. +struct GetFundPositionOptions +{ + /// Account channel + std::optional account_channel; + /// Account id + std::optional aaid; + /// Range start + std::optional start; + /// Range end + std::optional end; +}; + +/// Options for a single fund position cumulative-profit request. +struct GetFundPositionProfitsOptions +{ + /// Account channel + std::optional account_channel; + /// Account id + std::optional aaid; + /// Range start + std::optional start; + /// Range end + std::optional end; + /// Page number + std::optional page; + /// Page size + std::optional size; +}; + +/// Options for a single fund position dividend request. +struct GetFundPositionDividendsOptions +{ + /// Account channel + std::optional account_channel; + /// Account id + std::optional aaid; + /// Currency + std::optional currency; + /// Range start (unix seconds) + std::optional start; + /// Range end (unix seconds) + std::optional end; + /// Page number + std::optional page; + /// Page size + std::optional size; +}; + +/// Options for the fund orders list request. +struct GetFundOrdersOptions +{ + /// Filter by fund counter ids + std::vector counter_ids; + /// Filter by actions (comma-separated) + std::optional actions; + /// Filter by states (comma-separated) + std::optional states; + /// Filter by currency + std::optional currency; + /// Range start (unix seconds) + std::optional start; + /// Range end (unix seconds) + std::optional end; + /// Page number + std::optional page; + /// Page size + std::optional size; +}; + +/// Options for the fund transactions (cash-flow) list request. +struct GetFundTransactionsOptions +{ + /// Account channel + std::optional account_channel; + /// Business type + std::optional business_type; + /// Category + std::optional category; + /// Currencies (comma-separated) + std::optional currencies; + /// Range start (unix seconds) + std::optional start; + /// Range end (unix seconds) + std::optional end; + /// Page number + std::optional page; + /// Page size + std::optional size; +}; + +/// Options for validating a fund order. +struct ValidateFundOrderOptions +{ + /// Fund counter id + std::string counter_id; + /// Action (buy/sell) + std::string action; + /// Currency + std::string currency; + /// Amount (for amount-based orders) + std::optional amount; + /// Units (for unit-based orders) + std::optional units; + /// Dividend option + std::optional dividend_option; + /// Fund source + std::optional fund_source; + /// Account channel + std::optional account_channel; +}; + +/// Options for submitting a fund order. +struct SubmitFundOrderOptions +{ + /// Fund counter id + std::string counter_id; + /// Action (buy/sell) + std::string action; + /// Currency + std::string currency; + /// Amount (for amount-based orders) + std::optional amount; + /// Units (for unit-based orders) + std::optional units; + /// Dividend option + std::optional dividend_option; + /// Fee + std::optional fee; + /// Whether to sell all + std::optional is_sell_all; + /// Remark + std::optional remark; + /// Trade method + std::optional trade_method; +}; + +// ── Response types ─────────────────────────────────────────────────────────── + +/// A fund net-asset-value data point (latest / historical). +struct FundNavValue +{ + /// Net value change + std::string change; + /// Net value change percent + std::string change_percent; + /// Formatted change percent + std::string change_percent_format; + /// Fund counter id + std::string counter_id; + /// Fund name + std::string counter_name; + /// Currency + std::string currency; + /// Formatted date + std::string date_format; + /// ISIN + std::string isin; + /// Last update time (unix seconds) + int64_t last_update_time; + /// Net value + std::string value; + /// Formatted net value + std::string value_format; +}; + +/// A recent performance point used by the hot-fund list. +struct FundPerformancePoint +{ + /// Date + std::string date; + /// Last done value + std::string last_done; +}; + +/// A hot-selling fund entry. +struct HotFund +{ + /// Asset class + int32_t asset_class; + /// Asset class name + std::string asset_class_name; + /// Fund counter id + std::string counter_id; + /// Currency + std::string currency; + /// Earning rate + std::string earning_rate; + /// Recent performance points + std::vector fund_performances; + /// Fund name + std::string name; + /// Minimum purchase amount + std::string purchase_amount; + /// Recommendation text + std::string recommendation_text; + /// Risk level + int32_t risk_level; + /// Risk level name + std::string risk_level_name; + /// Time interval of the earning rate + std::string time_interval; +}; + +/// A fund entry in the fund list. +struct FundBrief +{ + /// Asset class + int32_t asset_class; + /// Asset class name + std::string asset_class_name; + /// Fund code + std::string code; + /// Fund counter id + std::string counter_id; + /// Currency + std::string currency; + /// Description + std::string description; + /// Earning rate + std::string earning_rate; + /// Whether the user is holding this fund + bool holding; + /// ISIN + std::string isin; + /// Fund name + std::string name; + /// Product + std::string product; + /// Minimum purchase amount + std::string purchase_amount; + /// Recommendation text + std::string recommendation_text; + /// Risk level + int32_t risk_level; + /// Risk level name + std::string risk_level_name; + /// Time interval of the earning rate + std::string time_interval; + /// Unit value + std::string unit_value; +}; + +/// Fund list filter options. Each list holds raw JSON option objects. +struct FundFilters +{ + /// Asset class options (raw JSON strings) + std::vector asset_class; + /// Company options (raw JSON strings) + std::vector company; + /// Currency options (raw JSON strings) + std::vector currency; + /// Industry category options (raw JSON strings) + std::vector industry_category_name; + /// Risk level options (raw JSON strings) + std::vector risk_level; +}; + +/// A single holding entry inside a fund's asset allocation. +struct FundAssetAllocationItem +{ + /// Security code + std::string code; + /// Security counter id + std::string counter_id; + /// Name + std::string name; + /// Position ratio + std::string position_ratio; +}; + +/// A fund's asset allocation. +struct FundAssetAllocation +{ + /// Asset type + int32_t asset_type; + /// Allocation entries + std::vector lists; + /// Report date + std::string report_date; +}; + +/// Fund detail. +struct FundDetail +{ + /// Additional purchase amount + std::string additional_purchase_amount; + /// Affirm day + int32_t affirm_day; + /// Amount affirm day + std::string amount_affirm_day; + /// Asset allocation + std::optional asset_allocation; + /// Asset class + int32_t asset_class; + /// Asset class name + std::string asset_class_name; + /// Bill purchase rate + std::string bill_purchase_rate; + /// Channel + std::string channel; + /// Close period + std::string close_period; + /// Fund code + std::string code; + /// Currency + std::string currency; + /// Cut off time + std::string cut_off_time; + /// Whether it is a derivative + bool derivatives; + /// Done day + int32_t done_day; + /// Excess return fee + std::string excess_return_fee; + /// GST rate + std::string gst_rate; + /// Introduction + std::string introduce; + /// Whether it is a cash-plus fund + bool is_cash_plus; + /// Whether it is a complex product + bool is_complex; + /// Whether it is a new cash-plus fund + bool is_new_cash_plus; + /// Whether it is a Yinghebao fund + bool is_yinghebao; + /// ISIN + std::string isin; + /// Management rate + std::string manage_rate; + /// Manager + std::string manager; + /// Minimum holding cash + std::string min_hold_cash; + /// Minimum holding share + std::string min_hold_share; + /// Minimum sell share + std::string min_sell_share; + /// Month raise day + std::string month_raise_day; + /// Fund name + std::string name; + /// Net value deadline + std::string nav_deadline; + /// Whether it is no-load + bool no_load; + /// Open date + std::string open_date; + /// Open period + std::string open_period; + /// Product + std::string product; + /// Product information locals + std::string product_information_locals; + /// Profile + std::string profile; + /// Whether purchasable + int32_t purchasable; + /// Purchase affirm day + std::string purchase_affirm_day; + /// Minimum purchase amount + std::string purchase_amount; + /// Purchase rate + std::string purchase_rate; + /// Rating + int32_t rating; + /// Whether redeemable + int32_t redeemable; + /// Redemption advance day + std::string redemption_advance_day; + /// Redemption amount + std::string redemption_amount; + /// Redemption close period text + std::string redemption_close_period_shows; + /// Redemption done day + std::string redemption_done_day; + /// Redemption open day text + std::string redemption_open_day_shows; + /// Risk level + int32_t risk_level; + /// Risk level name + std::string risk_level_name; + /// Verify status + int32_t verify_status; + /// Whether it is a virtual currency fund + bool virtual_currency; + /// Year to date yield + std::string year_to_date_yield; + /// Year to date yield type + int32_t ytd_yield_type; +}; + +/// Fund analysis (level 1). +struct FundAnalysis +{ + /// Actual period + int32_t actual_period; + /// Cost level (raw JSON string) + std::string cost_level; + /// Return ability (raw JSON string) + std::string return_ability; + /// Risk ability (raw JSON string) + std::string risk_ability; + /// Updated at + std::string updated_at; + /// Value for money (raw JSON string) + std::string value_for_money; + /// Whether visible + bool visible; +}; + +/// Fund analysis detail (level 2). +struct FundAnalysisDetail +{ + /// Actual period + int32_t actual_period; + /// Available periods + std::vector available_periods; + /// Cost level (raw JSON string) + std::string cost_level; + /// Return ability (raw JSON string) + std::string return_ability; + /// Risk ability (raw JSON string) + std::string risk_ability; + /// Updated at + std::string updated_at; + /// Value for money (raw JSON string) + std::string value_for_money; + /// Whether visible + bool visible; +}; + +/// A benchmark contrast series in a fund trend chart. +struct FundTrendContrast +{ + /// Benchmark name + std::string benchmark_name; + /// Performance points (raw JSON strings) + std::vector performances; +}; + +/// Fund trend chart. +struct FundTrend +{ + /// Actual period + int32_t actual_period; + /// Available periods + std::vector available_periods; + /// Category average performances (raw JSON strings) + std::vector category_average_performances; + /// Benchmark contrast performances + std::optional contrast_performances; + /// Fund performances (raw JSON strings) + std::vector fund_performances; +}; + +/// A named contrast performance series. +struct FundNamedContrast +{ + /// Series name + std::string name; + /// Performance points (raw JSON strings) + std::vector performances; +}; + +/// Fund performance comparison. +struct FundPerformanceComparison +{ + /// Contrast performance series + std::vector contrast_performances; + /// Fund performances (raw JSON strings) + std::vector fund_performances; +}; + +/// A fund annual return entry. +struct FundAnnualReturn +{ + /// Change percent + std::string change_percent; + /// Year + int32_t year; +}; + +/// A fund quarterly return entry. +struct FundQuarterlyReturn +{ + /// Change percent + std::string change_percent; + /// Quarter + int32_t quarter; + /// Year + int32_t year; +}; + +/// A fund's detailed performance figures. +struct FundPerformance +{ + /// Annualized return (5y) + std::string annualized_return_five; + /// Annualized return (1y) + std::string annualized_return_one; + /// Annualized return (10y) + std::string annualized_return_ten; + /// Annualized return (3y) + std::string annualized_return_three; + /// Annualized return (2y) + std::string annualized_return_two; + /// Fund counter id + std::string counter_id; + /// Fund name + std::string fund_name; + /// Rank (5y) + int32_t performance_rank_five_years; + /// Rank (1d) + int32_t performance_rank_one_day; + /// Rank (1m) + int32_t performance_rank_one_month; + /// Rank (1w) + int32_t performance_rank_one_week; + /// Rank (1y) + int32_t performance_rank_one_year; + /// Rank (6m) + int32_t performance_rank_six_months; + /// Rank (10y) + int32_t performance_rank_ten_years; + /// Rank (3m) + int32_t performance_rank_three_months; + /// Rank (3y) + int32_t performance_rank_three_years; + /// Rank (2y) + int32_t performance_rank_two_years; + /// Rank (ytd) + int32_t performance_rank_ytd; + /// Return (5y) + std::string performance_return_five_years; + /// Return (1d) + std::string performance_return_one_day; + /// Return (1m) + std::string performance_return_one_month; + /// Return (1w) + std::string performance_return_one_week; + /// Return (1y) + std::string performance_return_one_year; + /// Return (6m) + std::string performance_return_six_months; + /// Return (10y) + std::string performance_return_ten_years; + /// Return (3m) + std::string performance_return_three_months; + /// Return (3y) + std::string performance_return_three_years; + /// Return (2y) + std::string performance_return_two_years; + /// Return (ytd) + std::string performance_return_ytd; + /// Total peers (5y) + int32_t performance_total_five_years; + /// Total peers (1d) + int32_t performance_total_one_day; + /// Total peers (1m) + int32_t performance_total_one_month; + /// Total peers (1w) + int32_t performance_total_one_week; + /// Total peers (1y) + int32_t performance_total_one_year; + /// Total peers (6m) + int32_t performance_total_six_months; + /// Total peers (10y) + int32_t performance_total_ten_years; + /// Total peers (3m) + int32_t performance_total_three_months; + /// Total peers (3y) + int32_t performance_total_three_years; + /// Total peers (2y) + int32_t performance_total_two_years; + /// Total peers (ytd) + int32_t performance_total_ytd; + /// Seven days annualized + std::string seven_days_annualized; + /// Ten thousand price + std::string ten_thousand_price; + /// Update time (unix seconds) + int64_t update_time; +}; + +/// A single fund holding (top-10 holdings). +struct FundHolding +{ + /// Bond type + std::string bond_type; + /// Bond type name + std::string bond_type_name; + /// Country name + std::string country_name; + /// Holding type + std::string holding_type; + /// Industry name + std::string industry_name; + /// Market value + std::string market_value; + /// Maturity date + std::string maturity_date; + /// Name + std::string name; + /// Share change + std::string share_change; + /// Share change percent + std::string share_change_percent; + /// Shares + std::string shares; + /// Weighting + std::string weighting; +}; + +/// A fund's top-10 holdings. +struct FundHoldings +{ + /// Holding entries + std::vector holdings; + /// Report date + std::string report_date; + /// Total weighting + std::string weighting; +}; + +/// A stock held by the fund (reverse lookup). +struct FundStockHolding +{ + /// Stock code + std::string code; + /// Stock counter id + std::string counter_id; + /// Currency + std::string currency; + /// Stock name + std::string name; + /// Position ratio + std::string position_ratio; + /// Report date + std::string report_date; +}; + +/// A single fund position held by the user. +struct FundPosition +{ + /// Holding amount + std::string amount; + /// Fund counter id + std::string counter_id; + /// Currency + std::string currency; + /// Frozen units + std::string freeze_units; + /// Holding profit + std::string holding_profit; + /// Holding units + std::string holding_units; + /// Fund name + std::string name; + /// Recent profit + std::string recent_profit; + /// Recent trading day (unix seconds) + int64_t recent_trading_day; + /// Accumulated recent profit + std::string sum_recent_profit; +}; + +/// The user's fund positions overview. +struct FundPositions +{ + /// Account channel + std::string account_channel; + /// Position entries + std::vector list; + /// Pending buy orders amount + std::string pending_buy_orders; + /// Recent trading day (unix seconds) + int64_t recent_trading_day; + /// Sold pending credit orders amount + std::string sold_pending_credit_orders; +}; + +/// A dated value point. +struct FundDatedValue +{ + /// Date (unix seconds) + int64_t date; + /// Value + std::string value; +}; + +/// A fund unit-value point (position view). +struct FundUnitValue +{ + /// Date (unix seconds) + int64_t date; + /// Day increase rate + std::string day_increase_rate; + /// Total value + std::string total_value; + /// Unit value + std::string unit_value; +}; + +/// Detail values of a single fund position. +struct FundPositionDetailValues +{ + /// Amount + std::string amount; + /// Currency + std::string currency; + /// Holding cost + std::string holding_cost; + /// Holding profit + std::string holding_profit; + /// Holding profit rate + std::string holding_profit_rate; + /// Holding units + std::string holding_units; + /// Holding value + std::string holding_value; + /// Pending buy value + std::string pending_buy_value; + /// Pending sell value + std::string pending_sell_value; + /// Accumulated profit (to date) + std::string profit_amount_accum_td; + /// Accumulated profit rate (to date) + std::string profit_amount_accum_td_rate; + /// Recent profit + std::string recent_profit; + /// Recent trading day (unix seconds) + int64_t recent_tradingday; + /// Recent unit value + std::string recent_unit_value; + /// Sold pending-confirm units + std::string sold_pending_confirm_units; +}; + +/// Detail of a single fund position. +struct FundPositionDetail +{ + /// Detail values + std::optional detail_values; + /// Accumulated profit series + std::vector sum_profit; + /// Unit value series + std::vector ut_value; +}; + +/// Performance figures for a held fund. +struct FundPositionPerformance +{ + /// Annualized return (5y) + std::string annualized_return_five; + /// Annualized return (1y) + std::string annualized_return_one; + /// Annualized return (10y) + std::string annualized_return_ten; + /// Annualized return (3y) + std::string annualized_return_three; + /// Annualized return (2y) + std::string annualized_return_two; + /// Fund counter id + std::string counter_id; + /// Fund name + std::string fund_name; + /// Return (5y) + std::string performance_return_five_years; + /// Return (1d) + std::string performance_return_one_day; + /// Return (1m) + std::string performance_return_one_month; + /// Return (1w) + std::string performance_return_one_week; + /// Return (1y) + std::string performance_return_one_year; + /// Return (6m) + std::string performance_return_six_months; + /// Return (10y) + std::string performance_return_ten_years; + /// Return (3m) + std::string performance_return_three_months; + /// Return (3y) + std::string performance_return_three_years; + /// Return (2y) + std::string performance_return_two_years; + /// Return (ytd) + std::string performance_return_ytd; + /// Update time (unix seconds) + int64_t update_time; +}; + +/// The user's cumulative profit for a held fund. +struct FundPositionProfits +{ + /// Currency + std::string currency; + /// Profit series + std::vector history_value; + /// Last update time (unix seconds) + int64_t last_update_time; + /// Total profit + std::string sum_profit; +}; + +/// A held-fund net-value point (position view). +struct FundPositionNav +{ + /// Net value change + std::string change; + /// Net value change percent + std::string change_percent; + /// Fund counter id + std::string counter_id; + /// Fund name + std::string counter_name; + /// Last update time (unix seconds) + int64_t last_update_time; + /// Net value + std::string value; +}; + +/// A cash dividend record for a held fund. +struct FundDividend +{ + /// Amount + std::string amount; + /// Fund counter id + std::string counter_id; + /// Currency + std::string currency; + /// Date (unix seconds) + int64_t date; + /// Dividend method + std::string div_method; + /// Fund name + std::string name; +}; + +/// The user's dividend records for a held fund. +struct FundDividends +{ + /// Currency + std::string currency; + /// Dividend records + std::vector div_cash_infos; + /// Latest dividend date (unix seconds) + int64_t lastest_date; + /// Total cash dividend + std::string total_div_cash; +}; + +/// A fund order (list view). +struct FundOrder +{ + /// Action (buy/sell) + std::string action; + /// Amount + std::string amount; + /// Fund counter id + std::string counter_id; + /// Created at (unix seconds) + int64_t created_at; + /// Currency + std::string currency; + /// Fund name + std::string fund_name; + /// Order id + int64_t id; + /// Whether it is an auto (DCA) order + bool is_auto; + /// Net worth + std::string net_worth; + /// Product type + std::string product_type; + /// State + std::string state; + /// State description + std::string state_desc; + /// Units + std::string units; +}; + +/// A keyword block in a fund order detail. +struct FundOrderKeyword +{ + /// Content + std::string content; + /// Group + std::string group; + /// Key + std::string key; + /// Line strategy + std::string line_strategy; + /// Title + std::string title; +}; + +/// A processing stage in a fund order detail. +struct FundOrderStage +{ + /// Description + std::string desc; + /// Key + std::string key; + /// Link + std::string link; + /// Link text + std::string link_text; + /// Progress + std::string progress; + /// Stage + std::string stage; +}; + +/// The full information of a fund order. +struct FundOrderInfo +{ + /// Account id + int64_t aaid; + /// Account channel + std::string account_channel; + /// Action (buy/sell) + std::string action; + /// Amount + std::string amount; + /// Channel + std::string channel; + /// Fund counter id + std::string counter_id; + /// Created at (unix seconds) + int64_t created_at; + /// Currency + std::string currency; + /// Dividend option + std::string dividend_option; + /// Equity time (unix seconds) + int64_t eq_at; + /// Fee + std::string fee; + /// Fund name + std::string fund_name; + /// Fund source + std::string fund_source; + /// Histories + std::string histories; + /// Order id + int64_t id; + /// Message + std::string message; + /// Net worth + std::string net_worth; + /// Price time (unix seconds) + int64_t price_at; + /// Processed at (unix seconds) + int64_t processed_at; + /// Product type + std::string product_type; + /// Whether repurchaseable + bool repurchaseable; + /// Sale proceeds + std::string sale_proceeds; + /// Sales charge + std::string sales_charge; + /// Sales price + std::string sales_price; + /// Sales unit + std::string sales_unit; + /// State + std::string state; + /// State description + std::string state_desc; + /// Status + int32_t status; + /// Extended status + int32_t status_ex; + /// T+ description + std::string t_description; + /// Time partition + std::string time_partition; + /// Total amount + std::string total_amount; + /// Transaction at (unix seconds) + int64_t transaction_at; + /// Units + std::string units; + /// Withdraw at (unix seconds) + int64_t withdraw_at; + /// Whether withdrawable + bool withdrawable; +}; + +/// Fund order detail. +struct FundOrderDetail +{ + /// Keyword blocks + std::vector keywords; + /// The order + std::optional order; + /// Processing stages + std::vector stages; +}; + +/// A fund transaction / cash-flow record. +struct FundTransaction +{ + /// Amount + std::string amount; + /// Category + std::string category; + /// Created at (unix seconds) + int64_t created_at; + /// Currency + std::string currency; + /// Description + std::string description; + /// Detail created at (unix seconds) + int64_t detail_created_at; + /// Detail type + std::string detail_type; + /// Done at (unix seconds) + int64_t done_at; + /// Quantity description + std::string quantity_description; + /// Redirect page + std::string redirect_page; + /// Redirect page (v2) + std::string redirect_page_v2; + /// Reference number + std::string ref_no; + /// Stock quantity + std::string stock_quantity; + /// Transaction type + std::string tx_type; + /// Type name + std::string type_name; +}; + +/// The result of validating a fund order. +struct FundOrderValidation +{ + /// Auth token to carry into submit + std::string auth_token; + /// Risk-assessment eval address + std::string eval_address; + /// Fund risk level + int32_t fund_risk_level; + /// Message + std::string msg; + /// User PI status + int32_t user_pi; + /// User risk level + int32_t user_risk_level; +}; + +/// The result of submitting a fund order. +struct FundOrderSubmitResponse +{ + /// Action (buy/sell) + std::string action; + /// Amount + std::string amount; + /// Fund counter id + std::string counter_id; + /// Created at (unix seconds) + int64_t created_at; + /// Fund name + std::string fund_name; + /// Order id + int64_t id; + /// Message + std::string msg; + /// Status + int32_t status; + /// Units + std::string units; +}; + +} // namespace fund + } // namespace longbridge \ No newline at end of file diff --git a/cpp/src/convert.hpp b/cpp/src/convert.hpp index f87c84087..b15920a2a 100644 --- a/cpp/src/convert.hpp +++ b/cpp/src/convert.hpp @@ -1,6 +1,7 @@ #pragma once #include "longbridge.h" +#include "fund_ffi.hpp" #include "types.hpp" #include "portfolio_context.hpp" #include @@ -135,6 +136,47 @@ using longbridge::grid::GridTriggerHistoryResponse; using longbridge::trade::PushGridOrderChanged; using longbridge::grid::SubmitGridOrderResponse; using longbridge::grid::TriggerOrder; +using longbridge::fund::FundAnalysis; +using longbridge::fund::FundAnalysisDetail; +using longbridge::fund::FundAnnualReturn; +using longbridge::fund::FundAssetAllocation; +using longbridge::fund::FundAssetAllocationItem; +using longbridge::fund::FundBrief; +using longbridge::fund::FundDatedValue; +using longbridge::fund::FundDetail; +using longbridge::fund::FundDividend; +using longbridge::fund::FundDividends; +using longbridge::fund::FundFilters; +using longbridge::fund::FundHolding; +using longbridge::fund::FundHoldings; +using longbridge::fund::FundNamedContrast; +using longbridge::fund::FundNavValue; +using longbridge::fund::FundOrder; +using longbridge::fund::FundOrderDetail; +using longbridge::fund::FundOrderInfo; +using longbridge::fund::FundOrderKeyword; +using longbridge::fund::FundOrderStage; +using longbridge::fund::FundOrderSubmitResponse; +using longbridge::fund::FundOrderValidation; +using longbridge::fund::FundPerformance; +using longbridge::fund::FundPerformanceComparison; +using longbridge::fund::FundPerformancePoint; +// NB: `FundPosition` is intentionally NOT imported here — it collides with +// `longbridge::trade::FundPosition`. The fund converters qualify it as +// `fund::FundPosition`. +using longbridge::fund::FundPositionDetail; +using longbridge::fund::FundPositionDetailValues; +using longbridge::fund::FundPositionNav; +using longbridge::fund::FundPositionPerformance; +using longbridge::fund::FundPositionProfits; +using longbridge::fund::FundPositions; +using longbridge::fund::FundQuarterlyReturn; +using longbridge::fund::FundStockHolding; +using longbridge::fund::FundTransaction; +using longbridge::fund::FundTrend; +using longbridge::fund::FundTrendContrast; +using longbridge::fund::FundUnitValue; +using longbridge::fund::HotFund; using longbridge::quote::FilingItem; using longbridge::content::OwnedTopic; using longbridge::content::NewsItem; @@ -1812,7 +1854,7 @@ inline FundPosition convert(const lb_fund_position_t* position) { return FundPosition{ - position->symbol, + position->counter_id, Decimal(position->current_net_asset_value), position->net_asset_value_day, position->symbol_name, @@ -3995,6 +4037,743 @@ inline agent::ConversationStreamEvent convert(const lb_conversation_stream_event return event; } +// ── fund converters ────────────────────────────────────────────────────────── + +/// Convert a C array of JSON-encoded C strings to a vector of raw JSON strings. +inline std::vector +convert_fund_json_list(const char* const* items, uintptr_t count) +{ + std::vector out; + out.reserve(count); + for (uintptr_t i = 0; i < count; i++) { + out.emplace_back(items[i]); + } + return out; +} + +inline FundPerformancePoint +convert(const fund::ffi::CFundPerformancePoint* p) +{ + return FundPerformancePoint{ + p->date, + p->last_done, + }; +} + +inline FundNavValue +convert(const fund::ffi::CFundNavValue* v) +{ + return FundNavValue{ + v->change, + v->change_percent, + v->change_percent_format, + v->counter_id, + v->counter_name, + v->currency, + v->date_format, + v->isin, + v->last_update_time, + v->value, + v->value_format, + }; +} + +inline HotFund +convert(const fund::ffi::CHotFund* f) +{ + std::vector fund_performances; + std::transform(f->fund_performances, + f->fund_performances + f->num_fund_performances, + std::back_inserter(fund_performances), + [](auto& item) { return convert(&item); }); + + return HotFund{ + f->asset_class, + f->asset_class_name, + f->counter_id, + f->currency, + f->earning_rate, + fund_performances, + f->name, + f->purchase_amount, + f->recommendation_text, + f->risk_level, + f->risk_level_name, + f->time_interval, + }; +} + +inline FundBrief +convert(const fund::ffi::CFundBrief* f) +{ + return FundBrief{ + f->asset_class, + f->asset_class_name, + f->code, + f->counter_id, + f->currency, + f->description, + f->earning_rate, + f->holding, + f->isin, + f->name, + f->product, + f->purchase_amount, + f->recommendation_text, + f->risk_level, + f->risk_level_name, + f->time_interval, + f->unit_value, + }; +} + +inline FundFilters +convert(const fund::ffi::CFundFilters* f) +{ + return FundFilters{ + convert_fund_json_list(f->asset_class, f->num_asset_class), + convert_fund_json_list(f->company, f->num_company), + convert_fund_json_list(f->currency, f->num_currency), + convert_fund_json_list(f->industry_category_name, + f->num_industry_category_name), + convert_fund_json_list(f->risk_level, f->num_risk_level), + }; +} + +inline FundAssetAllocationItem +convert(const fund::ffi::CFundAssetAllocationItem* i) +{ + return FundAssetAllocationItem{ + i->code, + i->counter_id, + i->name, + i->position_ratio, + }; +} + +inline FundAssetAllocation +convert(const fund::ffi::CFundAssetAllocation* a) +{ + std::vector lists; + std::transform(a->lists, + a->lists + a->num_lists, + std::back_inserter(lists), + [](auto& item) { return convert(&item); }); + + return FundAssetAllocation{ + a->asset_type, + lists, + a->report_date, + }; +} + +inline FundDetail +convert(const fund::ffi::CFundDetail* d) +{ + return FundDetail{ + d->additional_purchase_amount, + d->affirm_day, + d->amount_affirm_day, + d->asset_allocation ? std::optional{ convert(d->asset_allocation) } + : std::nullopt, + d->asset_class, + d->asset_class_name, + d->bill_purchase_rate, + d->channel, + d->close_period, + d->code, + d->currency, + d->cut_off_time, + d->derivatives, + d->done_day, + d->excess_return_fee, + d->gst_rate, + d->introduce, + d->is_cash_plus, + d->is_complex, + d->is_new_cash_plus, + d->is_yinghebao, + d->isin, + d->manage_rate, + d->manager, + d->min_hold_cash, + d->min_hold_share, + d->min_sell_share, + d->month_raise_day, + d->name, + d->nav_deadline, + d->no_load, + d->open_date, + d->open_period, + d->product, + d->product_information_locals, + d->profile, + d->purchasable, + d->purchase_affirm_day, + d->purchase_amount, + d->purchase_rate, + d->rating, + d->redeemable, + d->redemption_advance_day, + d->redemption_amount, + d->redemption_close_period_shows, + d->redemption_done_day, + d->redemption_open_day_shows, + d->risk_level, + d->risk_level_name, + d->verify_status, + d->virtual_currency, + d->year_to_date_yield, + d->ytd_yield_type, + }; +} + +inline FundAnalysis +convert(const fund::ffi::CFundAnalysis* a) +{ + return FundAnalysis{ + a->actual_period, + a->cost_level, + a->return_ability, + a->risk_ability, + a->updated_at, + a->value_for_money, + a->visible, + }; +} + +inline FundAnalysisDetail +convert(const fund::ffi::CFundAnalysisDetail* a) +{ + std::vector available_periods( + a->available_periods, a->available_periods + a->num_available_periods); + + return FundAnalysisDetail{ + a->actual_period, + available_periods, + a->cost_level, + a->return_ability, + a->risk_ability, + a->updated_at, + a->value_for_money, + a->visible, + }; +} + +inline FundTrendContrast +convert(const fund::ffi::CFundTrendContrast* c) +{ + return FundTrendContrast{ + c->benchmark_name, + convert_fund_json_list(c->performances, c->num_performances), + }; +} + +inline FundTrend +convert(const fund::ffi::CFundTrend* t) +{ + std::vector available_periods( + t->available_periods, t->available_periods + t->num_available_periods); + + return FundTrend{ + t->actual_period, + available_periods, + convert_fund_json_list(t->category_average_performances, + t->num_category_average_performances), + t->contrast_performances ? std::optional{ convert(t->contrast_performances) } + : std::nullopt, + convert_fund_json_list(t->fund_performances, t->num_fund_performances), + }; +} + +inline FundNamedContrast +convert(const fund::ffi::CFundNamedContrast* c) +{ + return FundNamedContrast{ + c->name, + convert_fund_json_list(c->performances, c->num_performances), + }; +} + +inline FundPerformanceComparison +convert(const fund::ffi::CFundPerformanceComparison* c) +{ + std::vector contrast_performances; + std::transform(c->contrast_performances, + c->contrast_performances + c->num_contrast_performances, + std::back_inserter(contrast_performances), + [](auto& item) { return convert(&item); }); + + return FundPerformanceComparison{ + contrast_performances, + convert_fund_json_list(c->fund_performances, c->num_fund_performances), + }; +} + +inline FundAnnualReturn +convert(const fund::ffi::CFundAnnualReturn* r) +{ + return FundAnnualReturn{ + r->change_percent, + r->year, + }; +} + +inline FundQuarterlyReturn +convert(const fund::ffi::CFundQuarterlyReturn* r) +{ + return FundQuarterlyReturn{ + r->change_percent, + r->quarter, + r->year, + }; +} + +inline FundPerformance +convert(const fund::ffi::CFundPerformance* p) +{ + return FundPerformance{ + p->annualized_return_five, + p->annualized_return_one, + p->annualized_return_ten, + p->annualized_return_three, + p->annualized_return_two, + p->counter_id, + p->fund_name, + p->performance_rank_five_years, + p->performance_rank_one_day, + p->performance_rank_one_month, + p->performance_rank_one_week, + p->performance_rank_one_year, + p->performance_rank_six_months, + p->performance_rank_ten_years, + p->performance_rank_three_months, + p->performance_rank_three_years, + p->performance_rank_two_years, + p->performance_rank_ytd, + p->performance_return_five_years, + p->performance_return_one_day, + p->performance_return_one_month, + p->performance_return_one_week, + p->performance_return_one_year, + p->performance_return_six_months, + p->performance_return_ten_years, + p->performance_return_three_months, + p->performance_return_three_years, + p->performance_return_two_years, + p->performance_return_ytd, + p->performance_total_five_years, + p->performance_total_one_day, + p->performance_total_one_month, + p->performance_total_one_week, + p->performance_total_one_year, + p->performance_total_six_months, + p->performance_total_ten_years, + p->performance_total_three_months, + p->performance_total_three_years, + p->performance_total_two_years, + p->performance_total_ytd, + p->seven_days_annualized, + p->ten_thousand_price, + p->update_time, + }; +} + +inline FundHolding +convert(const fund::ffi::CFundHolding* h) +{ + return FundHolding{ + h->bond_type, + h->bond_type_name, + h->country_name, + h->holding_type, + h->industry_name, + h->market_value, + h->maturity_date, + h->name, + h->share_change, + h->share_change_percent, + h->shares, + h->weighting, + }; +} + +inline FundHoldings +convert(const fund::ffi::CFundHoldings* h) +{ + std::vector holdings; + std::transform(h->holdings, + h->holdings + h->num_holdings, + std::back_inserter(holdings), + [](auto& item) { return convert(&item); }); + + return FundHoldings{ + holdings, + h->report_date, + h->weighting, + }; +} + +inline FundStockHolding +convert(const fund::ffi::CFundStockHolding* h) +{ + return FundStockHolding{ + h->code, + h->counter_id, + h->currency, + h->name, + h->position_ratio, + h->report_date, + }; +} + +inline fund::FundPosition +convert(const fund::ffi::CFundPosition* p) +{ + return fund::FundPosition{ + p->amount, + p->counter_id, + p->currency, + p->freeze_units, + p->holding_profit, + p->holding_units, + p->name, + p->recent_profit, + p->recent_trading_day, + p->sum_recent_profit, + }; +} + +inline FundPositions +convert(const fund::ffi::CFundPositions* p) +{ + std::vector list; + std::transform(p->list, + p->list + p->num_list, + std::back_inserter(list), + [](auto& item) { return convert(&item); }); + + return FundPositions{ + p->account_channel, + list, + p->pending_buy_orders, + p->recent_trading_day, + p->sold_pending_credit_orders, + }; +} + +inline FundDatedValue +convert(const fund::ffi::CFundDatedValue* v) +{ + return FundDatedValue{ + v->date, + v->value, + }; +} + +inline FundUnitValue +convert(const fund::ffi::CFundUnitValue* v) +{ + return FundUnitValue{ + v->date, + v->day_increase_rate, + v->total_value, + v->unit_value, + }; +} + +inline FundPositionDetailValues +convert(const fund::ffi::CFundPositionDetailValues* v) +{ + return FundPositionDetailValues{ + v->amount, + v->currency, + v->holding_cost, + v->holding_profit, + v->holding_profit_rate, + v->holding_units, + v->holding_value, + v->pending_buy_value, + v->pending_sell_value, + v->profit_amount_accum_td, + v->profit_amount_accum_td_rate, + v->recent_profit, + v->recent_tradingday, + v->recent_unit_value, + v->sold_pending_confirm_units, + }; +} + +inline FundPositionDetail +convert(const fund::ffi::CFundPositionDetail* d) +{ + std::vector sum_profit; + std::transform(d->sum_profit, + d->sum_profit + d->num_sum_profit, + std::back_inserter(sum_profit), + [](auto& item) { return convert(&item); }); + + std::vector ut_value; + std::transform(d->ut_value, + d->ut_value + d->num_ut_value, + std::back_inserter(ut_value), + [](auto& item) { return convert(&item); }); + + return FundPositionDetail{ + d->detail_values ? std::optional{ convert(d->detail_values) } + : std::nullopt, + sum_profit, + ut_value, + }; +} + +inline FundPositionPerformance +convert(const fund::ffi::CFundPositionPerformance* p) +{ + return FundPositionPerformance{ + p->annualized_return_five, + p->annualized_return_one, + p->annualized_return_ten, + p->annualized_return_three, + p->annualized_return_two, + p->counter_id, + p->fund_name, + p->performance_return_five_years, + p->performance_return_one_day, + p->performance_return_one_month, + p->performance_return_one_week, + p->performance_return_one_year, + p->performance_return_six_months, + p->performance_return_ten_years, + p->performance_return_three_months, + p->performance_return_three_years, + p->performance_return_two_years, + p->performance_return_ytd, + p->update_time, + }; +} + +inline FundPositionProfits +convert(const fund::ffi::CFundPositionProfits* p) +{ + std::vector history_value; + std::transform(p->history_value, + p->history_value + p->num_history_value, + std::back_inserter(history_value), + [](auto& item) { return convert(&item); }); + + return FundPositionProfits{ + p->currency, + history_value, + p->last_update_time, + p->sum_profit, + }; +} + +inline FundPositionNav +convert(const fund::ffi::CFundPositionNav* v) +{ + return FundPositionNav{ + v->change, + v->change_percent, + v->counter_id, + v->counter_name, + v->last_update_time, + v->value, + }; +} + +inline FundDividend +convert(const fund::ffi::CFundDividend* d) +{ + return FundDividend{ + d->amount, + d->counter_id, + d->currency, + d->date, + d->div_method, + d->name, + }; +} + +inline FundDividends +convert(const fund::ffi::CFundDividends* d) +{ + std::vector div_cash_infos; + std::transform(d->div_cash_infos, + d->div_cash_infos + d->num_div_cash_infos, + std::back_inserter(div_cash_infos), + [](auto& item) { return convert(&item); }); + + return FundDividends{ + d->currency, + div_cash_infos, + d->lastest_date, + d->total_div_cash, + }; +} + +inline FundOrder +convert(const fund::ffi::CFundOrder* o) +{ + return FundOrder{ + o->action, + o->amount, + o->counter_id, + o->created_at, + o->currency, + o->fund_name, + o->id, + o->is_auto, + o->net_worth, + o->product_type, + o->state, + o->state_desc, + o->units, + }; +} + +inline FundOrderKeyword +convert(const fund::ffi::CFundOrderKeyword* k) +{ + return FundOrderKeyword{ + k->content, + k->group, + k->key, + k->line_strategy, + k->title, + }; +} + +inline FundOrderStage +convert(const fund::ffi::CFundOrderStage* s) +{ + return FundOrderStage{ + s->desc, + s->key, + s->link, + s->link_text, + s->progress, + s->stage, + }; +} + +inline FundOrderInfo +convert(const fund::ffi::CFundOrderInfo* o) +{ + return FundOrderInfo{ + o->aaid, + o->account_channel, + o->action, + o->amount, + o->channel, + o->counter_id, + o->created_at, + o->currency, + o->dividend_option, + o->eq_at, + o->fee, + o->fund_name, + o->fund_source, + o->histories, + o->id, + o->message, + o->net_worth, + o->price_at, + o->processed_at, + o->product_type, + o->repurchaseable, + o->sale_proceeds, + o->sales_charge, + o->sales_price, + o->sales_unit, + o->state, + o->state_desc, + o->status, + o->status_ex, + o->t_description, + o->time_partition, + o->total_amount, + o->transaction_at, + o->units, + o->withdraw_at, + o->withdrawable, + }; +} + +inline FundOrderDetail +convert(const fund::ffi::CFundOrderDetail* d) +{ + std::vector keywords; + std::transform(d->keywords, + d->keywords + d->num_keywords, + std::back_inserter(keywords), + [](auto& item) { return convert(&item); }); + + std::vector stages; + std::transform(d->stages, + d->stages + d->num_stages, + std::back_inserter(stages), + [](auto& item) { return convert(&item); }); + + return FundOrderDetail{ + keywords, + d->order ? std::optional{ convert(d->order) } : std::nullopt, + stages, + }; +} + +inline FundTransaction +convert(const fund::ffi::CFundTransaction* t) +{ + return FundTransaction{ + t->amount, + t->category, + t->created_at, + t->currency, + t->description, + t->detail_created_at, + t->detail_type, + t->done_at, + t->quantity_description, + t->redirect_page, + t->redirect_page_v2, + t->ref_no, + t->stock_quantity, + t->tx_type, + t->type_name, + }; +} + +inline FundOrderValidation +convert(const fund::ffi::CFundOrderValidation* v) +{ + return FundOrderValidation{ + v->auth_token, + v->eval_address, + v->fund_risk_level, + v->msg, + v->user_pi, + v->user_risk_level, + }; +} + +inline FundOrderSubmitResponse +convert(const fund::ffi::CFundOrderSubmitResponse* r) +{ + return FundOrderSubmitResponse{ + r->action, + r->amount, + r->counter_id, + r->created_at, + r->fund_name, + r->id, + r->msg, + r->status, + r->units, + }; +} + } // namespace convert } // namespace longbridge diff --git a/cpp/src/fund_context.cpp b/cpp/src/fund_context.cpp new file mode 100644 index 000000000..d38fb235b --- /dev/null +++ b/cpp/src/fund_context.cpp @@ -0,0 +1,650 @@ +#include "fund_context.hpp" +#include "longbridge.h" +#include "convert.hpp" +#include "fund_ffi.hpp" +#include "utils.hpp" +#include +#include +#include +#include + +namespace longbridge { +namespace fund { + +using longbridge::convert::convert; + +FundContext::FundContext() + : ctx_(nullptr) +{ +} + +FundContext::FundContext(const lb_fund_context_t* ctx) +{ + ctx_ = ctx; + if (ctx_) + lb_fund_context_retain(ctx_); +} + +FundContext::FundContext(const FundContext& ctx) +{ + ctx_ = ctx.ctx_; + if (ctx_) + lb_fund_context_retain(ctx_); +} + +FundContext::FundContext(FundContext&& ctx) +{ + ctx_ = ctx.ctx_; + ctx.ctx_ = nullptr; +} + +FundContext::~FundContext() +{ + if (ctx_) + lb_fund_context_release(ctx_); +} + +FundContext& +FundContext::operator=(const FundContext& ctx) +{ + ctx_ = ctx.ctx_; + if (ctx_) + lb_fund_context_retain(ctx_); + return *this; +} + +FundContext +FundContext::create(const Config& config) +{ + auto* ptr = lb_fund_context_new(config); + FundContext ctx(ptr); + if (ptr) + lb_fund_context_release(ptr); + return ctx; +} + +// A vector-returning callback: `res->data` is a `const CElem*` array of +// `res->length` items. +#define FUND_VEC_CALLBACK(Elem, Value) \ + [](auto res) { \ + auto callback_ptr = \ + callback::get_async_callback>( \ + res->userdata); \ + FundContext ctx((const lb_fund_context_t*)res->ctx); \ + Status status(res->error); \ + if (status) { \ + auto rows = (const fund::ffi::Elem*)res->data; \ + std::vector rows2; \ + std::transform(rows, \ + rows + res->length, \ + std::back_inserter(rows2), \ + [](auto& row) { return convert(&row); }); \ + (*callback_ptr)(AsyncResult>( \ + ctx, std::move(status), &rows2)); \ + } else { \ + (*callback_ptr)(AsyncResult>( \ + ctx, std::move(status), nullptr)); \ + } \ + } + +// A single-object-returning callback: `res->data` is a `const CElem*`. +#define FUND_OBJ_CALLBACK(Elem, Value) \ + [](auto res) { \ + auto callback_ptr = \ + callback::get_async_callback(res->userdata); \ + FundContext ctx((const lb_fund_context_t*)res->ctx); \ + Status status(res->error); \ + if (status) { \ + Value resp = convert((const fund::ffi::Elem*)res->data); \ + (*callback_ptr)( \ + AsyncResult(ctx, std::move(status), &resp)); \ + } else { \ + (*callback_ptr)( \ + AsyncResult(ctx, std::move(status), nullptr)); \ + } \ + } + +void +FundContext::hot_funds( + AsyncCallback> callback) const +{ + lb_fund_context_hot_funds( + ctx_, + FUND_VEC_CALLBACK(CHotFund, HotFund), + new AsyncCallback>(callback)); +} + +void +FundContext::funds( + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_get_funds_options_t opts2 = { nullptr, nullptr, 0, nullptr, 0 }; + std::vector time_interval; + + if (opts) { + opts2.filter = opts->filter ? opts->filter->c_str() : nullptr; + if (!opts->quick_ids.empty()) { + opts2.quick_ids = opts->quick_ids.data(); + opts2.num_quick_ids = opts->quick_ids.size(); + } + std::transform(opts->time_interval.cbegin(), + opts->time_interval.cend(), + std::back_inserter(time_interval), + [](auto& s) { return s.c_str(); }); + if (!time_interval.empty()) { + opts2.time_interval = time_interval.data(); + opts2.num_time_interval = time_interval.size(); + } + } + + lb_fund_context_funds( + ctx_, + &opts2, + FUND_VEC_CALLBACK(CFundBrief, FundBrief), + new AsyncCallback>(callback)); +} + +void +FundContext::filters(AsyncCallback callback) const +{ + lb_fund_context_filters( + ctx_, + FUND_OBJ_CALLBACK(CFundFilters, FundFilters), + new AsyncCallback(callback)); +} + +void +FundContext::detail(const std::string& counter_id, + AsyncCallback callback) const +{ + lb_fund_context_detail( + ctx_, + counter_id.c_str(), + FUND_OBJ_CALLBACK(CFundDetail, FundDetail), + new AsyncCallback(callback)); +} + +void +FundContext::analysis(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_analysis_options_t opts2 = { nullptr }; + if (opts) + opts2.period = opts->period ? &opts->period.value() : nullptr; + + lb_fund_context_analysis( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundAnalysis, FundAnalysis), + new AsyncCallback(callback)); +} + +void +FundContext::analysis_detail( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_analysis_options_t opts2 = { nullptr }; + if (opts) + opts2.period = opts->period ? &opts->period.value() : nullptr; + + lb_fund_context_analysis_detail( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundAnalysisDetail, FundAnalysisDetail), + new AsyncCallback(callback)); +} + +void +FundContext::trend(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_analysis_options_t opts2 = { nullptr }; + if (opts) + opts2.period = opts->period ? &opts->period.value() : nullptr; + + lb_fund_context_trend( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundTrend, FundTrend), + new AsyncCallback(callback)); +} + +void +FundContext::annual_returns( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_fund_page_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_annual_returns( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundAnnualReturn, FundAnnualReturn), + new AsyncCallback>(callback)); +} + +void +FundContext::quarterly_returns( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_fund_page_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_quarterly_returns( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundQuarterlyReturn, FundQuarterlyReturn), + new AsyncCallback>(callback)); +} + +void +FundContext::performance( + const std::string& counter_id, + AsyncCallback> callback) const +{ + lb_fund_context_performance( + ctx_, + counter_id.c_str(), + FUND_VEC_CALLBACK(CFundPerformance, FundPerformance), + new AsyncCallback>(callback)); +} + +void +FundContext::performance_comparison( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_analysis_options_t opts2 = { nullptr }; + if (opts) + opts2.period = opts->period ? &opts->period.value() : nullptr; + + lb_fund_context_performance_comparison( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundPerformanceComparison, FundPerformanceComparison), + new AsyncCallback(callback)); +} + +void +FundContext::nav( + const std::string& counter_id, + AsyncCallback> callback) const +{ + lb_fund_context_nav( + ctx_, + counter_id.c_str(), + FUND_VEC_CALLBACK(CFundNavValue, FundNavValue), + new AsyncCallback>(callback)); +} + +void +FundContext::nav_history( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_fund_page_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_nav_history( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundNavValue, FundNavValue), + new AsyncCallback>(callback)); +} + +void +FundContext::nav_range( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_fund_nav_range_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.month_before = + opts->month_before ? &opts->month_before.value() : nullptr; + opts2.year_before = opts->year_before ? &opts->year_before.value() : nullptr; + } + + lb_fund_context_nav_range( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundNavValue, FundNavValue), + new AsyncCallback>(callback)); +} + +void +FundContext::holdings(const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_holdings_options_t opts2 = { nullptr }; + if (opts) + opts2.scene = opts->scene ? &opts->scene.value() : nullptr; + + lb_fund_context_holdings( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundHoldings, FundHoldings), + new AsyncCallback(callback)); +} + +void +FundContext::stock_holdings( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_get_fund_stock_holdings_options_t opts2 = { nullptr }; + if (opts) + opts2.limit = opts->limit ? &opts->limit.value() : nullptr; + + lb_fund_context_stock_holdings( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundStockHolding, FundStockHolding), + new AsyncCallback>(callback)); +} + +void +FundContext::positions(const std::optional& opts, + AsyncCallback callback) const +{ + lb_fund_positions_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.account_channel = + opts->account_channel ? opts->account_channel->c_str() : nullptr; + opts2.aaid = opts->aaid ? &opts->aaid.value() : nullptr; + } + + lb_fund_context_positions( + ctx_, + &opts2, + FUND_OBJ_CALLBACK(CFundPositions, FundPositions), + new AsyncCallback(callback)); +} + +void +FundContext::position( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_position_options_t opts2 = { nullptr, nullptr, nullptr, nullptr }; + if (opts) { + opts2.account_channel = + opts->account_channel ? opts->account_channel->c_str() : nullptr; + opts2.aaid = opts->aaid ? &opts->aaid.value() : nullptr; + opts2.start = opts->start ? opts->start->c_str() : nullptr; + opts2.end = opts->end ? opts->end->c_str() : nullptr; + } + + lb_fund_context_position( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundPositionDetail, FundPositionDetail), + new AsyncCallback(callback)); +} + +void +FundContext::position_performance( + const std::string& counter_id, + AsyncCallback> callback) + const +{ + lb_fund_context_position_performance( + ctx_, + counter_id.c_str(), + FUND_VEC_CALLBACK(CFundPositionPerformance, FundPositionPerformance), + new AsyncCallback>( + callback)); +} + +void +FundContext::position_profits( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_position_profits_options_t opts2 = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + if (opts) { + opts2.account_channel = + opts->account_channel ? opts->account_channel->c_str() : nullptr; + opts2.aaid = opts->aaid ? &opts->aaid.value() : nullptr; + opts2.start = opts->start ? opts->start->c_str() : nullptr; + opts2.end = opts->end ? opts->end->c_str() : nullptr; + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_position_profits( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundPositionProfits, FundPositionProfits), + new AsyncCallback(callback)); +} + +void +FundContext::position_nav( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_fund_nav_range_options_t opts2 = { nullptr, nullptr }; + if (opts) { + opts2.month_before = + opts->month_before ? &opts->month_before.value() : nullptr; + opts2.year_before = opts->year_before ? &opts->year_before.value() : nullptr; + } + + lb_fund_context_position_nav( + ctx_, + counter_id.c_str(), + &opts2, + FUND_VEC_CALLBACK(CFundPositionNav, FundPositionNav), + new AsyncCallback>(callback)); +} + +void +FundContext::position_dividends( + const std::string& counter_id, + const std::optional& opts, + AsyncCallback callback) const +{ + lb_get_fund_position_dividends_options_t opts2 = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + if (opts) { + opts2.account_channel = + opts->account_channel ? opts->account_channel->c_str() : nullptr; + opts2.aaid = opts->aaid ? &opts->aaid.value() : nullptr; + opts2.currency = opts->currency ? opts->currency->c_str() : nullptr; + opts2.start = opts->start ? &opts->start.value() : nullptr; + opts2.end = opts->end ? &opts->end.value() : nullptr; + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_position_dividends( + ctx_, + counter_id.c_str(), + &opts2, + FUND_OBJ_CALLBACK(CFundDividends, FundDividends), + new AsyncCallback(callback)); +} + +void +FundContext::orders( + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_get_fund_orders_options_t opts2 = { + nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + std::vector counter_ids; + + if (opts) { + std::transform(opts->counter_ids.cbegin(), + opts->counter_ids.cend(), + std::back_inserter(counter_ids), + [](auto& s) { return s.c_str(); }); + if (!counter_ids.empty()) { + opts2.counter_ids = counter_ids.data(); + opts2.num_counter_ids = counter_ids.size(); + } + opts2.actions = opts->actions ? opts->actions->c_str() : nullptr; + opts2.states = opts->states ? opts->states->c_str() : nullptr; + opts2.currency = opts->currency ? opts->currency->c_str() : nullptr; + opts2.start = opts->start ? &opts->start.value() : nullptr; + opts2.end = opts->end ? &opts->end.value() : nullptr; + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_orders( + ctx_, + &opts2, + FUND_VEC_CALLBACK(CFundOrder, FundOrder), + new AsyncCallback>(callback)); +} + +void +FundContext::order(int64_t order_id, + AsyncCallback callback) const +{ + lb_fund_context_order( + ctx_, + order_id, + FUND_OBJ_CALLBACK(CFundOrderDetail, FundOrderDetail), + new AsyncCallback(callback)); +} + +void +FundContext::transactions( + const std::optional& opts, + AsyncCallback> callback) const +{ + lb_get_fund_transactions_options_t opts2 = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + if (opts) { + opts2.account_channel = + opts->account_channel ? opts->account_channel->c_str() : nullptr; + opts2.business_type = + opts->business_type ? opts->business_type->c_str() : nullptr; + opts2.category = opts->category ? opts->category->c_str() : nullptr; + opts2.currencies = opts->currencies ? opts->currencies->c_str() : nullptr; + opts2.start = opts->start ? &opts->start.value() : nullptr; + opts2.end = opts->end ? &opts->end.value() : nullptr; + opts2.page = opts->page ? &opts->page.value() : nullptr; + opts2.size = opts->size ? &opts->size.value() : nullptr; + } + + lb_fund_context_transactions( + ctx_, + &opts2, + FUND_VEC_CALLBACK(CFundTransaction, FundTransaction), + new AsyncCallback>(callback)); +} + +void +FundContext::validate_order( + const ValidateFundOrderOptions& opts, + AsyncCallback callback) const +{ + lb_validate_fund_order_options_t opts2 = { + opts.counter_id.c_str(), + opts.action.c_str(), + opts.currency.c_str(), + opts.amount ? opts.amount->c_str() : nullptr, + opts.units ? opts.units->c_str() : nullptr, + opts.dividend_option ? &opts.dividend_option.value() : nullptr, + opts.fund_source ? &opts.fund_source.value() : nullptr, + opts.account_channel ? opts.account_channel->c_str() : nullptr, + }; + + lb_fund_context_validate_order( + ctx_, + &opts2, + FUND_OBJ_CALLBACK(CFundOrderValidation, FundOrderValidation), + new AsyncCallback(callback)); +} + +void +FundContext::submit_order( + const SubmitFundOrderOptions& opts, + AsyncCallback callback) const +{ + lb_submit_fund_order_options_t opts2 = { + opts.counter_id.c_str(), + opts.action.c_str(), + opts.currency.c_str(), + opts.amount ? opts.amount->c_str() : nullptr, + opts.units ? opts.units->c_str() : nullptr, + opts.dividend_option ? &opts.dividend_option.value() : nullptr, + opts.fee ? opts.fee->c_str() : nullptr, + opts.is_sell_all ? &opts.is_sell_all.value() : nullptr, + opts.remark ? opts.remark->c_str() : nullptr, + opts.trade_method ? &opts.trade_method.value() : nullptr, + }; + + lb_fund_context_submit_order( + ctx_, + &opts2, + FUND_OBJ_CALLBACK(CFundOrderSubmitResponse, FundOrderSubmitResponse), + new AsyncCallback(callback)); +} + +void +FundContext::cancel_order(int64_t order_id, + AsyncCallback callback) const +{ + lb_fund_context_cancel_order( + ctx_, + order_id, + [](auto res) { + auto callback_ptr = + callback::get_async_callback(res->userdata); + (*callback_ptr)(AsyncResult( + FundContext((const lb_fund_context_t*)res->ctx), + Status(res->error), + nullptr)); + }, + new AsyncCallback(callback)); +} + +} // namespace fund +} // namespace longbridge diff --git a/cpp/src/fund_ffi.hpp b/cpp/src/fund_ffi.hpp new file mode 100644 index 000000000..19498be2a --- /dev/null +++ b/cpp/src/fund_ffi.hpp @@ -0,0 +1,581 @@ +#pragma once + +// FFI mirror structs for the fund module response types. +// +// The fund C FFI layer returns these `#[repr(C)]` structs through the generic +// `void* lb_async_result_t::data` pointer, so cbindgen does not emit them into +// `longbridge.h` (and the raw `CFundPosition` name additionally collides with +// the portfolio `CFundPosition` -> `lb_fund_position_t`). These declarations +// reproduce the exact layout of `c/src/fund_context/types.rs` so the C++ +// converters can cast `res->data` and read the fields. +// +// Layout rules mirrored from the Rust `ToFFI` output: +// * `CString` -> `const char*` +// * `CVec` -> `const char* const*` + `uintptr_t num_*` +// * `CVec` -> `const T*` + `uintptr_t num_*` +// * nested struct -> embedded by value +// * COption -> `const T*` (nullable; null when `None`) +// * i64/i32/bool -> int64_t/int32_t/bool +// Field order matches the Rust `#[repr(C)]` declaration order exactly. + +#include + +namespace longbridge { +namespace fund { +namespace ffi { + +struct CFundNavValue +{ + const char* change; + const char* change_percent; + const char* change_percent_format; + const char* counter_id; + const char* counter_name; + const char* currency; + const char* date_format; + const char* isin; + int64_t last_update_time; + const char* value; + const char* value_format; +}; + +struct CFundPerformancePoint +{ + const char* date; + const char* last_done; +}; + +struct CHotFund +{ + int32_t asset_class; + const char* asset_class_name; + const char* counter_id; + const char* currency; + const char* earning_rate; + const CFundPerformancePoint* fund_performances; + uintptr_t num_fund_performances; + const char* name; + const char* purchase_amount; + const char* recommendation_text; + int32_t risk_level; + const char* risk_level_name; + const char* time_interval; +}; + +struct CFundBrief +{ + int32_t asset_class; + const char* asset_class_name; + const char* code; + const char* counter_id; + const char* currency; + const char* description; + const char* earning_rate; + bool holding; + const char* isin; + const char* name; + const char* product; + const char* purchase_amount; + const char* recommendation_text; + int32_t risk_level; + const char* risk_level_name; + const char* time_interval; + const char* unit_value; +}; + +struct CFundFilters +{ + const char* const* asset_class; + uintptr_t num_asset_class; + const char* const* company; + uintptr_t num_company; + const char* const* currency; + uintptr_t num_currency; + const char* const* industry_category_name; + uintptr_t num_industry_category_name; + const char* const* risk_level; + uintptr_t num_risk_level; +}; + +struct CFundAssetAllocationItem +{ + const char* code; + const char* counter_id; + const char* name; + const char* position_ratio; +}; + +struct CFundAssetAllocation +{ + int32_t asset_type; + const CFundAssetAllocationItem* lists; + uintptr_t num_lists; + const char* report_date; +}; + +struct CFundDetail +{ + const char* additional_purchase_amount; + int32_t affirm_day; + const char* amount_affirm_day; + const CFundAssetAllocation* asset_allocation; + int32_t asset_class; + const char* asset_class_name; + const char* bill_purchase_rate; + const char* channel; + const char* close_period; + const char* code; + const char* currency; + const char* cut_off_time; + bool derivatives; + int32_t done_day; + const char* excess_return_fee; + const char* gst_rate; + const char* introduce; + bool is_cash_plus; + bool is_complex; + bool is_new_cash_plus; + bool is_yinghebao; + const char* isin; + const char* manage_rate; + const char* manager; + const char* min_hold_cash; + const char* min_hold_share; + const char* min_sell_share; + const char* month_raise_day; + const char* name; + const char* nav_deadline; + bool no_load; + const char* open_date; + const char* open_period; + const char* product; + const char* product_information_locals; + const char* profile; + int32_t purchasable; + const char* purchase_affirm_day; + const char* purchase_amount; + const char* purchase_rate; + int32_t rating; + int32_t redeemable; + const char* redemption_advance_day; + const char* redemption_amount; + const char* redemption_close_period_shows; + const char* redemption_done_day; + const char* redemption_open_day_shows; + int32_t risk_level; + const char* risk_level_name; + int32_t verify_status; + bool virtual_currency; + const char* year_to_date_yield; + int32_t ytd_yield_type; +}; + +struct CFundAnalysis +{ + int32_t actual_period; + const char* cost_level; + const char* return_ability; + const char* risk_ability; + const char* updated_at; + const char* value_for_money; + bool visible; +}; + +struct CFundAnalysisDetail +{ + int32_t actual_period; + const int32_t* available_periods; + uintptr_t num_available_periods; + const char* cost_level; + const char* return_ability; + const char* risk_ability; + const char* updated_at; + const char* value_for_money; + bool visible; +}; + +struct CFundTrendContrast +{ + const char* benchmark_name; + const char* const* performances; + uintptr_t num_performances; +}; + +struct CFundTrend +{ + int32_t actual_period; + const int32_t* available_periods; + uintptr_t num_available_periods; + const char* const* category_average_performances; + uintptr_t num_category_average_performances; + const CFundTrendContrast* contrast_performances; + const char* const* fund_performances; + uintptr_t num_fund_performances; +}; + +struct CFundNamedContrast +{ + const char* name; + const char* const* performances; + uintptr_t num_performances; +}; + +struct CFundPerformanceComparison +{ + const CFundNamedContrast* contrast_performances; + uintptr_t num_contrast_performances; + const char* const* fund_performances; + uintptr_t num_fund_performances; +}; + +struct CFundAnnualReturn +{ + const char* change_percent; + int32_t year; +}; + +struct CFundQuarterlyReturn +{ + const char* change_percent; + int32_t quarter; + int32_t year; +}; + +struct CFundPerformance +{ + const char* annualized_return_five; + const char* annualized_return_one; + const char* annualized_return_ten; + const char* annualized_return_three; + const char* annualized_return_two; + const char* counter_id; + const char* fund_name; + int32_t performance_rank_five_years; + int32_t performance_rank_one_day; + int32_t performance_rank_one_month; + int32_t performance_rank_one_week; + int32_t performance_rank_one_year; + int32_t performance_rank_six_months; + int32_t performance_rank_ten_years; + int32_t performance_rank_three_months; + int32_t performance_rank_three_years; + int32_t performance_rank_two_years; + int32_t performance_rank_ytd; + const char* performance_return_five_years; + const char* performance_return_one_day; + const char* performance_return_one_month; + const char* performance_return_one_week; + const char* performance_return_one_year; + const char* performance_return_six_months; + const char* performance_return_ten_years; + const char* performance_return_three_months; + const char* performance_return_three_years; + const char* performance_return_two_years; + const char* performance_return_ytd; + int32_t performance_total_five_years; + int32_t performance_total_one_day; + int32_t performance_total_one_month; + int32_t performance_total_one_week; + int32_t performance_total_one_year; + int32_t performance_total_six_months; + int32_t performance_total_ten_years; + int32_t performance_total_three_months; + int32_t performance_total_three_years; + int32_t performance_total_two_years; + int32_t performance_total_ytd; + const char* seven_days_annualized; + const char* ten_thousand_price; + int64_t update_time; +}; + +struct CFundHolding +{ + const char* bond_type; + const char* bond_type_name; + const char* country_name; + const char* holding_type; + const char* industry_name; + const char* market_value; + const char* maturity_date; + const char* name; + const char* share_change; + const char* share_change_percent; + const char* shares; + const char* weighting; +}; + +struct CFundHoldings +{ + const CFundHolding* holdings; + uintptr_t num_holdings; + const char* report_date; + const char* weighting; +}; + +struct CFundStockHolding +{ + const char* code; + const char* counter_id; + const char* currency; + const char* name; + const char* position_ratio; + const char* report_date; +}; + +struct CFundPosition +{ + const char* amount; + const char* counter_id; + const char* currency; + const char* freeze_units; + const char* holding_profit; + const char* holding_units; + const char* name; + const char* recent_profit; + int64_t recent_trading_day; + const char* sum_recent_profit; +}; + +struct CFundPositions +{ + const char* account_channel; + const CFundPosition* list; + uintptr_t num_list; + const char* pending_buy_orders; + int64_t recent_trading_day; + const char* sold_pending_credit_orders; +}; + +struct CFundDatedValue +{ + int64_t date; + const char* value; +}; + +struct CFundUnitValue +{ + int64_t date; + const char* day_increase_rate; + const char* total_value; + const char* unit_value; +}; + +struct CFundPositionDetailValues +{ + const char* amount; + const char* currency; + const char* holding_cost; + const char* holding_profit; + const char* holding_profit_rate; + const char* holding_units; + const char* holding_value; + const char* pending_buy_value; + const char* pending_sell_value; + const char* profit_amount_accum_td; + const char* profit_amount_accum_td_rate; + const char* recent_profit; + int64_t recent_tradingday; + const char* recent_unit_value; + const char* sold_pending_confirm_units; +}; + +struct CFundPositionDetail +{ + const CFundPositionDetailValues* detail_values; + const CFundDatedValue* sum_profit; + uintptr_t num_sum_profit; + const CFundUnitValue* ut_value; + uintptr_t num_ut_value; +}; + +struct CFundPositionPerformance +{ + const char* annualized_return_five; + const char* annualized_return_one; + const char* annualized_return_ten; + const char* annualized_return_three; + const char* annualized_return_two; + const char* counter_id; + const char* fund_name; + const char* performance_return_five_years; + const char* performance_return_one_day; + const char* performance_return_one_month; + const char* performance_return_one_week; + const char* performance_return_one_year; + const char* performance_return_six_months; + const char* performance_return_ten_years; + const char* performance_return_three_months; + const char* performance_return_three_years; + const char* performance_return_two_years; + const char* performance_return_ytd; + int64_t update_time; +}; + +struct CFundPositionProfits +{ + const char* currency; + const CFundDatedValue* history_value; + uintptr_t num_history_value; + int64_t last_update_time; + const char* sum_profit; +}; + +struct CFundPositionNav +{ + const char* change; + const char* change_percent; + const char* counter_id; + const char* counter_name; + int64_t last_update_time; + const char* value; +}; + +struct CFundDividend +{ + const char* amount; + const char* counter_id; + const char* currency; + int64_t date; + const char* div_method; + const char* name; +}; + +struct CFundDividends +{ + const char* currency; + const CFundDividend* div_cash_infos; + uintptr_t num_div_cash_infos; + int64_t lastest_date; + const char* total_div_cash; +}; + +struct CFundOrder +{ + const char* action; + const char* amount; + const char* counter_id; + int64_t created_at; + const char* currency; + const char* fund_name; + int64_t id; + bool is_auto; + const char* net_worth; + const char* product_type; + const char* state; + const char* state_desc; + const char* units; +}; + +struct CFundOrderKeyword +{ + const char* content; + const char* group; + const char* key; + const char* line_strategy; + const char* title; +}; + +struct CFundOrderStage +{ + const char* desc; + const char* key; + const char* link; + const char* link_text; + const char* progress; + const char* stage; +}; + +struct CFundOrderInfo +{ + int64_t aaid; + const char* account_channel; + const char* action; + const char* amount; + const char* channel; + const char* counter_id; + int64_t created_at; + const char* currency; + const char* dividend_option; + int64_t eq_at; + const char* fee; + const char* fund_name; + const char* fund_source; + const char* histories; + int64_t id; + const char* message; + const char* net_worth; + int64_t price_at; + int64_t processed_at; + const char* product_type; + bool repurchaseable; + const char* sale_proceeds; + const char* sales_charge; + const char* sales_price; + const char* sales_unit; + const char* state; + const char* state_desc; + int32_t status; + int32_t status_ex; + const char* t_description; + const char* time_partition; + const char* total_amount; + int64_t transaction_at; + const char* units; + int64_t withdraw_at; + bool withdrawable; +}; + +struct CFundOrderDetail +{ + const CFundOrderKeyword* keywords; + uintptr_t num_keywords; + const CFundOrderInfo* order; + const CFundOrderStage* stages; + uintptr_t num_stages; +}; + +struct CFundTransaction +{ + const char* amount; + const char* category; + int64_t created_at; + const char* currency; + const char* description; + int64_t detail_created_at; + const char* detail_type; + int64_t done_at; + const char* quantity_description; + const char* redirect_page; + const char* redirect_page_v2; + const char* ref_no; + const char* stock_quantity; + const char* tx_type; + const char* type_name; +}; + +struct CFundOrderValidation +{ + const char* auth_token; + const char* eval_address; + int32_t fund_risk_level; + const char* msg; + int32_t user_pi; + int32_t user_risk_level; +}; + +struct CFundOrderSubmitResponse +{ + const char* action; + const char* amount; + const char* counter_id; + int64_t created_at; + const char* fund_name; + int64_t id; + const char* msg; + int32_t status; + const char* units; +}; + +} // namespace ffi +} // namespace fund +} // namespace longbridge diff --git a/java/javasrc/src/main/java/com/longbridge/SdkNative.java b/java/javasrc/src/main/java/com/longbridge/SdkNative.java index 64eb3d667..a3c6b7456 100644 --- a/java/javasrc/src/main/java/com/longbridge/SdkNative.java +++ b/java/javasrc/src/main/java/com/longbridge/SdkNative.java @@ -300,6 +300,88 @@ public static native void gridContextTriggerHistory(long context, GetGridTrigger public static native void gridContextSymbolInfo(long context, String symbol, AsyncCallback callback); + // ── FundContext ─────────────────────────────────────────────── + public static native long newFundContext(long config); + + public static native void freeFundContext(long context); + + public static native void fundContextHotFunds(long context, AsyncCallback callback); + + public static native void fundContextFunds(long context, com.longbridge.fund.GetFundsOptions opts, + AsyncCallback callback); + + public static native void fundContextFilters(long context, AsyncCallback callback); + + public static native void fundContextDetail(long context, String counterId, AsyncCallback callback); + + public static native void fundContextAnalysis(long context, String counterId, + com.longbridge.fund.GetFundAnalysisOptions opts, AsyncCallback callback); + + public static native void fundContextAnalysisDetail(long context, String counterId, + com.longbridge.fund.GetFundAnalysisOptions opts, AsyncCallback callback); + + public static native void fundContextTrend(long context, String counterId, + com.longbridge.fund.GetFundAnalysisOptions opts, AsyncCallback callback); + + public static native void fundContextAnnualReturns(long context, String counterId, + com.longbridge.fund.FundPageOptions opts, AsyncCallback callback); + + public static native void fundContextQuarterlyReturns(long context, String counterId, + com.longbridge.fund.FundPageOptions opts, AsyncCallback callback); + + public static native void fundContextPerformance(long context, String counterId, AsyncCallback callback); + + public static native void fundContextPerformanceComparison(long context, String counterId, + com.longbridge.fund.GetFundAnalysisOptions opts, AsyncCallback callback); + + public static native void fundContextNav(long context, String counterId, AsyncCallback callback); + + public static native void fundContextNavHistory(long context, String counterId, + com.longbridge.fund.FundPageOptions opts, AsyncCallback callback); + + public static native void fundContextNavRange(long context, String counterId, + com.longbridge.fund.FundNavRangeOptions opts, AsyncCallback callback); + + public static native void fundContextHoldings(long context, String counterId, + com.longbridge.fund.GetFundHoldingsOptions opts, AsyncCallback callback); + + public static native void fundContextStockHoldings(long context, String counterId, + com.longbridge.fund.GetFundStockHoldingsOptions opts, AsyncCallback callback); + + public static native void fundContextPositions(long context, + com.longbridge.fund.GetFundPositionsOptions opts, AsyncCallback callback); + + public static native void fundContextPosition(long context, String counterId, + com.longbridge.fund.GetFundPositionOptions opts, AsyncCallback callback); + + public static native void fundContextPositionPerformance(long context, String counterId, + AsyncCallback callback); + + public static native void fundContextPositionProfits(long context, String counterId, + com.longbridge.fund.GetFundPositionProfitsOptions opts, AsyncCallback callback); + + public static native void fundContextPositionNav(long context, String counterId, + com.longbridge.fund.FundNavRangeOptions opts, AsyncCallback callback); + + public static native void fundContextPositionDividends(long context, String counterId, + com.longbridge.fund.GetFundPositionDividendsOptions opts, AsyncCallback callback); + + public static native void fundContextOrders(long context, + com.longbridge.fund.GetFundOrdersOptions opts, AsyncCallback callback); + + public static native void fundContextOrder(long context, long orderId, AsyncCallback callback); + + public static native void fundContextTransactions(long context, + com.longbridge.fund.GetFundTransactionsOptions opts, AsyncCallback callback); + + public static native void fundContextValidateOrder(long context, + com.longbridge.fund.ValidateFundOrderOptions opts, AsyncCallback callback); + + public static native void fundContextSubmitOrder(long context, + com.longbridge.fund.SubmitFundOrderOptions opts, AsyncCallback callback); + + public static native void fundContextCancelOrder(long context, long orderId, AsyncCallback callback); + // ── DCAContext ──────────────────────────────────────────────── public static native long newDcaContext(long config); public static native void freeDcaContext(long context); diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysis.java b/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysis.java new file mode 100644 index 000000000..3f43738d0 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysis.java @@ -0,0 +1,88 @@ +package com.longbridge.fund; + +/** + * FundAnalysis + */ +public class FundAnalysis { + private int actualPeriod; + private String costLevel; + private String returnAbility; + private String riskAbility; + private String updatedAt; + private String valueForMoney; + private boolean visible; + + /** + * Returns actualPeriod. + * + * @return actualPeriod + */ + public int getActualPeriod() { + return actualPeriod; + } + + /** + * Returns costLevel. + * + * @return costLevel + */ + public String getCostLevel() { + return costLevel; + } + + /** + * Returns returnAbility. + * + * @return returnAbility + */ + public String getReturnAbility() { + return returnAbility; + } + + /** + * Returns riskAbility. + * + * @return riskAbility + */ + public String getRiskAbility() { + return riskAbility; + } + + /** + * Returns updatedAt. + * + * @return updatedAt + */ + public String getUpdatedAt() { + return updatedAt; + } + + /** + * Returns valueForMoney. + * + * @return valueForMoney + */ + public String getValueForMoney() { + return valueForMoney; + } + + /** + * Returns visible. + * + * @return visible + */ + public boolean getVisible() { + return visible; + } + + @Override + public String toString() { + return "FundAnalysis [actualPeriod=" + actualPeriod + + ", costLevel=" + costLevel + + ", returnAbility=" + returnAbility + + ", riskAbility=" + riskAbility + + ", updatedAt=" + updatedAt + + ", valueForMoney=" + valueForMoney + + ", visible=" + visible + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysisDetail.java b/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysisDetail.java new file mode 100644 index 000000000..1bf333e10 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundAnalysisDetail.java @@ -0,0 +1,99 @@ +package com.longbridge.fund; + +/** + * FundAnalysisDetail + */ +public class FundAnalysisDetail { + private int actualPeriod; + private int[] availablePeriods; + private String costLevel; + private String returnAbility; + private String riskAbility; + private String updatedAt; + private String valueForMoney; + private boolean visible; + + /** + * Returns actualPeriod. + * + * @return actualPeriod + */ + public int getActualPeriod() { + return actualPeriod; + } + + /** + * Returns availablePeriods. + * + * @return availablePeriods + */ + public int[] getAvailablePeriods() { + return availablePeriods; + } + + /** + * Returns costLevel. + * + * @return costLevel + */ + public String getCostLevel() { + return costLevel; + } + + /** + * Returns returnAbility. + * + * @return returnAbility + */ + public String getReturnAbility() { + return returnAbility; + } + + /** + * Returns riskAbility. + * + * @return riskAbility + */ + public String getRiskAbility() { + return riskAbility; + } + + /** + * Returns updatedAt. + * + * @return updatedAt + */ + public String getUpdatedAt() { + return updatedAt; + } + + /** + * Returns valueForMoney. + * + * @return valueForMoney + */ + public String getValueForMoney() { + return valueForMoney; + } + + /** + * Returns visible. + * + * @return visible + */ + public boolean getVisible() { + return visible; + } + + @Override + public String toString() { + return "FundAnalysisDetail [actualPeriod=" + actualPeriod + + ", availablePeriods=" + java.util.Arrays.toString(availablePeriods) + + ", costLevel=" + costLevel + + ", returnAbility=" + returnAbility + + ", riskAbility=" + riskAbility + + ", updatedAt=" + updatedAt + + ", valueForMoney=" + valueForMoney + + ", visible=" + visible + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundAnnualReturn.java b/java/javasrc/src/main/java/com/longbridge/fund/FundAnnualReturn.java new file mode 100644 index 000000000..2e53870da --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundAnnualReturn.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundAnnualReturn + */ +public class FundAnnualReturn { + private String changePercent; + private int year; + + /** + * Returns changePercent. + * + * @return changePercent + */ + public String getChangePercent() { + return changePercent; + } + + /** + * Returns year. + * + * @return year + */ + public int getYear() { + return year; + } + + @Override + public String toString() { + return "FundAnnualReturn [changePercent=" + changePercent + + ", year=" + year + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocation.java b/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocation.java new file mode 100644 index 000000000..ac4c03e25 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocation.java @@ -0,0 +1,44 @@ +package com.longbridge.fund; + +/** + * FundAssetAllocation + */ +public class FundAssetAllocation { + private int assetType; + private FundAssetAllocationItem[] lists; + private String reportDate; + + /** + * Returns assetType. + * + * @return assetType + */ + public int getAssetType() { + return assetType; + } + + /** + * Returns lists. + * + * @return lists + */ + public FundAssetAllocationItem[] getLists() { + return lists; + } + + /** + * Returns reportDate. + * + * @return reportDate + */ + public String getReportDate() { + return reportDate; + } + + @Override + public String toString() { + return "FundAssetAllocation [assetType=" + assetType + + ", lists=" + java.util.Arrays.toString(lists) + + ", reportDate=" + reportDate + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocationItem.java b/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocationItem.java new file mode 100644 index 000000000..d999bfad5 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundAssetAllocationItem.java @@ -0,0 +1,55 @@ +package com.longbridge.fund; + +/** + * FundAssetAllocationItem + */ +public class FundAssetAllocationItem { + private String code; + private String counterId; + private String name; + private String positionRatio; + + /** + * Returns code. + * + * @return code + */ + public String getCode() { + return code; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns positionRatio. + * + * @return positionRatio + */ + public String getPositionRatio() { + return positionRatio; + } + + @Override + public String toString() { + return "FundAssetAllocationItem [code=" + code + + ", counterId=" + counterId + + ", name=" + name + + ", positionRatio=" + positionRatio + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundBrief.java b/java/javasrc/src/main/java/com/longbridge/fund/FundBrief.java new file mode 100644 index 000000000..ac6bd14fc --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundBrief.java @@ -0,0 +1,198 @@ +package com.longbridge.fund; + +/** + * FundBrief + */ +public class FundBrief { + private int assetClass; + private String assetClassName; + private String code; + private String counterId; + private String currency; + private String description; + private String earningRate; + private boolean holding; + private String isin; + private String name; + private String product; + private String purchaseAmount; + private String recommendationText; + private int riskLevel; + private String riskLevelName; + private String timeInterval; + private String unitValue; + + /** + * Returns assetClass. + * + * @return assetClass + */ + public int getAssetClass() { + return assetClass; + } + + /** + * Returns assetClassName. + * + * @return assetClassName + */ + public String getAssetClassName() { + return assetClassName; + } + + /** + * Returns code. + * + * @return code + */ + public String getCode() { + return code; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns description. + * + * @return description + */ + public String getDescription() { + return description; + } + + /** + * Returns earningRate. + * + * @return earningRate + */ + public String getEarningRate() { + return earningRate; + } + + /** + * Returns holding. + * + * @return holding + */ + public boolean getHolding() { + return holding; + } + + /** + * Returns isin. + * + * @return isin + */ + public String getIsin() { + return isin; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns product. + * + * @return product + */ + public String getProduct() { + return product; + } + + /** + * Returns purchaseAmount. + * + * @return purchaseAmount + */ + public String getPurchaseAmount() { + return purchaseAmount; + } + + /** + * Returns recommendationText. + * + * @return recommendationText + */ + public String getRecommendationText() { + return recommendationText; + } + + /** + * Returns riskLevel. + * + * @return riskLevel + */ + public int getRiskLevel() { + return riskLevel; + } + + /** + * Returns riskLevelName. + * + * @return riskLevelName + */ + public String getRiskLevelName() { + return riskLevelName; + } + + /** + * Returns timeInterval. + * + * @return timeInterval + */ + public String getTimeInterval() { + return timeInterval; + } + + /** + * Returns unitValue. + * + * @return unitValue + */ + public String getUnitValue() { + return unitValue; + } + + @Override + public String toString() { + return "FundBrief [assetClass=" + assetClass + + ", assetClassName=" + assetClassName + + ", code=" + code + + ", counterId=" + counterId + + ", currency=" + currency + + ", description=" + description + + ", earningRate=" + earningRate + + ", holding=" + holding + + ", isin=" + isin + + ", name=" + name + + ", product=" + product + + ", purchaseAmount=" + purchaseAmount + + ", recommendationText=" + recommendationText + + ", riskLevel=" + riskLevel + + ", riskLevelName=" + riskLevelName + + ", timeInterval=" + timeInterval + + ", unitValue=" + unitValue + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundContext.java b/java/javasrc/src/main/java/com/longbridge/fund/FundContext.java new file mode 100644 index 000000000..14597d95e --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundContext.java @@ -0,0 +1,441 @@ +package com.longbridge.fund; + +import java.util.concurrent.CompletableFuture; +import com.longbridge.*; + +/** + * Fund (mutual fund) channel context. + */ +public class FundContext implements AutoCloseable { + private long raw; + + private long raw() { + long r = this.raw; + if (r == 0) { + throw new IllegalStateException( + getClass().getSimpleName() + " has already been closed"); + } + return r; + } + + /** + * Create a FundContext object. + * + * @param config Config object + * @return A new FundContext instance + */ + public static FundContext create(Config config) { + FundContext ctx = new FundContext(); + ctx.raw = SdkNative.newFundContext(config.getRaw()); + return ctx; + } + + @Override + public synchronized void close() throws Exception { + long h = this.raw; + if (h != 0) { + this.raw = 0; + SdkNative.freeFundContext(h); + } + } + + // ----- fund catalog / market data ----- + + /** + * Get the hot-selling fund list. + * + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture hotFunds() throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextHotFunds(raw(), callback); + }); + } + + /** + * Get the fund list. + * + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture funds(GetFundsOptions opts) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextFunds(raw(), opts, callback); + }); + } + + /** + * Get the fund list filter options. + * + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture filters() throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextFilters(raw(), callback); + }); + } + + /** + * Get fund detail. + * + * @param counterId Fund counter id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture detail(String counterId) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextDetail(raw(), counterId, callback); + }); + } + + /** + * Get fund analysis (level 1). + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture analysis(String counterId, GetFundAnalysisOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextAnalysis(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund analysis detail (level 2). + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture analysisDetail(String counterId, GetFundAnalysisOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextAnalysisDetail(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund trend chart. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture trend(String counterId, GetFundAnalysisOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextTrend(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund annual returns. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture annualReturns(String counterId, FundPageOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextAnnualReturns(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund quarterly returns. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture quarterlyReturns(String counterId, FundPageOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextQuarterlyReturns(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund performance figures. + * + * @param counterId Fund counter id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture performance(String counterId) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPerformance(raw(), counterId, callback); + }); + } + + /** + * Get fund performance comparison. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture performanceComparison(String counterId, + GetFundAnalysisOptions opts) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPerformanceComparison(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund latest net value. + * + * @param counterId Fund counter id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture nav(String counterId) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextNav(raw(), counterId, callback); + }); + } + + /** + * Get fund historical net value (paged). + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture navHistory(String counterId, FundPageOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextNavHistory(raw(), counterId, opts, callback); + }); + } + + /** + * Get fund historical net value by relative time range. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture navRange(String counterId, FundNavRangeOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextNavRange(raw(), counterId, opts, callback); + }); + } + + /** + * Get a fund's top-10 holdings. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture holdings(String counterId, GetFundHoldingsOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextHoldings(raw(), counterId, opts, callback); + }); + } + + /** + * Get the stocks held by a fund (reverse lookup). + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture stockHoldings(String counterId, GetFundStockHoldingsOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextStockHoldings(raw(), counterId, opts, callback); + }); + } + + // ----- user fund positions ----- + + /** + * Get the user's fund positions overview. + * + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture positions(GetFundPositionsOptions opts) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPositions(raw(), opts, callback); + }); + } + + /** + * Get the user's single fund position detail. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture position(String counterId, GetFundPositionOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPosition(raw(), counterId, opts, callback); + }); + } + + /** + * Get the performance figures of a held fund. + * + * @param counterId Fund counter id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture positionPerformance(String counterId) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPositionPerformance(raw(), counterId, callback); + }); + } + + /** + * Get the cumulative-profit series of a held fund. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture positionProfits(String counterId, GetFundPositionProfitsOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPositionProfits(raw(), counterId, opts, callback); + }); + } + + /** + * Get the net-value history of a held fund. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture positionNav(String counterId, FundNavRangeOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPositionNav(raw(), counterId, opts, callback); + }); + } + + /** + * Get the dividend records of a held fund. + * + * @param counterId Fund counter id + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture positionDividends(String counterId, GetFundPositionDividendsOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextPositionDividends(raw(), counterId, opts, callback); + }); + } + + // ----- fund orders & trading ----- + + /** + * Get the user's fund orders. + * + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture orders(GetFundOrdersOptions opts) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextOrders(raw(), opts, callback); + }); + } + + /** + * Get a fund order detail. + * + * @param orderId Fund order id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture order(long orderId) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextOrder(raw(), orderId, callback); + }); + } + + /** + * Get the user's fund transactions (cash-flow records). + * + * @param opts Options for this request; may be null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture transactions(GetFundTransactionsOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextTransactions(raw(), opts, callback); + }); + } + + /** + * Validate a fund order before submitting. + * + * @param opts Options for this request, not null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture validateOrder(ValidateFundOrderOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextValidateOrder(raw(), opts, callback); + }); + } + + /** + * Submit a fund order (buy / sell). + * + * @param opts Options for this request, not null + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture submitOrder(SubmitFundOrderOptions opts) + throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextSubmitOrder(raw(), opts, callback); + }); + } + + /** + * Cancel (withdraw) a fund order. + * + * @param orderId Fund order id + * @return A Future representing the result of the operation + * @throws OpenApiException If an error occurs + */ + public CompletableFuture cancelOrder(long orderId) throws OpenApiException { + return AsyncCallback.executeTask((callback) -> { + SdkNative.fundContextCancelOrder(raw(), orderId, callback); + }); + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundDatedValue.java b/java/javasrc/src/main/java/com/longbridge/fund/FundDatedValue.java new file mode 100644 index 000000000..4c7dfef25 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundDatedValue.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundDatedValue + */ +public class FundDatedValue { + private long date; + private String value; + + /** + * Returns date. + * + * @return date + */ + public long getDate() { + return date; + } + + /** + * Returns value. + * + * @return value + */ + public String getValue() { + return value; + } + + @Override + public String toString() { + return "FundDatedValue [date=" + date + + ", value=" + value + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundDetail.java b/java/javasrc/src/main/java/com/longbridge/fund/FundDetail.java new file mode 100644 index 000000000..f0c461d1c --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundDetail.java @@ -0,0 +1,594 @@ +package com.longbridge.fund; + +/** + * FundDetail + */ +public class FundDetail { + private String additionalPurchaseAmount; + private int affirmDay; + private String amountAffirmDay; + private FundAssetAllocation assetAllocation; + private int assetClass; + private String assetClassName; + private String billPurchaseRate; + private String channel; + private String closePeriod; + private String code; + private String currency; + private String cutOffTime; + private boolean derivatives; + private int doneDay; + private String excessReturnFee; + private String gstRate; + private String introduce; + private boolean isCashPlus; + private boolean isComplex; + private boolean isNewCashPlus; + private boolean isYinghebao; + private String isin; + private String manageRate; + private String manager; + private String minHoldCash; + private String minHoldShare; + private String minSellShare; + private String monthRaiseDay; + private String name; + private String navDeadline; + private boolean noLoad; + private String openDate; + private String openPeriod; + private String product; + private String productInformationLocals; + private String profile; + private int purchasable; + private String purchaseAffirmDay; + private String purchaseAmount; + private String purchaseRate; + private int rating; + private int redeemable; + private String redemptionAdvanceDay; + private String redemptionAmount; + private String redemptionClosePeriodShows; + private String redemptionDoneDay; + private String redemptionOpenDayShows; + private int riskLevel; + private String riskLevelName; + private int verifyStatus; + private boolean virtualCurrency; + private String yearToDateYield; + private int ytdYieldType; + + /** + * Returns additionalPurchaseAmount. + * + * @return additionalPurchaseAmount + */ + public String getAdditionalPurchaseAmount() { + return additionalPurchaseAmount; + } + + /** + * Returns affirmDay. + * + * @return affirmDay + */ + public int getAffirmDay() { + return affirmDay; + } + + /** + * Returns amountAffirmDay. + * + * @return amountAffirmDay + */ + public String getAmountAffirmDay() { + return amountAffirmDay; + } + + /** + * Returns assetAllocation. + * + * @return assetAllocation + */ + public FundAssetAllocation getAssetAllocation() { + return assetAllocation; + } + + /** + * Returns assetClass. + * + * @return assetClass + */ + public int getAssetClass() { + return assetClass; + } + + /** + * Returns assetClassName. + * + * @return assetClassName + */ + public String getAssetClassName() { + return assetClassName; + } + + /** + * Returns billPurchaseRate. + * + * @return billPurchaseRate + */ + public String getBillPurchaseRate() { + return billPurchaseRate; + } + + /** + * Returns channel. + * + * @return channel + */ + public String getChannel() { + return channel; + } + + /** + * Returns closePeriod. + * + * @return closePeriod + */ + public String getClosePeriod() { + return closePeriod; + } + + /** + * Returns code. + * + * @return code + */ + public String getCode() { + return code; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns cutOffTime. + * + * @return cutOffTime + */ + public String getCutOffTime() { + return cutOffTime; + } + + /** + * Returns derivatives. + * + * @return derivatives + */ + public boolean getDerivatives() { + return derivatives; + } + + /** + * Returns doneDay. + * + * @return doneDay + */ + public int getDoneDay() { + return doneDay; + } + + /** + * Returns excessReturnFee. + * + * @return excessReturnFee + */ + public String getExcessReturnFee() { + return excessReturnFee; + } + + /** + * Returns gstRate. + * + * @return gstRate + */ + public String getGstRate() { + return gstRate; + } + + /** + * Returns introduce. + * + * @return introduce + */ + public String getIntroduce() { + return introduce; + } + + /** + * Returns isCashPlus. + * + * @return isCashPlus + */ + public boolean getIsCashPlus() { + return isCashPlus; + } + + /** + * Returns isComplex. + * + * @return isComplex + */ + public boolean getIsComplex() { + return isComplex; + } + + /** + * Returns isNewCashPlus. + * + * @return isNewCashPlus + */ + public boolean getIsNewCashPlus() { + return isNewCashPlus; + } + + /** + * Returns isYinghebao. + * + * @return isYinghebao + */ + public boolean getIsYinghebao() { + return isYinghebao; + } + + /** + * Returns isin. + * + * @return isin + */ + public String getIsin() { + return isin; + } + + /** + * Returns manageRate. + * + * @return manageRate + */ + public String getManageRate() { + return manageRate; + } + + /** + * Returns manager. + * + * @return manager + */ + public String getManager() { + return manager; + } + + /** + * Returns minHoldCash. + * + * @return minHoldCash + */ + public String getMinHoldCash() { + return minHoldCash; + } + + /** + * Returns minHoldShare. + * + * @return minHoldShare + */ + public String getMinHoldShare() { + return minHoldShare; + } + + /** + * Returns minSellShare. + * + * @return minSellShare + */ + public String getMinSellShare() { + return minSellShare; + } + + /** + * Returns monthRaiseDay. + * + * @return monthRaiseDay + */ + public String getMonthRaiseDay() { + return monthRaiseDay; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns navDeadline. + * + * @return navDeadline + */ + public String getNavDeadline() { + return navDeadline; + } + + /** + * Returns noLoad. + * + * @return noLoad + */ + public boolean getNoLoad() { + return noLoad; + } + + /** + * Returns openDate. + * + * @return openDate + */ + public String getOpenDate() { + return openDate; + } + + /** + * Returns openPeriod. + * + * @return openPeriod + */ + public String getOpenPeriod() { + return openPeriod; + } + + /** + * Returns product. + * + * @return product + */ + public String getProduct() { + return product; + } + + /** + * Returns productInformationLocals. + * + * @return productInformationLocals + */ + public String getProductInformationLocals() { + return productInformationLocals; + } + + /** + * Returns profile. + * + * @return profile + */ + public String getProfile() { + return profile; + } + + /** + * Returns purchasable. + * + * @return purchasable + */ + public int getPurchasable() { + return purchasable; + } + + /** + * Returns purchaseAffirmDay. + * + * @return purchaseAffirmDay + */ + public String getPurchaseAffirmDay() { + return purchaseAffirmDay; + } + + /** + * Returns purchaseAmount. + * + * @return purchaseAmount + */ + public String getPurchaseAmount() { + return purchaseAmount; + } + + /** + * Returns purchaseRate. + * + * @return purchaseRate + */ + public String getPurchaseRate() { + return purchaseRate; + } + + /** + * Returns rating. + * + * @return rating + */ + public int getRating() { + return rating; + } + + /** + * Returns redeemable. + * + * @return redeemable + */ + public int getRedeemable() { + return redeemable; + } + + /** + * Returns redemptionAdvanceDay. + * + * @return redemptionAdvanceDay + */ + public String getRedemptionAdvanceDay() { + return redemptionAdvanceDay; + } + + /** + * Returns redemptionAmount. + * + * @return redemptionAmount + */ + public String getRedemptionAmount() { + return redemptionAmount; + } + + /** + * Returns redemptionClosePeriodShows. + * + * @return redemptionClosePeriodShows + */ + public String getRedemptionClosePeriodShows() { + return redemptionClosePeriodShows; + } + + /** + * Returns redemptionDoneDay. + * + * @return redemptionDoneDay + */ + public String getRedemptionDoneDay() { + return redemptionDoneDay; + } + + /** + * Returns redemptionOpenDayShows. + * + * @return redemptionOpenDayShows + */ + public String getRedemptionOpenDayShows() { + return redemptionOpenDayShows; + } + + /** + * Returns riskLevel. + * + * @return riskLevel + */ + public int getRiskLevel() { + return riskLevel; + } + + /** + * Returns riskLevelName. + * + * @return riskLevelName + */ + public String getRiskLevelName() { + return riskLevelName; + } + + /** + * Returns verifyStatus. + * + * @return verifyStatus + */ + public int getVerifyStatus() { + return verifyStatus; + } + + /** + * Returns virtualCurrency. + * + * @return virtualCurrency + */ + public boolean getVirtualCurrency() { + return virtualCurrency; + } + + /** + * Returns yearToDateYield. + * + * @return yearToDateYield + */ + public String getYearToDateYield() { + return yearToDateYield; + } + + /** + * Returns ytdYieldType. + * + * @return ytdYieldType + */ + public int getYtdYieldType() { + return ytdYieldType; + } + + @Override + public String toString() { + return "FundDetail [additionalPurchaseAmount=" + additionalPurchaseAmount + + ", affirmDay=" + affirmDay + + ", amountAffirmDay=" + amountAffirmDay + + ", assetAllocation=" + assetAllocation + + ", assetClass=" + assetClass + + ", assetClassName=" + assetClassName + + ", billPurchaseRate=" + billPurchaseRate + + ", channel=" + channel + + ", closePeriod=" + closePeriod + + ", code=" + code + + ", currency=" + currency + + ", cutOffTime=" + cutOffTime + + ", derivatives=" + derivatives + + ", doneDay=" + doneDay + + ", excessReturnFee=" + excessReturnFee + + ", gstRate=" + gstRate + + ", introduce=" + introduce + + ", isCashPlus=" + isCashPlus + + ", isComplex=" + isComplex + + ", isNewCashPlus=" + isNewCashPlus + + ", isYinghebao=" + isYinghebao + + ", isin=" + isin + + ", manageRate=" + manageRate + + ", manager=" + manager + + ", minHoldCash=" + minHoldCash + + ", minHoldShare=" + minHoldShare + + ", minSellShare=" + minSellShare + + ", monthRaiseDay=" + monthRaiseDay + + ", name=" + name + + ", navDeadline=" + navDeadline + + ", noLoad=" + noLoad + + ", openDate=" + openDate + + ", openPeriod=" + openPeriod + + ", product=" + product + + ", productInformationLocals=" + productInformationLocals + + ", profile=" + profile + + ", purchasable=" + purchasable + + ", purchaseAffirmDay=" + purchaseAffirmDay + + ", purchaseAmount=" + purchaseAmount + + ", purchaseRate=" + purchaseRate + + ", rating=" + rating + + ", redeemable=" + redeemable + + ", redemptionAdvanceDay=" + redemptionAdvanceDay + + ", redemptionAmount=" + redemptionAmount + + ", redemptionClosePeriodShows=" + redemptionClosePeriodShows + + ", redemptionDoneDay=" + redemptionDoneDay + + ", redemptionOpenDayShows=" + redemptionOpenDayShows + + ", riskLevel=" + riskLevel + + ", riskLevelName=" + riskLevelName + + ", verifyStatus=" + verifyStatus + + ", virtualCurrency=" + virtualCurrency + + ", yearToDateYield=" + yearToDateYield + + ", ytdYieldType=" + ytdYieldType + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundDividend.java b/java/javasrc/src/main/java/com/longbridge/fund/FundDividend.java new file mode 100644 index 000000000..e79695dc4 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundDividend.java @@ -0,0 +1,77 @@ +package com.longbridge.fund; + +/** + * FundDividend + */ +public class FundDividend { + private String amount; + private String counterId; + private String currency; + private long date; + private String divMethod; + private String name; + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns date. + * + * @return date + */ + public long getDate() { + return date; + } + + /** + * Returns divMethod. + * + * @return divMethod + */ + public String getDivMethod() { + return divMethod; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + @Override + public String toString() { + return "FundDividend [amount=" + amount + + ", counterId=" + counterId + + ", currency=" + currency + + ", date=" + date + + ", divMethod=" + divMethod + + ", name=" + name + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundDividends.java b/java/javasrc/src/main/java/com/longbridge/fund/FundDividends.java new file mode 100644 index 000000000..e35668773 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundDividends.java @@ -0,0 +1,55 @@ +package com.longbridge.fund; + +/** + * FundDividends + */ +public class FundDividends { + private String currency; + private FundDividend[] divCashInfos; + private long lastestDate; + private String totalDivCash; + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns divCashInfos. + * + * @return divCashInfos + */ + public FundDividend[] getDivCashInfos() { + return divCashInfos; + } + + /** + * Returns lastestDate. + * + * @return lastestDate + */ + public long getLastestDate() { + return lastestDate; + } + + /** + * Returns totalDivCash. + * + * @return totalDivCash + */ + public String getTotalDivCash() { + return totalDivCash; + } + + @Override + public String toString() { + return "FundDividends [currency=" + currency + + ", divCashInfos=" + java.util.Arrays.toString(divCashInfos) + + ", lastestDate=" + lastestDate + + ", totalDivCash=" + totalDivCash + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundFilters.java b/java/javasrc/src/main/java/com/longbridge/fund/FundFilters.java new file mode 100644 index 000000000..444b895cf --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundFilters.java @@ -0,0 +1,66 @@ +package com.longbridge.fund; + +/** + * FundFilters + */ +public class FundFilters { + private String[] assetClass; + private String[] company; + private String[] currency; + private String[] industryCategoryName; + private String[] riskLevel; + + /** + * Returns assetClass. + * + * @return assetClass + */ + public String[] getAssetClass() { + return assetClass; + } + + /** + * Returns company. + * + * @return company + */ + public String[] getCompany() { + return company; + } + + /** + * Returns currency. + * + * @return currency + */ + public String[] getCurrency() { + return currency; + } + + /** + * Returns industryCategoryName. + * + * @return industryCategoryName + */ + public String[] getIndustryCategoryName() { + return industryCategoryName; + } + + /** + * Returns riskLevel. + * + * @return riskLevel + */ + public String[] getRiskLevel() { + return riskLevel; + } + + @Override + public String toString() { + return "FundFilters [assetClass=" + java.util.Arrays.toString(assetClass) + + ", company=" + java.util.Arrays.toString(company) + + ", currency=" + java.util.Arrays.toString(currency) + + ", industryCategoryName=" + java.util.Arrays.toString(industryCategoryName) + + ", riskLevel=" + java.util.Arrays.toString(riskLevel) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundHolding.java b/java/javasrc/src/main/java/com/longbridge/fund/FundHolding.java new file mode 100644 index 000000000..c5e625aa9 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundHolding.java @@ -0,0 +1,143 @@ +package com.longbridge.fund; + +/** + * FundHolding + */ +public class FundHolding { + private String bondType; + private String bondTypeName; + private String countryName; + private String holdingType; + private String industryName; + private String marketValue; + private String maturityDate; + private String name; + private String shareChange; + private String shareChangePercent; + private String shares; + private String weighting; + + /** + * Returns bondType. + * + * @return bondType + */ + public String getBondType() { + return bondType; + } + + /** + * Returns bondTypeName. + * + * @return bondTypeName + */ + public String getBondTypeName() { + return bondTypeName; + } + + /** + * Returns countryName. + * + * @return countryName + */ + public String getCountryName() { + return countryName; + } + + /** + * Returns holdingType. + * + * @return holdingType + */ + public String getHoldingType() { + return holdingType; + } + + /** + * Returns industryName. + * + * @return industryName + */ + public String getIndustryName() { + return industryName; + } + + /** + * Returns marketValue. + * + * @return marketValue + */ + public String getMarketValue() { + return marketValue; + } + + /** + * Returns maturityDate. + * + * @return maturityDate + */ + public String getMaturityDate() { + return maturityDate; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns shareChange. + * + * @return shareChange + */ + public String getShareChange() { + return shareChange; + } + + /** + * Returns shareChangePercent. + * + * @return shareChangePercent + */ + public String getShareChangePercent() { + return shareChangePercent; + } + + /** + * Returns shares. + * + * @return shares + */ + public String getShares() { + return shares; + } + + /** + * Returns weighting. + * + * @return weighting + */ + public String getWeighting() { + return weighting; + } + + @Override + public String toString() { + return "FundHolding [bondType=" + bondType + + ", bondTypeName=" + bondTypeName + + ", countryName=" + countryName + + ", holdingType=" + holdingType + + ", industryName=" + industryName + + ", marketValue=" + marketValue + + ", maturityDate=" + maturityDate + + ", name=" + name + + ", shareChange=" + shareChange + + ", shareChangePercent=" + shareChangePercent + + ", shares=" + shares + + ", weighting=" + weighting + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundHoldings.java b/java/javasrc/src/main/java/com/longbridge/fund/FundHoldings.java new file mode 100644 index 000000000..dbc37a2b0 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundHoldings.java @@ -0,0 +1,44 @@ +package com.longbridge.fund; + +/** + * FundHoldings + */ +public class FundHoldings { + private FundHolding[] holdings; + private String reportDate; + private String weighting; + + /** + * Returns holdings. + * + * @return holdings + */ + public FundHolding[] getHoldings() { + return holdings; + } + + /** + * Returns reportDate. + * + * @return reportDate + */ + public String getReportDate() { + return reportDate; + } + + /** + * Returns weighting. + * + * @return weighting + */ + public String getWeighting() { + return weighting; + } + + @Override + public String toString() { + return "FundHoldings [holdings=" + java.util.Arrays.toString(holdings) + + ", reportDate=" + reportDate + + ", weighting=" + weighting + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundNamedContrast.java b/java/javasrc/src/main/java/com/longbridge/fund/FundNamedContrast.java new file mode 100644 index 000000000..d05fb937e --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundNamedContrast.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundNamedContrast + */ +public class FundNamedContrast { + private String name; + private String[] performances; + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns performances. + * + * @return performances + */ + public String[] getPerformances() { + return performances; + } + + @Override + public String toString() { + return "FundNamedContrast [name=" + name + + ", performances=" + java.util.Arrays.toString(performances) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundNavRangeOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/FundNavRangeOptions.java new file mode 100644 index 000000000..15eb02a11 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundNavRangeOptions.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * Options for FundNavRangeOptions. + */ +@SuppressWarnings("unused") +public class FundNavRangeOptions { + private Integer monthBefore; + private Integer yearBefore; + + /** + * Sets monthBefore. + * + * @param monthBefore monthBefore + * @return this instance for chaining + */ + public FundNavRangeOptions setMonthBefore(Integer monthBefore) { + this.monthBefore = monthBefore; + return this; + } + + /** + * Sets yearBefore. + * + * @param yearBefore yearBefore + * @return this instance for chaining + */ + public FundNavRangeOptions setYearBefore(Integer yearBefore) { + this.yearBefore = yearBefore; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundNavValue.java b/java/javasrc/src/main/java/com/longbridge/fund/FundNavValue.java new file mode 100644 index 000000000..c5a1cf4eb --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundNavValue.java @@ -0,0 +1,132 @@ +package com.longbridge.fund; + +/** + * FundNavValue + */ +public class FundNavValue { + private String change; + private String changePercent; + private String changePercentFormat; + private String counterId; + private String counterName; + private String currency; + private String dateFormat; + private String isin; + private long lastUpdateTime; + private String value; + private String valueFormat; + + /** + * Returns change. + * + * @return change + */ + public String getChange() { + return change; + } + + /** + * Returns changePercent. + * + * @return changePercent + */ + public String getChangePercent() { + return changePercent; + } + + /** + * Returns changePercentFormat. + * + * @return changePercentFormat + */ + public String getChangePercentFormat() { + return changePercentFormat; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns counterName. + * + * @return counterName + */ + public String getCounterName() { + return counterName; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns dateFormat. + * + * @return dateFormat + */ + public String getDateFormat() { + return dateFormat; + } + + /** + * Returns isin. + * + * @return isin + */ + public String getIsin() { + return isin; + } + + /** + * Returns lastUpdateTime. + * + * @return lastUpdateTime + */ + public long getLastUpdateTime() { + return lastUpdateTime; + } + + /** + * Returns value. + * + * @return value + */ + public String getValue() { + return value; + } + + /** + * Returns valueFormat. + * + * @return valueFormat + */ + public String getValueFormat() { + return valueFormat; + } + + @Override + public String toString() { + return "FundNavValue [change=" + change + + ", changePercent=" + changePercent + + ", changePercentFormat=" + changePercentFormat + + ", counterId=" + counterId + + ", counterName=" + counterName + + ", currency=" + currency + + ", dateFormat=" + dateFormat + + ", isin=" + isin + + ", lastUpdateTime=" + lastUpdateTime + + ", value=" + value + + ", valueFormat=" + valueFormat + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrder.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrder.java new file mode 100644 index 000000000..8f9e87509 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrder.java @@ -0,0 +1,154 @@ +package com.longbridge.fund; + +/** + * FundOrder + */ +public class FundOrder { + private String action; + private String amount; + private String counterId; + private long createdAt; + private String currency; + private String fundName; + private long id; + private boolean isAuto; + private String netWorth; + private String productType; + private String state; + private String stateDesc; + private String units; + + /** + * Returns action. + * + * @return action + */ + public String getAction() { + return action; + } + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns createdAt. + * + * @return createdAt + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns fundName. + * + * @return fundName + */ + public String getFundName() { + return fundName; + } + + /** + * Returns id. + * + * @return id + */ + public long getId() { + return id; + } + + /** + * Returns isAuto. + * + * @return isAuto + */ + public boolean getIsAuto() { + return isAuto; + } + + /** + * Returns netWorth. + * + * @return netWorth + */ + public String getNetWorth() { + return netWorth; + } + + /** + * Returns productType. + * + * @return productType + */ + public String getProductType() { + return productType; + } + + /** + * Returns state. + * + * @return state + */ + public String getState() { + return state; + } + + /** + * Returns stateDesc. + * + * @return stateDesc + */ + public String getStateDesc() { + return stateDesc; + } + + /** + * Returns units. + * + * @return units + */ + public String getUnits() { + return units; + } + + @Override + public String toString() { + return "FundOrder [action=" + action + + ", amount=" + amount + + ", counterId=" + counterId + + ", createdAt=" + createdAt + + ", currency=" + currency + + ", fundName=" + fundName + + ", id=" + id + + ", isAuto=" + isAuto + + ", netWorth=" + netWorth + + ", productType=" + productType + + ", state=" + state + + ", stateDesc=" + stateDesc + + ", units=" + units + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderDetail.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderDetail.java new file mode 100644 index 000000000..8ee26b409 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderDetail.java @@ -0,0 +1,44 @@ +package com.longbridge.fund; + +/** + * FundOrderDetail + */ +public class FundOrderDetail { + private FundOrderKeyword[] keywords; + private FundOrderInfo order; + private FundOrderStage[] stages; + + /** + * Returns keywords. + * + * @return keywords + */ + public FundOrderKeyword[] getKeywords() { + return keywords; + } + + /** + * Returns order. + * + * @return order + */ + public FundOrderInfo getOrder() { + return order; + } + + /** + * Returns stages. + * + * @return stages + */ + public FundOrderStage[] getStages() { + return stages; + } + + @Override + public String toString() { + return "FundOrderDetail [keywords=" + java.util.Arrays.toString(keywords) + + ", order=" + order + + ", stages=" + java.util.Arrays.toString(stages) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderInfo.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderInfo.java new file mode 100644 index 000000000..8602268fa --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderInfo.java @@ -0,0 +1,407 @@ +package com.longbridge.fund; + +/** + * FundOrderInfo + */ +public class FundOrderInfo { + private long aaid; + private String accountChannel; + private String action; + private String amount; + private String channel; + private String counterId; + private long createdAt; + private String currency; + private String dividendOption; + private long eqAt; + private String fee; + private String fundName; + private String fundSource; + private String histories; + private long id; + private String message; + private String netWorth; + private long priceAt; + private long processedAt; + private String productType; + private boolean repurchaseable; + private String saleProceeds; + private String salesCharge; + private String salesPrice; + private String salesUnit; + private String state; + private String stateDesc; + private int status; + private int statusEx; + private String tDescription; + private String timePartition; + private String totalAmount; + private long transactionAt; + private String units; + private long withdrawAt; + private boolean withdrawable; + + /** + * Returns aaid. + * + * @return aaid + */ + public long getAaid() { + return aaid; + } + + /** + * Returns accountChannel. + * + * @return accountChannel + */ + public String getAccountChannel() { + return accountChannel; + } + + /** + * Returns action. + * + * @return action + */ + public String getAction() { + return action; + } + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns channel. + * + * @return channel + */ + public String getChannel() { + return channel; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns createdAt. + * + * @return createdAt + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns dividendOption. + * + * @return dividendOption + */ + public String getDividendOption() { + return dividendOption; + } + + /** + * Returns eqAt. + * + * @return eqAt + */ + public long getEqAt() { + return eqAt; + } + + /** + * Returns fee. + * + * @return fee + */ + public String getFee() { + return fee; + } + + /** + * Returns fundName. + * + * @return fundName + */ + public String getFundName() { + return fundName; + } + + /** + * Returns fundSource. + * + * @return fundSource + */ + public String getFundSource() { + return fundSource; + } + + /** + * Returns histories. + * + * @return histories + */ + public String getHistories() { + return histories; + } + + /** + * Returns id. + * + * @return id + */ + public long getId() { + return id; + } + + /** + * Returns message. + * + * @return message + */ + public String getMessage() { + return message; + } + + /** + * Returns netWorth. + * + * @return netWorth + */ + public String getNetWorth() { + return netWorth; + } + + /** + * Returns priceAt. + * + * @return priceAt + */ + public long getPriceAt() { + return priceAt; + } + + /** + * Returns processedAt. + * + * @return processedAt + */ + public long getProcessedAt() { + return processedAt; + } + + /** + * Returns productType. + * + * @return productType + */ + public String getProductType() { + return productType; + } + + /** + * Returns repurchaseable. + * + * @return repurchaseable + */ + public boolean getRepurchaseable() { + return repurchaseable; + } + + /** + * Returns saleProceeds. + * + * @return saleProceeds + */ + public String getSaleProceeds() { + return saleProceeds; + } + + /** + * Returns salesCharge. + * + * @return salesCharge + */ + public String getSalesCharge() { + return salesCharge; + } + + /** + * Returns salesPrice. + * + * @return salesPrice + */ + public String getSalesPrice() { + return salesPrice; + } + + /** + * Returns salesUnit. + * + * @return salesUnit + */ + public String getSalesUnit() { + return salesUnit; + } + + /** + * Returns state. + * + * @return state + */ + public String getState() { + return state; + } + + /** + * Returns stateDesc. + * + * @return stateDesc + */ + public String getStateDesc() { + return stateDesc; + } + + /** + * Returns status. + * + * @return status + */ + public int getStatus() { + return status; + } + + /** + * Returns statusEx. + * + * @return statusEx + */ + public int getStatusEx() { + return statusEx; + } + + /** + * Returns tDescription. + * + * @return tDescription + */ + public String getTDescription() { + return tDescription; + } + + /** + * Returns timePartition. + * + * @return timePartition + */ + public String getTimePartition() { + return timePartition; + } + + /** + * Returns totalAmount. + * + * @return totalAmount + */ + public String getTotalAmount() { + return totalAmount; + } + + /** + * Returns transactionAt. + * + * @return transactionAt + */ + public long getTransactionAt() { + return transactionAt; + } + + /** + * Returns units. + * + * @return units + */ + public String getUnits() { + return units; + } + + /** + * Returns withdrawAt. + * + * @return withdrawAt + */ + public long getWithdrawAt() { + return withdrawAt; + } + + /** + * Returns withdrawable. + * + * @return withdrawable + */ + public boolean getWithdrawable() { + return withdrawable; + } + + @Override + public String toString() { + return "FundOrderInfo [aaid=" + aaid + + ", accountChannel=" + accountChannel + + ", action=" + action + + ", amount=" + amount + + ", channel=" + channel + + ", counterId=" + counterId + + ", createdAt=" + createdAt + + ", currency=" + currency + + ", dividendOption=" + dividendOption + + ", eqAt=" + eqAt + + ", fee=" + fee + + ", fundName=" + fundName + + ", fundSource=" + fundSource + + ", histories=" + histories + + ", id=" + id + + ", message=" + message + + ", netWorth=" + netWorth + + ", priceAt=" + priceAt + + ", processedAt=" + processedAt + + ", productType=" + productType + + ", repurchaseable=" + repurchaseable + + ", saleProceeds=" + saleProceeds + + ", salesCharge=" + salesCharge + + ", salesPrice=" + salesPrice + + ", salesUnit=" + salesUnit + + ", state=" + state + + ", stateDesc=" + stateDesc + + ", status=" + status + + ", statusEx=" + statusEx + + ", tDescription=" + tDescription + + ", timePartition=" + timePartition + + ", totalAmount=" + totalAmount + + ", transactionAt=" + transactionAt + + ", units=" + units + + ", withdrawAt=" + withdrawAt + + ", withdrawable=" + withdrawable + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderKeyword.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderKeyword.java new file mode 100644 index 000000000..dc0f39d34 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderKeyword.java @@ -0,0 +1,66 @@ +package com.longbridge.fund; + +/** + * FundOrderKeyword + */ +public class FundOrderKeyword { + private String content; + private String group; + private String key; + private String lineStrategy; + private String title; + + /** + * Returns content. + * + * @return content + */ + public String getContent() { + return content; + } + + /** + * Returns group. + * + * @return group + */ + public String getGroup() { + return group; + } + + /** + * Returns key. + * + * @return key + */ + public String getKey() { + return key; + } + + /** + * Returns lineStrategy. + * + * @return lineStrategy + */ + public String getLineStrategy() { + return lineStrategy; + } + + /** + * Returns title. + * + * @return title + */ + public String getTitle() { + return title; + } + + @Override + public String toString() { + return "FundOrderKeyword [content=" + content + + ", group=" + group + + ", key=" + key + + ", lineStrategy=" + lineStrategy + + ", title=" + title + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderStage.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderStage.java new file mode 100644 index 000000000..201b5a580 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderStage.java @@ -0,0 +1,77 @@ +package com.longbridge.fund; + +/** + * FundOrderStage + */ +public class FundOrderStage { + private String desc; + private String key; + private String link; + private String linkText; + private String progress; + private String stage; + + /** + * Returns desc. + * + * @return desc + */ + public String getDesc() { + return desc; + } + + /** + * Returns key. + * + * @return key + */ + public String getKey() { + return key; + } + + /** + * Returns link. + * + * @return link + */ + public String getLink() { + return link; + } + + /** + * Returns linkText. + * + * @return linkText + */ + public String getLinkText() { + return linkText; + } + + /** + * Returns progress. + * + * @return progress + */ + public String getProgress() { + return progress; + } + + /** + * Returns stage. + * + * @return stage + */ + public String getStage() { + return stage; + } + + @Override + public String toString() { + return "FundOrderStage [desc=" + desc + + ", key=" + key + + ", link=" + link + + ", linkText=" + linkText + + ", progress=" + progress + + ", stage=" + stage + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderSubmitResponse.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderSubmitResponse.java new file mode 100644 index 000000000..b11e56df2 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderSubmitResponse.java @@ -0,0 +1,110 @@ +package com.longbridge.fund; + +/** + * FundOrderSubmitResponse + */ +public class FundOrderSubmitResponse { + private String action; + private String amount; + private String counterId; + private long createdAt; + private String fundName; + private long id; + private String msg; + private int status; + private String units; + + /** + * Returns action. + * + * @return action + */ + public String getAction() { + return action; + } + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns createdAt. + * + * @return createdAt + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Returns fundName. + * + * @return fundName + */ + public String getFundName() { + return fundName; + } + + /** + * Returns id. + * + * @return id + */ + public long getId() { + return id; + } + + /** + * Returns msg. + * + * @return msg + */ + public String getMsg() { + return msg; + } + + /** + * Returns status. + * + * @return status + */ + public int getStatus() { + return status; + } + + /** + * Returns units. + * + * @return units + */ + public String getUnits() { + return units; + } + + @Override + public String toString() { + return "FundOrderSubmitResponse [action=" + action + + ", amount=" + amount + + ", counterId=" + counterId + + ", createdAt=" + createdAt + + ", fundName=" + fundName + + ", id=" + id + + ", msg=" + msg + + ", status=" + status + + ", units=" + units + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundOrderValidation.java b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderValidation.java new file mode 100644 index 000000000..c8205f36d --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundOrderValidation.java @@ -0,0 +1,77 @@ +package com.longbridge.fund; + +/** + * FundOrderValidation + */ +public class FundOrderValidation { + private String authToken; + private String evalAddress; + private int fundRiskLevel; + private String msg; + private int userPi; + private int userRiskLevel; + + /** + * Returns authToken. + * + * @return authToken + */ + public String getAuthToken() { + return authToken; + } + + /** + * Returns evalAddress. + * + * @return evalAddress + */ + public String getEvalAddress() { + return evalAddress; + } + + /** + * Returns fundRiskLevel. + * + * @return fundRiskLevel + */ + public int getFundRiskLevel() { + return fundRiskLevel; + } + + /** + * Returns msg. + * + * @return msg + */ + public String getMsg() { + return msg; + } + + /** + * Returns userPi. + * + * @return userPi + */ + public int getUserPi() { + return userPi; + } + + /** + * Returns userRiskLevel. + * + * @return userRiskLevel + */ + public int getUserRiskLevel() { + return userRiskLevel; + } + + @Override + public String toString() { + return "FundOrderValidation [authToken=" + authToken + + ", evalAddress=" + evalAddress + + ", fundRiskLevel=" + fundRiskLevel + + ", msg=" + msg + + ", userPi=" + userPi + + ", userRiskLevel=" + userRiskLevel + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPageOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPageOptions.java new file mode 100644 index 000000000..54f280223 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPageOptions.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * Options for FundPageOptions. + */ +@SuppressWarnings("unused") +public class FundPageOptions { + private Integer page; + private Integer size; + + /** + * Sets page. + * + * @param page page + * @return this instance for chaining + */ + public FundPageOptions setPage(Integer page) { + this.page = page; + return this; + } + + /** + * Sets size. + * + * @param size size + * @return this instance for chaining + */ + public FundPageOptions setSize(Integer size) { + this.size = size; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPerformance.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformance.java new file mode 100644 index 000000000..fd0850d0f --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformance.java @@ -0,0 +1,484 @@ +package com.longbridge.fund; + +/** + * FundPerformance + */ +public class FundPerformance { + private String annualizedReturnFive; + private String annualizedReturnOne; + private String annualizedReturnTen; + private String annualizedReturnThree; + private String annualizedReturnTwo; + private String counterId; + private String fundName; + private int performanceRankFiveYears; + private int performanceRankOneDay; + private int performanceRankOneMonth; + private int performanceRankOneWeek; + private int performanceRankOneYear; + private int performanceRankSixMonths; + private int performanceRankTenYears; + private int performanceRankThreeMonths; + private int performanceRankThreeYears; + private int performanceRankTwoYears; + private int performanceRankYtd; + private String performanceReturnFiveYears; + private String performanceReturnOneDay; + private String performanceReturnOneMonth; + private String performanceReturnOneWeek; + private String performanceReturnOneYear; + private String performanceReturnSixMonths; + private String performanceReturnTenYears; + private String performanceReturnThreeMonths; + private String performanceReturnThreeYears; + private String performanceReturnTwoYears; + private String performanceReturnYtd; + private int performanceTotalFiveYears; + private int performanceTotalOneDay; + private int performanceTotalOneMonth; + private int performanceTotalOneWeek; + private int performanceTotalOneYear; + private int performanceTotalSixMonths; + private int performanceTotalTenYears; + private int performanceTotalThreeMonths; + private int performanceTotalThreeYears; + private int performanceTotalTwoYears; + private int performanceTotalYtd; + private String sevenDaysAnnualized; + private String tenThousandPrice; + private long updateTime; + + /** + * Returns annualizedReturnFive. + * + * @return annualizedReturnFive + */ + public String getAnnualizedReturnFive() { + return annualizedReturnFive; + } + + /** + * Returns annualizedReturnOne. + * + * @return annualizedReturnOne + */ + public String getAnnualizedReturnOne() { + return annualizedReturnOne; + } + + /** + * Returns annualizedReturnTen. + * + * @return annualizedReturnTen + */ + public String getAnnualizedReturnTen() { + return annualizedReturnTen; + } + + /** + * Returns annualizedReturnThree. + * + * @return annualizedReturnThree + */ + public String getAnnualizedReturnThree() { + return annualizedReturnThree; + } + + /** + * Returns annualizedReturnTwo. + * + * @return annualizedReturnTwo + */ + public String getAnnualizedReturnTwo() { + return annualizedReturnTwo; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns fundName. + * + * @return fundName + */ + public String getFundName() { + return fundName; + } + + /** + * Returns performanceRankFiveYears. + * + * @return performanceRankFiveYears + */ + public int getPerformanceRankFiveYears() { + return performanceRankFiveYears; + } + + /** + * Returns performanceRankOneDay. + * + * @return performanceRankOneDay + */ + public int getPerformanceRankOneDay() { + return performanceRankOneDay; + } + + /** + * Returns performanceRankOneMonth. + * + * @return performanceRankOneMonth + */ + public int getPerformanceRankOneMonth() { + return performanceRankOneMonth; + } + + /** + * Returns performanceRankOneWeek. + * + * @return performanceRankOneWeek + */ + public int getPerformanceRankOneWeek() { + return performanceRankOneWeek; + } + + /** + * Returns performanceRankOneYear. + * + * @return performanceRankOneYear + */ + public int getPerformanceRankOneYear() { + return performanceRankOneYear; + } + + /** + * Returns performanceRankSixMonths. + * + * @return performanceRankSixMonths + */ + public int getPerformanceRankSixMonths() { + return performanceRankSixMonths; + } + + /** + * Returns performanceRankTenYears. + * + * @return performanceRankTenYears + */ + public int getPerformanceRankTenYears() { + return performanceRankTenYears; + } + + /** + * Returns performanceRankThreeMonths. + * + * @return performanceRankThreeMonths + */ + public int getPerformanceRankThreeMonths() { + return performanceRankThreeMonths; + } + + /** + * Returns performanceRankThreeYears. + * + * @return performanceRankThreeYears + */ + public int getPerformanceRankThreeYears() { + return performanceRankThreeYears; + } + + /** + * Returns performanceRankTwoYears. + * + * @return performanceRankTwoYears + */ + public int getPerformanceRankTwoYears() { + return performanceRankTwoYears; + } + + /** + * Returns performanceRankYtd. + * + * @return performanceRankYtd + */ + public int getPerformanceRankYtd() { + return performanceRankYtd; + } + + /** + * Returns performanceReturnFiveYears. + * + * @return performanceReturnFiveYears + */ + public String getPerformanceReturnFiveYears() { + return performanceReturnFiveYears; + } + + /** + * Returns performanceReturnOneDay. + * + * @return performanceReturnOneDay + */ + public String getPerformanceReturnOneDay() { + return performanceReturnOneDay; + } + + /** + * Returns performanceReturnOneMonth. + * + * @return performanceReturnOneMonth + */ + public String getPerformanceReturnOneMonth() { + return performanceReturnOneMonth; + } + + /** + * Returns performanceReturnOneWeek. + * + * @return performanceReturnOneWeek + */ + public String getPerformanceReturnOneWeek() { + return performanceReturnOneWeek; + } + + /** + * Returns performanceReturnOneYear. + * + * @return performanceReturnOneYear + */ + public String getPerformanceReturnOneYear() { + return performanceReturnOneYear; + } + + /** + * Returns performanceReturnSixMonths. + * + * @return performanceReturnSixMonths + */ + public String getPerformanceReturnSixMonths() { + return performanceReturnSixMonths; + } + + /** + * Returns performanceReturnTenYears. + * + * @return performanceReturnTenYears + */ + public String getPerformanceReturnTenYears() { + return performanceReturnTenYears; + } + + /** + * Returns performanceReturnThreeMonths. + * + * @return performanceReturnThreeMonths + */ + public String getPerformanceReturnThreeMonths() { + return performanceReturnThreeMonths; + } + + /** + * Returns performanceReturnThreeYears. + * + * @return performanceReturnThreeYears + */ + public String getPerformanceReturnThreeYears() { + return performanceReturnThreeYears; + } + + /** + * Returns performanceReturnTwoYears. + * + * @return performanceReturnTwoYears + */ + public String getPerformanceReturnTwoYears() { + return performanceReturnTwoYears; + } + + /** + * Returns performanceReturnYtd. + * + * @return performanceReturnYtd + */ + public String getPerformanceReturnYtd() { + return performanceReturnYtd; + } + + /** + * Returns performanceTotalFiveYears. + * + * @return performanceTotalFiveYears + */ + public int getPerformanceTotalFiveYears() { + return performanceTotalFiveYears; + } + + /** + * Returns performanceTotalOneDay. + * + * @return performanceTotalOneDay + */ + public int getPerformanceTotalOneDay() { + return performanceTotalOneDay; + } + + /** + * Returns performanceTotalOneMonth. + * + * @return performanceTotalOneMonth + */ + public int getPerformanceTotalOneMonth() { + return performanceTotalOneMonth; + } + + /** + * Returns performanceTotalOneWeek. + * + * @return performanceTotalOneWeek + */ + public int getPerformanceTotalOneWeek() { + return performanceTotalOneWeek; + } + + /** + * Returns performanceTotalOneYear. + * + * @return performanceTotalOneYear + */ + public int getPerformanceTotalOneYear() { + return performanceTotalOneYear; + } + + /** + * Returns performanceTotalSixMonths. + * + * @return performanceTotalSixMonths + */ + public int getPerformanceTotalSixMonths() { + return performanceTotalSixMonths; + } + + /** + * Returns performanceTotalTenYears. + * + * @return performanceTotalTenYears + */ + public int getPerformanceTotalTenYears() { + return performanceTotalTenYears; + } + + /** + * Returns performanceTotalThreeMonths. + * + * @return performanceTotalThreeMonths + */ + public int getPerformanceTotalThreeMonths() { + return performanceTotalThreeMonths; + } + + /** + * Returns performanceTotalThreeYears. + * + * @return performanceTotalThreeYears + */ + public int getPerformanceTotalThreeYears() { + return performanceTotalThreeYears; + } + + /** + * Returns performanceTotalTwoYears. + * + * @return performanceTotalTwoYears + */ + public int getPerformanceTotalTwoYears() { + return performanceTotalTwoYears; + } + + /** + * Returns performanceTotalYtd. + * + * @return performanceTotalYtd + */ + public int getPerformanceTotalYtd() { + return performanceTotalYtd; + } + + /** + * Returns sevenDaysAnnualized. + * + * @return sevenDaysAnnualized + */ + public String getSevenDaysAnnualized() { + return sevenDaysAnnualized; + } + + /** + * Returns tenThousandPrice. + * + * @return tenThousandPrice + */ + public String getTenThousandPrice() { + return tenThousandPrice; + } + + /** + * Returns updateTime. + * + * @return updateTime + */ + public long getUpdateTime() { + return updateTime; + } + + @Override + public String toString() { + return "FundPerformance [annualizedReturnFive=" + annualizedReturnFive + + ", annualizedReturnOne=" + annualizedReturnOne + + ", annualizedReturnTen=" + annualizedReturnTen + + ", annualizedReturnThree=" + annualizedReturnThree + + ", annualizedReturnTwo=" + annualizedReturnTwo + + ", counterId=" + counterId + + ", fundName=" + fundName + + ", performanceRankFiveYears=" + performanceRankFiveYears + + ", performanceRankOneDay=" + performanceRankOneDay + + ", performanceRankOneMonth=" + performanceRankOneMonth + + ", performanceRankOneWeek=" + performanceRankOneWeek + + ", performanceRankOneYear=" + performanceRankOneYear + + ", performanceRankSixMonths=" + performanceRankSixMonths + + ", performanceRankTenYears=" + performanceRankTenYears + + ", performanceRankThreeMonths=" + performanceRankThreeMonths + + ", performanceRankThreeYears=" + performanceRankThreeYears + + ", performanceRankTwoYears=" + performanceRankTwoYears + + ", performanceRankYtd=" + performanceRankYtd + + ", performanceReturnFiveYears=" + performanceReturnFiveYears + + ", performanceReturnOneDay=" + performanceReturnOneDay + + ", performanceReturnOneMonth=" + performanceReturnOneMonth + + ", performanceReturnOneWeek=" + performanceReturnOneWeek + + ", performanceReturnOneYear=" + performanceReturnOneYear + + ", performanceReturnSixMonths=" + performanceReturnSixMonths + + ", performanceReturnTenYears=" + performanceReturnTenYears + + ", performanceReturnThreeMonths=" + performanceReturnThreeMonths + + ", performanceReturnThreeYears=" + performanceReturnThreeYears + + ", performanceReturnTwoYears=" + performanceReturnTwoYears + + ", performanceReturnYtd=" + performanceReturnYtd + + ", performanceTotalFiveYears=" + performanceTotalFiveYears + + ", performanceTotalOneDay=" + performanceTotalOneDay + + ", performanceTotalOneMonth=" + performanceTotalOneMonth + + ", performanceTotalOneWeek=" + performanceTotalOneWeek + + ", performanceTotalOneYear=" + performanceTotalOneYear + + ", performanceTotalSixMonths=" + performanceTotalSixMonths + + ", performanceTotalTenYears=" + performanceTotalTenYears + + ", performanceTotalThreeMonths=" + performanceTotalThreeMonths + + ", performanceTotalThreeYears=" + performanceTotalThreeYears + + ", performanceTotalTwoYears=" + performanceTotalTwoYears + + ", performanceTotalYtd=" + performanceTotalYtd + + ", sevenDaysAnnualized=" + sevenDaysAnnualized + + ", tenThousandPrice=" + tenThousandPrice + + ", updateTime=" + updateTime + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPerformanceComparison.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformanceComparison.java new file mode 100644 index 000000000..7fa89ad30 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformanceComparison.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundPerformanceComparison + */ +public class FundPerformanceComparison { + private FundNamedContrast[] contrastPerformances; + private String[] fundPerformances; + + /** + * Returns contrastPerformances. + * + * @return contrastPerformances + */ + public FundNamedContrast[] getContrastPerformances() { + return contrastPerformances; + } + + /** + * Returns fundPerformances. + * + * @return fundPerformances + */ + public String[] getFundPerformances() { + return fundPerformances; + } + + @Override + public String toString() { + return "FundPerformanceComparison [contrastPerformances=" + java.util.Arrays.toString(contrastPerformances) + + ", fundPerformances=" + java.util.Arrays.toString(fundPerformances) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPerformancePoint.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformancePoint.java new file mode 100644 index 000000000..74ae8b737 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPerformancePoint.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundPerformancePoint + */ +public class FundPerformancePoint { + private String date; + private String lastDone; + + /** + * Returns date. + * + * @return date + */ + public String getDate() { + return date; + } + + /** + * Returns lastDone. + * + * @return lastDone + */ + public String getLastDone() { + return lastDone; + } + + @Override + public String toString() { + return "FundPerformancePoint [date=" + date + + ", lastDone=" + lastDone + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPosition.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPosition.java new file mode 100644 index 000000000..b264667ff --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPosition.java @@ -0,0 +1,121 @@ +package com.longbridge.fund; + +/** + * FundPosition + */ +public class FundPosition { + private String amount; + private String counterId; + private String currency; + private String freezeUnits; + private String holdingProfit; + private String holdingUnits; + private String name; + private String recentProfit; + private long recentTradingDay; + private String sumRecentProfit; + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns freezeUnits. + * + * @return freezeUnits + */ + public String getFreezeUnits() { + return freezeUnits; + } + + /** + * Returns holdingProfit. + * + * @return holdingProfit + */ + public String getHoldingProfit() { + return holdingProfit; + } + + /** + * Returns holdingUnits. + * + * @return holdingUnits + */ + public String getHoldingUnits() { + return holdingUnits; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns recentProfit. + * + * @return recentProfit + */ + public String getRecentProfit() { + return recentProfit; + } + + /** + * Returns recentTradingDay. + * + * @return recentTradingDay + */ + public long getRecentTradingDay() { + return recentTradingDay; + } + + /** + * Returns sumRecentProfit. + * + * @return sumRecentProfit + */ + public String getSumRecentProfit() { + return sumRecentProfit; + } + + @Override + public String toString() { + return "FundPosition [amount=" + amount + + ", counterId=" + counterId + + ", currency=" + currency + + ", freezeUnits=" + freezeUnits + + ", holdingProfit=" + holdingProfit + + ", holdingUnits=" + holdingUnits + + ", name=" + name + + ", recentProfit=" + recentProfit + + ", recentTradingDay=" + recentTradingDay + + ", sumRecentProfit=" + sumRecentProfit + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetail.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetail.java new file mode 100644 index 000000000..4f11b44ae --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetail.java @@ -0,0 +1,44 @@ +package com.longbridge.fund; + +/** + * FundPositionDetail + */ +public class FundPositionDetail { + private FundPositionDetailValues detailValues; + private FundDatedValue[] sumProfit; + private FundUnitValue[] utValue; + + /** + * Returns detailValues. + * + * @return detailValues + */ + public FundPositionDetailValues getDetailValues() { + return detailValues; + } + + /** + * Returns sumProfit. + * + * @return sumProfit + */ + public FundDatedValue[] getSumProfit() { + return sumProfit; + } + + /** + * Returns utValue. + * + * @return utValue + */ + public FundUnitValue[] getUtValue() { + return utValue; + } + + @Override + public String toString() { + return "FundPositionDetail [detailValues=" + detailValues + + ", sumProfit=" + java.util.Arrays.toString(sumProfit) + + ", utValue=" + java.util.Arrays.toString(utValue) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetailValues.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetailValues.java new file mode 100644 index 000000000..e615a2b98 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionDetailValues.java @@ -0,0 +1,176 @@ +package com.longbridge.fund; + +/** + * FundPositionDetailValues + */ +public class FundPositionDetailValues { + private String amount; + private String currency; + private String holdingCost; + private String holdingProfit; + private String holdingProfitRate; + private String holdingUnits; + private String holdingValue; + private String pendingBuyValue; + private String pendingSellValue; + private String profitAmountAccumTd; + private String profitAmountAccumTdRate; + private String recentProfit; + private long recentTradingday; + private String recentUnitValue; + private String soldPendingConfirmUnits; + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns holdingCost. + * + * @return holdingCost + */ + public String getHoldingCost() { + return holdingCost; + } + + /** + * Returns holdingProfit. + * + * @return holdingProfit + */ + public String getHoldingProfit() { + return holdingProfit; + } + + /** + * Returns holdingProfitRate. + * + * @return holdingProfitRate + */ + public String getHoldingProfitRate() { + return holdingProfitRate; + } + + /** + * Returns holdingUnits. + * + * @return holdingUnits + */ + public String getHoldingUnits() { + return holdingUnits; + } + + /** + * Returns holdingValue. + * + * @return holdingValue + */ + public String getHoldingValue() { + return holdingValue; + } + + /** + * Returns pendingBuyValue. + * + * @return pendingBuyValue + */ + public String getPendingBuyValue() { + return pendingBuyValue; + } + + /** + * Returns pendingSellValue. + * + * @return pendingSellValue + */ + public String getPendingSellValue() { + return pendingSellValue; + } + + /** + * Returns profitAmountAccumTd. + * + * @return profitAmountAccumTd + */ + public String getProfitAmountAccumTd() { + return profitAmountAccumTd; + } + + /** + * Returns profitAmountAccumTdRate. + * + * @return profitAmountAccumTdRate + */ + public String getProfitAmountAccumTdRate() { + return profitAmountAccumTdRate; + } + + /** + * Returns recentProfit. + * + * @return recentProfit + */ + public String getRecentProfit() { + return recentProfit; + } + + /** + * Returns recentTradingday. + * + * @return recentTradingday + */ + public long getRecentTradingday() { + return recentTradingday; + } + + /** + * Returns recentUnitValue. + * + * @return recentUnitValue + */ + public String getRecentUnitValue() { + return recentUnitValue; + } + + /** + * Returns soldPendingConfirmUnits. + * + * @return soldPendingConfirmUnits + */ + public String getSoldPendingConfirmUnits() { + return soldPendingConfirmUnits; + } + + @Override + public String toString() { + return "FundPositionDetailValues [amount=" + amount + + ", currency=" + currency + + ", holdingCost=" + holdingCost + + ", holdingProfit=" + holdingProfit + + ", holdingProfitRate=" + holdingProfitRate + + ", holdingUnits=" + holdingUnits + + ", holdingValue=" + holdingValue + + ", pendingBuyValue=" + pendingBuyValue + + ", pendingSellValue=" + pendingSellValue + + ", profitAmountAccumTd=" + profitAmountAccumTd + + ", profitAmountAccumTdRate=" + profitAmountAccumTdRate + + ", recentProfit=" + recentProfit + + ", recentTradingday=" + recentTradingday + + ", recentUnitValue=" + recentUnitValue + + ", soldPendingConfirmUnits=" + soldPendingConfirmUnits + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositionNav.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionNav.java new file mode 100644 index 000000000..0070890af --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionNav.java @@ -0,0 +1,77 @@ +package com.longbridge.fund; + +/** + * FundPositionNav + */ +public class FundPositionNav { + private String change; + private String changePercent; + private String counterId; + private String counterName; + private long lastUpdateTime; + private String value; + + /** + * Returns change. + * + * @return change + */ + public String getChange() { + return change; + } + + /** + * Returns changePercent. + * + * @return changePercent + */ + public String getChangePercent() { + return changePercent; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns counterName. + * + * @return counterName + */ + public String getCounterName() { + return counterName; + } + + /** + * Returns lastUpdateTime. + * + * @return lastUpdateTime + */ + public long getLastUpdateTime() { + return lastUpdateTime; + } + + /** + * Returns value. + * + * @return value + */ + public String getValue() { + return value; + } + + @Override + public String toString() { + return "FundPositionNav [change=" + change + + ", changePercent=" + changePercent + + ", counterId=" + counterId + + ", counterName=" + counterName + + ", lastUpdateTime=" + lastUpdateTime + + ", value=" + value + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositionPerformance.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionPerformance.java new file mode 100644 index 000000000..ce519051f --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionPerformance.java @@ -0,0 +1,220 @@ +package com.longbridge.fund; + +/** + * FundPositionPerformance + */ +public class FundPositionPerformance { + private String annualizedReturnFive; + private String annualizedReturnOne; + private String annualizedReturnTen; + private String annualizedReturnThree; + private String annualizedReturnTwo; + private String counterId; + private String fundName; + private String performanceReturnFiveYears; + private String performanceReturnOneDay; + private String performanceReturnOneMonth; + private String performanceReturnOneWeek; + private String performanceReturnOneYear; + private String performanceReturnSixMonths; + private String performanceReturnTenYears; + private String performanceReturnThreeMonths; + private String performanceReturnThreeYears; + private String performanceReturnTwoYears; + private String performanceReturnYtd; + private long updateTime; + + /** + * Returns annualizedReturnFive. + * + * @return annualizedReturnFive + */ + public String getAnnualizedReturnFive() { + return annualizedReturnFive; + } + + /** + * Returns annualizedReturnOne. + * + * @return annualizedReturnOne + */ + public String getAnnualizedReturnOne() { + return annualizedReturnOne; + } + + /** + * Returns annualizedReturnTen. + * + * @return annualizedReturnTen + */ + public String getAnnualizedReturnTen() { + return annualizedReturnTen; + } + + /** + * Returns annualizedReturnThree. + * + * @return annualizedReturnThree + */ + public String getAnnualizedReturnThree() { + return annualizedReturnThree; + } + + /** + * Returns annualizedReturnTwo. + * + * @return annualizedReturnTwo + */ + public String getAnnualizedReturnTwo() { + return annualizedReturnTwo; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns fundName. + * + * @return fundName + */ + public String getFundName() { + return fundName; + } + + /** + * Returns performanceReturnFiveYears. + * + * @return performanceReturnFiveYears + */ + public String getPerformanceReturnFiveYears() { + return performanceReturnFiveYears; + } + + /** + * Returns performanceReturnOneDay. + * + * @return performanceReturnOneDay + */ + public String getPerformanceReturnOneDay() { + return performanceReturnOneDay; + } + + /** + * Returns performanceReturnOneMonth. + * + * @return performanceReturnOneMonth + */ + public String getPerformanceReturnOneMonth() { + return performanceReturnOneMonth; + } + + /** + * Returns performanceReturnOneWeek. + * + * @return performanceReturnOneWeek + */ + public String getPerformanceReturnOneWeek() { + return performanceReturnOneWeek; + } + + /** + * Returns performanceReturnOneYear. + * + * @return performanceReturnOneYear + */ + public String getPerformanceReturnOneYear() { + return performanceReturnOneYear; + } + + /** + * Returns performanceReturnSixMonths. + * + * @return performanceReturnSixMonths + */ + public String getPerformanceReturnSixMonths() { + return performanceReturnSixMonths; + } + + /** + * Returns performanceReturnTenYears. + * + * @return performanceReturnTenYears + */ + public String getPerformanceReturnTenYears() { + return performanceReturnTenYears; + } + + /** + * Returns performanceReturnThreeMonths. + * + * @return performanceReturnThreeMonths + */ + public String getPerformanceReturnThreeMonths() { + return performanceReturnThreeMonths; + } + + /** + * Returns performanceReturnThreeYears. + * + * @return performanceReturnThreeYears + */ + public String getPerformanceReturnThreeYears() { + return performanceReturnThreeYears; + } + + /** + * Returns performanceReturnTwoYears. + * + * @return performanceReturnTwoYears + */ + public String getPerformanceReturnTwoYears() { + return performanceReturnTwoYears; + } + + /** + * Returns performanceReturnYtd. + * + * @return performanceReturnYtd + */ + public String getPerformanceReturnYtd() { + return performanceReturnYtd; + } + + /** + * Returns updateTime. + * + * @return updateTime + */ + public long getUpdateTime() { + return updateTime; + } + + @Override + public String toString() { + return "FundPositionPerformance [annualizedReturnFive=" + annualizedReturnFive + + ", annualizedReturnOne=" + annualizedReturnOne + + ", annualizedReturnTen=" + annualizedReturnTen + + ", annualizedReturnThree=" + annualizedReturnThree + + ", annualizedReturnTwo=" + annualizedReturnTwo + + ", counterId=" + counterId + + ", fundName=" + fundName + + ", performanceReturnFiveYears=" + performanceReturnFiveYears + + ", performanceReturnOneDay=" + performanceReturnOneDay + + ", performanceReturnOneMonth=" + performanceReturnOneMonth + + ", performanceReturnOneWeek=" + performanceReturnOneWeek + + ", performanceReturnOneYear=" + performanceReturnOneYear + + ", performanceReturnSixMonths=" + performanceReturnSixMonths + + ", performanceReturnTenYears=" + performanceReturnTenYears + + ", performanceReturnThreeMonths=" + performanceReturnThreeMonths + + ", performanceReturnThreeYears=" + performanceReturnThreeYears + + ", performanceReturnTwoYears=" + performanceReturnTwoYears + + ", performanceReturnYtd=" + performanceReturnYtd + + ", updateTime=" + updateTime + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositionProfits.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionProfits.java new file mode 100644 index 000000000..bee70a22c --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositionProfits.java @@ -0,0 +1,55 @@ +package com.longbridge.fund; + +/** + * FundPositionProfits + */ +public class FundPositionProfits { + private String currency; + private FundDatedValue[] historyValue; + private long lastUpdateTime; + private String sumProfit; + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns historyValue. + * + * @return historyValue + */ + public FundDatedValue[] getHistoryValue() { + return historyValue; + } + + /** + * Returns lastUpdateTime. + * + * @return lastUpdateTime + */ + public long getLastUpdateTime() { + return lastUpdateTime; + } + + /** + * Returns sumProfit. + * + * @return sumProfit + */ + public String getSumProfit() { + return sumProfit; + } + + @Override + public String toString() { + return "FundPositionProfits [currency=" + currency + + ", historyValue=" + java.util.Arrays.toString(historyValue) + + ", lastUpdateTime=" + lastUpdateTime + + ", sumProfit=" + sumProfit + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundPositions.java b/java/javasrc/src/main/java/com/longbridge/fund/FundPositions.java new file mode 100644 index 000000000..2220bc410 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundPositions.java @@ -0,0 +1,66 @@ +package com.longbridge.fund; + +/** + * FundPositions + */ +public class FundPositions { + private String accountChannel; + private FundPosition[] list; + private String pendingBuyOrders; + private long recentTradingDay; + private String soldPendingCreditOrders; + + /** + * Returns accountChannel. + * + * @return accountChannel + */ + public String getAccountChannel() { + return accountChannel; + } + + /** + * Returns list. + * + * @return list + */ + public FundPosition[] getList() { + return list; + } + + /** + * Returns pendingBuyOrders. + * + * @return pendingBuyOrders + */ + public String getPendingBuyOrders() { + return pendingBuyOrders; + } + + /** + * Returns recentTradingDay. + * + * @return recentTradingDay + */ + public long getRecentTradingDay() { + return recentTradingDay; + } + + /** + * Returns soldPendingCreditOrders. + * + * @return soldPendingCreditOrders + */ + public String getSoldPendingCreditOrders() { + return soldPendingCreditOrders; + } + + @Override + public String toString() { + return "FundPositions [accountChannel=" + accountChannel + + ", list=" + java.util.Arrays.toString(list) + + ", pendingBuyOrders=" + pendingBuyOrders + + ", recentTradingDay=" + recentTradingDay + + ", soldPendingCreditOrders=" + soldPendingCreditOrders + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundQuarterlyReturn.java b/java/javasrc/src/main/java/com/longbridge/fund/FundQuarterlyReturn.java new file mode 100644 index 000000000..65efe7436 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundQuarterlyReturn.java @@ -0,0 +1,44 @@ +package com.longbridge.fund; + +/** + * FundQuarterlyReturn + */ +public class FundQuarterlyReturn { + private String changePercent; + private int quarter; + private int year; + + /** + * Returns changePercent. + * + * @return changePercent + */ + public String getChangePercent() { + return changePercent; + } + + /** + * Returns quarter. + * + * @return quarter + */ + public int getQuarter() { + return quarter; + } + + /** + * Returns year. + * + * @return year + */ + public int getYear() { + return year; + } + + @Override + public String toString() { + return "FundQuarterlyReturn [changePercent=" + changePercent + + ", quarter=" + quarter + + ", year=" + year + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundStockHolding.java b/java/javasrc/src/main/java/com/longbridge/fund/FundStockHolding.java new file mode 100644 index 000000000..a256c1595 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundStockHolding.java @@ -0,0 +1,77 @@ +package com.longbridge.fund; + +/** + * FundStockHolding + */ +public class FundStockHolding { + private String code; + private String counterId; + private String currency; + private String name; + private String positionRatio; + private String reportDate; + + /** + * Returns code. + * + * @return code + */ + public String getCode() { + return code; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns positionRatio. + * + * @return positionRatio + */ + public String getPositionRatio() { + return positionRatio; + } + + /** + * Returns reportDate. + * + * @return reportDate + */ + public String getReportDate() { + return reportDate; + } + + @Override + public String toString() { + return "FundStockHolding [code=" + code + + ", counterId=" + counterId + + ", currency=" + currency + + ", name=" + name + + ", positionRatio=" + positionRatio + + ", reportDate=" + reportDate + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundTransaction.java b/java/javasrc/src/main/java/com/longbridge/fund/FundTransaction.java new file mode 100644 index 000000000..9f9eb7d65 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundTransaction.java @@ -0,0 +1,176 @@ +package com.longbridge.fund; + +/** + * FundTransaction + */ +public class FundTransaction { + private String amount; + private String category; + private long createdAt; + private String currency; + private String description; + private long detailCreatedAt; + private String detailType; + private long doneAt; + private String quantityDescription; + private String redirectPage; + private String redirectPageV2; + private String refNo; + private String stockQuantity; + private String txType; + private String typeName; + + /** + * Returns amount. + * + * @return amount + */ + public String getAmount() { + return amount; + } + + /** + * Returns category. + * + * @return category + */ + public String getCategory() { + return category; + } + + /** + * Returns createdAt. + * + * @return createdAt + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns description. + * + * @return description + */ + public String getDescription() { + return description; + } + + /** + * Returns detailCreatedAt. + * + * @return detailCreatedAt + */ + public long getDetailCreatedAt() { + return detailCreatedAt; + } + + /** + * Returns detailType. + * + * @return detailType + */ + public String getDetailType() { + return detailType; + } + + /** + * Returns doneAt. + * + * @return doneAt + */ + public long getDoneAt() { + return doneAt; + } + + /** + * Returns quantityDescription. + * + * @return quantityDescription + */ + public String getQuantityDescription() { + return quantityDescription; + } + + /** + * Returns redirectPage. + * + * @return redirectPage + */ + public String getRedirectPage() { + return redirectPage; + } + + /** + * Returns redirectPageV2. + * + * @return redirectPageV2 + */ + public String getRedirectPageV2() { + return redirectPageV2; + } + + /** + * Returns refNo. + * + * @return refNo + */ + public String getRefNo() { + return refNo; + } + + /** + * Returns stockQuantity. + * + * @return stockQuantity + */ + public String getStockQuantity() { + return stockQuantity; + } + + /** + * Returns txType. + * + * @return txType + */ + public String getTxType() { + return txType; + } + + /** + * Returns typeName. + * + * @return typeName + */ + public String getTypeName() { + return typeName; + } + + @Override + public String toString() { + return "FundTransaction [amount=" + amount + + ", category=" + category + + ", createdAt=" + createdAt + + ", currency=" + currency + + ", description=" + description + + ", detailCreatedAt=" + detailCreatedAt + + ", detailType=" + detailType + + ", doneAt=" + doneAt + + ", quantityDescription=" + quantityDescription + + ", redirectPage=" + redirectPage + + ", redirectPageV2=" + redirectPageV2 + + ", refNo=" + refNo + + ", stockQuantity=" + stockQuantity + + ", txType=" + txType + + ", typeName=" + typeName + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundTrend.java b/java/javasrc/src/main/java/com/longbridge/fund/FundTrend.java new file mode 100644 index 000000000..223bac898 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundTrend.java @@ -0,0 +1,66 @@ +package com.longbridge.fund; + +/** + * FundTrend + */ +public class FundTrend { + private int actualPeriod; + private int[] availablePeriods; + private String[] categoryAveragePerformances; + private FundTrendContrast contrastPerformances; + private String[] fundPerformances; + + /** + * Returns actualPeriod. + * + * @return actualPeriod + */ + public int getActualPeriod() { + return actualPeriod; + } + + /** + * Returns availablePeriods. + * + * @return availablePeriods + */ + public int[] getAvailablePeriods() { + return availablePeriods; + } + + /** + * Returns categoryAveragePerformances. + * + * @return categoryAveragePerformances + */ + public String[] getCategoryAveragePerformances() { + return categoryAveragePerformances; + } + + /** + * Returns contrastPerformances. + * + * @return contrastPerformances + */ + public FundTrendContrast getContrastPerformances() { + return contrastPerformances; + } + + /** + * Returns fundPerformances. + * + * @return fundPerformances + */ + public String[] getFundPerformances() { + return fundPerformances; + } + + @Override + public String toString() { + return "FundTrend [actualPeriod=" + actualPeriod + + ", availablePeriods=" + java.util.Arrays.toString(availablePeriods) + + ", categoryAveragePerformances=" + java.util.Arrays.toString(categoryAveragePerformances) + + ", contrastPerformances=" + contrastPerformances + + ", fundPerformances=" + java.util.Arrays.toString(fundPerformances) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundTrendContrast.java b/java/javasrc/src/main/java/com/longbridge/fund/FundTrendContrast.java new file mode 100644 index 000000000..9981b0ae8 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundTrendContrast.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * FundTrendContrast + */ +public class FundTrendContrast { + private String benchmarkName; + private String[] performances; + + /** + * Returns benchmarkName. + * + * @return benchmarkName + */ + public String getBenchmarkName() { + return benchmarkName; + } + + /** + * Returns performances. + * + * @return performances + */ + public String[] getPerformances() { + return performances; + } + + @Override + public String toString() { + return "FundTrendContrast [benchmarkName=" + benchmarkName + + ", performances=" + java.util.Arrays.toString(performances) + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/FundUnitValue.java b/java/javasrc/src/main/java/com/longbridge/fund/FundUnitValue.java new file mode 100644 index 000000000..56642dd26 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/FundUnitValue.java @@ -0,0 +1,55 @@ +package com.longbridge.fund; + +/** + * FundUnitValue + */ +public class FundUnitValue { + private long date; + private String dayIncreaseRate; + private String totalValue; + private String unitValue; + + /** + * Returns date. + * + * @return date + */ + public long getDate() { + return date; + } + + /** + * Returns dayIncreaseRate. + * + * @return dayIncreaseRate + */ + public String getDayIncreaseRate() { + return dayIncreaseRate; + } + + /** + * Returns totalValue. + * + * @return totalValue + */ + public String getTotalValue() { + return totalValue; + } + + /** + * Returns unitValue. + * + * @return unitValue + */ + public String getUnitValue() { + return unitValue; + } + + @Override + public String toString() { + return "FundUnitValue [date=" + date + + ", dayIncreaseRate=" + dayIncreaseRate + + ", totalValue=" + totalValue + + ", unitValue=" + unitValue + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundAnalysisOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundAnalysisOptions.java new file mode 100644 index 000000000..216154c9e --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundAnalysisOptions.java @@ -0,0 +1,21 @@ +package com.longbridge.fund; + +/** + * Options for GetFundAnalysisOptions. + */ +@SuppressWarnings("unused") +public class GetFundAnalysisOptions { + private Integer period; + + /** + * Sets period. + * + * @param period period + * @return this instance for chaining + */ + public GetFundAnalysisOptions setPeriod(Integer period) { + this.period = period; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundHoldingsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundHoldingsOptions.java new file mode 100644 index 000000000..374e437c0 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundHoldingsOptions.java @@ -0,0 +1,21 @@ +package com.longbridge.fund; + +/** + * Options for GetFundHoldingsOptions. + */ +@SuppressWarnings("unused") +public class GetFundHoldingsOptions { + private Integer scene; + + /** + * Sets scene. + * + * @param scene scene + * @return this instance for chaining + */ + public GetFundHoldingsOptions setScene(Integer scene) { + this.scene = scene; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundOrdersOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundOrdersOptions.java new file mode 100644 index 000000000..ff9bce9c1 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundOrdersOptions.java @@ -0,0 +1,105 @@ +package com.longbridge.fund; + +/** + * Options for GetFundOrdersOptions. + */ +@SuppressWarnings("unused") +public class GetFundOrdersOptions { + private String[] counterIds; + private String actions; + private String states; + private String currency; + private Long start; + private Long end; + private Integer page; + private Integer size; + + /** + * Sets counterIds. + * + * @param counterIds counterIds + * @return this instance for chaining + */ + public GetFundOrdersOptions setCounterIds(String[] counterIds) { + this.counterIds = counterIds; + return this; + } + + /** + * Sets actions. + * + * @param actions actions + * @return this instance for chaining + */ + public GetFundOrdersOptions setActions(String actions) { + this.actions = actions; + return this; + } + + /** + * Sets states. + * + * @param states states + * @return this instance for chaining + */ + public GetFundOrdersOptions setStates(String states) { + this.states = states; + return this; + } + + /** + * Sets currency. + * + * @param currency currency + * @return this instance for chaining + */ + public GetFundOrdersOptions setCurrency(String currency) { + this.currency = currency; + return this; + } + + /** + * Sets start. + * + * @param start start + * @return this instance for chaining + */ + public GetFundOrdersOptions setStart(Long start) { + this.start = start; + return this; + } + + /** + * Sets end. + * + * @param end end + * @return this instance for chaining + */ + public GetFundOrdersOptions setEnd(Long end) { + this.end = end; + return this; + } + + /** + * Sets page. + * + * @param page page + * @return this instance for chaining + */ + public GetFundOrdersOptions setPage(Integer page) { + this.page = page; + return this; + } + + /** + * Sets size. + * + * @param size size + * @return this instance for chaining + */ + public GetFundOrdersOptions setSize(Integer size) { + this.size = size; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionDividendsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionDividendsOptions.java new file mode 100644 index 000000000..e3d40b0d4 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionDividendsOptions.java @@ -0,0 +1,93 @@ +package com.longbridge.fund; + +/** + * Options for GetFundPositionDividendsOptions. + */ +@SuppressWarnings("unused") +public class GetFundPositionDividendsOptions { + private String accountChannel; + private Long aaid; + private String currency; + private Long start; + private Long end; + private Integer page; + private Integer size; + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + + /** + * Sets aaid. + * + * @param aaid aaid + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setAaid(Long aaid) { + this.aaid = aaid; + return this; + } + + /** + * Sets currency. + * + * @param currency currency + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setCurrency(String currency) { + this.currency = currency; + return this; + } + + /** + * Sets start. + * + * @param start start + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setStart(Long start) { + this.start = start; + return this; + } + + /** + * Sets end. + * + * @param end end + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setEnd(Long end) { + this.end = end; + return this; + } + + /** + * Sets page. + * + * @param page page + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setPage(Integer page) { + this.page = page; + return this; + } + + /** + * Sets size. + * + * @param size size + * @return this instance for chaining + */ + public GetFundPositionDividendsOptions setSize(Integer size) { + this.size = size; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionOptions.java new file mode 100644 index 000000000..3f3369452 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionOptions.java @@ -0,0 +1,57 @@ +package com.longbridge.fund; + +/** + * Options for GetFundPositionOptions. + */ +@SuppressWarnings("unused") +public class GetFundPositionOptions { + private String accountChannel; + private Long aaid; + private String start; + private String end; + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public GetFundPositionOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + + /** + * Sets aaid. + * + * @param aaid aaid + * @return this instance for chaining + */ + public GetFundPositionOptions setAaid(Long aaid) { + this.aaid = aaid; + return this; + } + + /** + * Sets start. + * + * @param start start + * @return this instance for chaining + */ + public GetFundPositionOptions setStart(String start) { + this.start = start; + return this; + } + + /** + * Sets end. + * + * @param end end + * @return this instance for chaining + */ + public GetFundPositionOptions setEnd(String end) { + this.end = end; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionProfitsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionProfitsOptions.java new file mode 100644 index 000000000..7a9b49f4e --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionProfitsOptions.java @@ -0,0 +1,81 @@ +package com.longbridge.fund; + +/** + * Options for GetFundPositionProfitsOptions. + */ +@SuppressWarnings("unused") +public class GetFundPositionProfitsOptions { + private String accountChannel; + private Long aaid; + private String start; + private String end; + private Integer page; + private Integer size; + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + + /** + * Sets aaid. + * + * @param aaid aaid + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setAaid(Long aaid) { + this.aaid = aaid; + return this; + } + + /** + * Sets start. + * + * @param start start + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setStart(String start) { + this.start = start; + return this; + } + + /** + * Sets end. + * + * @param end end + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setEnd(String end) { + this.end = end; + return this; + } + + /** + * Sets page. + * + * @param page page + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setPage(Integer page) { + this.page = page; + return this; + } + + /** + * Sets size. + * + * @param size size + * @return this instance for chaining + */ + public GetFundPositionProfitsOptions setSize(Integer size) { + this.size = size; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionsOptions.java new file mode 100644 index 000000000..1ced05f46 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundPositionsOptions.java @@ -0,0 +1,33 @@ +package com.longbridge.fund; + +/** + * Options for GetFundPositionsOptions. + */ +@SuppressWarnings("unused") +public class GetFundPositionsOptions { + private String accountChannel; + private Long aaid; + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public GetFundPositionsOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + + /** + * Sets aaid. + * + * @param aaid aaid + * @return this instance for chaining + */ + public GetFundPositionsOptions setAaid(Long aaid) { + this.aaid = aaid; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundStockHoldingsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundStockHoldingsOptions.java new file mode 100644 index 000000000..5b34a0c57 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundStockHoldingsOptions.java @@ -0,0 +1,21 @@ +package com.longbridge.fund; + +/** + * Options for GetFundStockHoldingsOptions. + */ +@SuppressWarnings("unused") +public class GetFundStockHoldingsOptions { + private Integer limit; + + /** + * Sets limit. + * + * @param limit limit + * @return this instance for chaining + */ + public GetFundStockHoldingsOptions setLimit(Integer limit) { + this.limit = limit; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundTransactionsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundTransactionsOptions.java new file mode 100644 index 000000000..ff59fdabc --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundTransactionsOptions.java @@ -0,0 +1,105 @@ +package com.longbridge.fund; + +/** + * Options for GetFundTransactionsOptions. + */ +@SuppressWarnings("unused") +public class GetFundTransactionsOptions { + private String accountChannel; + private String businessType; + private String category; + private String currencies; + private Long start; + private Long end; + private Integer page; + private Integer size; + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public GetFundTransactionsOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + + /** + * Sets businessType. + * + * @param businessType businessType + * @return this instance for chaining + */ + public GetFundTransactionsOptions setBusinessType(String businessType) { + this.businessType = businessType; + return this; + } + + /** + * Sets category. + * + * @param category category + * @return this instance for chaining + */ + public GetFundTransactionsOptions setCategory(String category) { + this.category = category; + return this; + } + + /** + * Sets currencies. + * + * @param currencies currencies + * @return this instance for chaining + */ + public GetFundTransactionsOptions setCurrencies(String currencies) { + this.currencies = currencies; + return this; + } + + /** + * Sets start. + * + * @param start start + * @return this instance for chaining + */ + public GetFundTransactionsOptions setStart(Long start) { + this.start = start; + return this; + } + + /** + * Sets end. + * + * @param end end + * @return this instance for chaining + */ + public GetFundTransactionsOptions setEnd(Long end) { + this.end = end; + return this; + } + + /** + * Sets page. + * + * @param page page + * @return this instance for chaining + */ + public GetFundTransactionsOptions setPage(Integer page) { + this.page = page; + return this; + } + + /** + * Sets size. + * + * @param size size + * @return this instance for chaining + */ + public GetFundTransactionsOptions setSize(Integer size) { + this.size = size; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/GetFundsOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/GetFundsOptions.java new file mode 100644 index 000000000..d2f7ae8d2 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/GetFundsOptions.java @@ -0,0 +1,45 @@ +package com.longbridge.fund; + +/** + * Options for GetFundsOptions. + */ +@SuppressWarnings("unused") +public class GetFundsOptions { + private String filter; + private long[] quickIds; + private String[] timeInterval; + + /** + * Sets filter. + * + * @param filter filter + * @return this instance for chaining + */ + public GetFundsOptions setFilter(String filter) { + this.filter = filter; + return this; + } + + /** + * Sets quickIds. + * + * @param quickIds quickIds + * @return this instance for chaining + */ + public GetFundsOptions setQuickIds(long[] quickIds) { + this.quickIds = quickIds; + return this; + } + + /** + * Sets timeInterval. + * + * @param timeInterval timeInterval + * @return this instance for chaining + */ + public GetFundsOptions setTimeInterval(String[] timeInterval) { + this.timeInterval = timeInterval; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/HotFund.java b/java/javasrc/src/main/java/com/longbridge/fund/HotFund.java new file mode 100644 index 000000000..d10ec7734 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/HotFund.java @@ -0,0 +1,143 @@ +package com.longbridge.fund; + +/** + * HotFund + */ +public class HotFund { + private int assetClass; + private String assetClassName; + private String counterId; + private String currency; + private String earningRate; + private FundPerformancePoint[] fundPerformances; + private String name; + private String purchaseAmount; + private String recommendationText; + private int riskLevel; + private String riskLevelName; + private String timeInterval; + + /** + * Returns assetClass. + * + * @return assetClass + */ + public int getAssetClass() { + return assetClass; + } + + /** + * Returns assetClassName. + * + * @return assetClassName + */ + public String getAssetClassName() { + return assetClassName; + } + + /** + * Returns counterId. + * + * @return counterId + */ + public String getCounterId() { + return counterId; + } + + /** + * Returns currency. + * + * @return currency + */ + public String getCurrency() { + return currency; + } + + /** + * Returns earningRate. + * + * @return earningRate + */ + public String getEarningRate() { + return earningRate; + } + + /** + * Returns fundPerformances. + * + * @return fundPerformances + */ + public FundPerformancePoint[] getFundPerformances() { + return fundPerformances; + } + + /** + * Returns name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Returns purchaseAmount. + * + * @return purchaseAmount + */ + public String getPurchaseAmount() { + return purchaseAmount; + } + + /** + * Returns recommendationText. + * + * @return recommendationText + */ + public String getRecommendationText() { + return recommendationText; + } + + /** + * Returns riskLevel. + * + * @return riskLevel + */ + public int getRiskLevel() { + return riskLevel; + } + + /** + * Returns riskLevelName. + * + * @return riskLevelName + */ + public String getRiskLevelName() { + return riskLevelName; + } + + /** + * Returns timeInterval. + * + * @return timeInterval + */ + public String getTimeInterval() { + return timeInterval; + } + + @Override + public String toString() { + return "HotFund [assetClass=" + assetClass + + ", assetClassName=" + assetClassName + + ", counterId=" + counterId + + ", currency=" + currency + + ", earningRate=" + earningRate + + ", fundPerformances=" + java.util.Arrays.toString(fundPerformances) + + ", name=" + name + + ", purchaseAmount=" + purchaseAmount + + ", recommendationText=" + recommendationText + + ", riskLevel=" + riskLevel + + ", riskLevelName=" + riskLevelName + + ", timeInterval=" + timeInterval + "]"; + } +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/SubmitFundOrderOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/SubmitFundOrderOptions.java new file mode 100644 index 000000000..95dc4ab44 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/SubmitFundOrderOptions.java @@ -0,0 +1,109 @@ +package com.longbridge.fund; + +/** + * Options for SubmitFundOrderOptions. + */ +@SuppressWarnings("unused") +public class SubmitFundOrderOptions { + private String counterId; + private String action; + private String currency; + private String amount; + private String units; + private Integer dividendOption; + private String fee; + private Boolean isSellAll; + private String remark; + private Integer tradeMethod; + + /** + * Creates a new SubmitFundOrderOptions. + * + * @param counterId counterId + * @param action action + * @param currency currency + */ + public SubmitFundOrderOptions(String counterId, String action, String currency) { + this.counterId = counterId; + this.action = action; + this.currency = currency; + } + + /** + * Sets amount. + * + * @param amount amount + * @return this instance for chaining + */ + public SubmitFundOrderOptions setAmount(String amount) { + this.amount = amount; + return this; + } + + /** + * Sets units. + * + * @param units units + * @return this instance for chaining + */ + public SubmitFundOrderOptions setUnits(String units) { + this.units = units; + return this; + } + + /** + * Sets dividendOption. + * + * @param dividendOption dividendOption + * @return this instance for chaining + */ + public SubmitFundOrderOptions setDividendOption(Integer dividendOption) { + this.dividendOption = dividendOption; + return this; + } + + /** + * Sets fee. + * + * @param fee fee + * @return this instance for chaining + */ + public SubmitFundOrderOptions setFee(String fee) { + this.fee = fee; + return this; + } + + /** + * Sets isSellAll. + * + * @param isSellAll isSellAll + * @return this instance for chaining + */ + public SubmitFundOrderOptions setIsSellAll(Boolean isSellAll) { + this.isSellAll = isSellAll; + return this; + } + + /** + * Sets remark. + * + * @param remark remark + * @return this instance for chaining + */ + public SubmitFundOrderOptions setRemark(String remark) { + this.remark = remark; + return this; + } + + /** + * Sets tradeMethod. + * + * @param tradeMethod tradeMethod + * @return this instance for chaining + */ + public SubmitFundOrderOptions setTradeMethod(Integer tradeMethod) { + this.tradeMethod = tradeMethod; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/fund/ValidateFundOrderOptions.java b/java/javasrc/src/main/java/com/longbridge/fund/ValidateFundOrderOptions.java new file mode 100644 index 000000000..2d3d0d639 --- /dev/null +++ b/java/javasrc/src/main/java/com/longbridge/fund/ValidateFundOrderOptions.java @@ -0,0 +1,85 @@ +package com.longbridge.fund; + +/** + * Options for ValidateFundOrderOptions. + */ +@SuppressWarnings("unused") +public class ValidateFundOrderOptions { + private String counterId; + private String action; + private String currency; + private String amount; + private String units; + private Integer dividendOption; + private Integer fundSource; + private String accountChannel; + + /** + * Creates a new ValidateFundOrderOptions. + * + * @param counterId counterId + * @param action action + * @param currency currency + */ + public ValidateFundOrderOptions(String counterId, String action, String currency) { + this.counterId = counterId; + this.action = action; + this.currency = currency; + } + + /** + * Sets amount. + * + * @param amount amount + * @return this instance for chaining + */ + public ValidateFundOrderOptions setAmount(String amount) { + this.amount = amount; + return this; + } + + /** + * Sets units. + * + * @param units units + * @return this instance for chaining + */ + public ValidateFundOrderOptions setUnits(String units) { + this.units = units; + return this; + } + + /** + * Sets dividendOption. + * + * @param dividendOption dividendOption + * @return this instance for chaining + */ + public ValidateFundOrderOptions setDividendOption(Integer dividendOption) { + this.dividendOption = dividendOption; + return this; + } + + /** + * Sets fundSource. + * + * @param fundSource fundSource + * @return this instance for chaining + */ + public ValidateFundOrderOptions setFundSource(Integer fundSource) { + this.fundSource = fundSource; + return this; + } + + /** + * Sets accountChannel. + * + * @param accountChannel accountChannel + * @return this instance for chaining + */ + public ValidateFundOrderOptions setAccountChannel(String accountChannel) { + this.accountChannel = accountChannel; + return this; + } + +} diff --git a/java/javasrc/src/main/java/com/longbridge/trade/FundPosition.java b/java/javasrc/src/main/java/com/longbridge/trade/FundPosition.java index c661737eb..b07cc3f12 100644 --- a/java/javasrc/src/main/java/com/longbridge/trade/FundPosition.java +++ b/java/javasrc/src/main/java/com/longbridge/trade/FundPosition.java @@ -7,7 +7,7 @@ * Fund position */ public class FundPosition { - private String symbol; + private String counterId; private BigDecimal currentNetAssetValue; private OffsetDateTime netAssetValueDay; private String symbolName; @@ -16,12 +16,12 @@ public class FundPosition { private BigDecimal holdingUnits; /** - * Returns the fund symbol. + * Returns the fund counter id (the ISIN is the last {@code /}-separated segment). * - * @return fund symbol + * @return fund counter id */ - public String getSymbol() { - return symbol; + public String getCounterId() { + return counterId; } /** @@ -80,7 +80,7 @@ public BigDecimal getHoldingUnits() { @Override public String toString() { - return "FundPosition [symbol=" + symbol + ", currentNetAssetValue=" + currentNetAssetValue + return "FundPosition [counterId=" + counterId + ", currentNetAssetValue=" + currentNetAssetValue + ", netAssetValueDay=" + netAssetValueDay + ", symbolName=" + symbolName + ", currency=" + currency + ", costNetAssetValue=" + costNetAssetValue + ", holdingUnits=" + holdingUnits + "]"; } diff --git a/java/src/fund_context.rs b/java/src/fund_context.rs new file mode 100644 index 000000000..baee1a084 --- /dev/null +++ b/java/src/fund_context.rs @@ -0,0 +1,922 @@ +use std::sync::Arc; + +use jni::{ + JNIEnv, + objects::{JClass, JObject, JString}, +}; +use longbridge::{ + Config, + fund::{ + FundContext, FundNavRangeOptions, FundPageOptions, GetFundAnalysisOptions, + GetFundHoldingsOptions, GetFundOrdersOptions, GetFundPositionDividendsOptions, + GetFundPositionOptions, GetFundPositionProfitsOptions, GetFundPositionsOptions, + GetFundStockHoldingsOptions, GetFundTransactionsOptions, GetFundsOptions, + SubmitFundOrderOptions, ValidateFundOrderOptions, + }, +}; + +use crate::{ + async_util, + error::jni_result, + types::{FromJValue, JavaInteger, JavaLong, ObjectArray, PrimaryArray, get_field}, +}; + +struct ContextObj { + ctx: FundContext, +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_newFundContext( + mut env: JNIEnv, + _class: JClass, + config: i64, +) -> i64 { + jni_result(&mut env, 0i64, |_env| { + Ok(Box::into_raw(Box::new(ContextObj { + ctx: FundContext::new(Arc::new((*(config as *const Config)).clone())), + })) as i64) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_freeFundContext( + _env: JNIEnv, + _class: JClass, + ctx: i64, +) { + let _ = Box::from_raw(ctx as *mut ContextObj); +} + +// ----- fund catalog / market data ----- + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextHotFunds( + mut env: JNIEnv, + _class: JClass, + context: i64, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.hot_funds().await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextFunds( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let opts = if !opts.is_null() { + let mut new_opts = GetFundsOptions::new(); + if let Some(filter) = get_field::<_, _, Option>(env, &opts, "filter")? + && let Ok(value) = serde_json::from_str::(&filter) + { + new_opts = new_opts.filter(value); + } + let quick_ids: PrimaryArray = get_field(env, &opts, "quickIds")?; + if !quick_ids.0.is_empty() { + new_opts = new_opts.quick_ids(quick_ids.0); + } + let time_interval: ObjectArray = get_field(env, &opts, "timeInterval")?; + if !time_interval.0.is_empty() { + new_opts = new_opts.time_interval(time_interval.0); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.funds(opts).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextFilters( + mut env: JNIEnv, + _class: JClass, + context: i64, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + async_util::execute( + env, + callback, + async move { Ok(__owned_ctx.filters().await?) }, + )?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextDetail( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.detail(counter_id).await?) + })?; + Ok(()) + }) +} + +/// Read a nullable Java `Boolean` field as `Option`. +fn read_opt_bool( + env: &mut JNIEnv<'_>, + obj: &JObject<'_>, + name: &str, +) -> jni::errors::Result> { + let field = env.get_field(obj, name, "Ljava/lang/Boolean;")?.l()?; + if field.is_null() { + Ok(None) + } else { + Ok(Some( + env.call_method(&field, "booleanValue", "()Z", &[])?.z()?, + )) + } +} + +/// Read a [`GetFundAnalysisOptions`] from a nullable Java options object. +fn read_analysis_opts( + env: &mut JNIEnv<'_>, + opts: &JObject<'_>, +) -> jni::errors::Result> { + if opts.is_null() { + return Ok(None); + } + let mut new_opts = GetFundAnalysisOptions::new(); + if let Some(period) = get_field::<_, _, Option>(env, opts, "period")? { + new_opts = new_opts.period(period.into()); + } + Ok(Some(new_opts)) +} + +/// Read a [`FundPageOptions`] from a nullable Java options object. +fn read_page_opts( + env: &mut JNIEnv<'_>, + opts: &JObject<'_>, +) -> jni::errors::Result> { + if opts.is_null() { + return Ok(None); + } + let mut new_opts = FundPageOptions::new(); + if let Some(page) = get_field::<_, _, Option>(env, opts, "page")? { + new_opts = new_opts.page(page.into()); + } + if let Some(size) = get_field::<_, _, Option>(env, opts, "size")? { + new_opts = new_opts.size(size.into()); + } + Ok(Some(new_opts)) +} + +/// Read a [`FundNavRangeOptions`] from a nullable Java options object. +fn read_nav_range_opts( + env: &mut JNIEnv<'_>, + opts: &JObject<'_>, +) -> jni::errors::Result> { + if opts.is_null() { + return Ok(None); + } + let mut new_opts = FundNavRangeOptions::new(); + if let Some(month_before) = get_field::<_, _, Option>(env, opts, "monthBefore")? { + new_opts = new_opts.month_before(month_before.into()); + } + if let Some(year_before) = get_field::<_, _, Option>(env, opts, "yearBefore")? { + new_opts = new_opts.year_before(year_before.into()); + } + Ok(Some(new_opts)) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextAnalysis( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_analysis_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.analysis(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextAnalysisDetail( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_analysis_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.analysis_detail(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextTrend( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_analysis_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.trend(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextAnnualReturns( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_page_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.annual_returns(counter_id, opts).await?, + )) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextQuarterlyReturns( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_page_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.quarterly_returns(counter_id, opts).await?, + )) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPerformance( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.performance(counter_id).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPerformanceComparison( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_analysis_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.performance_comparison(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextNav( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.nav(counter_id).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextNavHistory( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_page_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.nav_history(counter_id, opts).await?, + )) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextNavRange( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_nav_range_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.nav_range(counter_id, opts).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextHoldings( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = if !opts.is_null() { + let mut new_opts = GetFundHoldingsOptions::new(); + if let Some(scene) = get_field::<_, _, Option>(env, &opts, "scene")? { + new_opts = new_opts.scene(scene.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.holdings(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextStockHoldings( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = if !opts.is_null() { + let mut new_opts = GetFundStockHoldingsOptions::new(); + if let Some(limit) = get_field::<_, _, Option>(env, &opts, "limit")? { + new_opts = new_opts.limit(limit.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.stock_holdings(counter_id, opts).await?, + )) + })?; + Ok(()) + }) +} + +// ----- user fund positions ----- + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPositions( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let opts = if !opts.is_null() { + let mut new_opts = GetFundPositionsOptions::new(); + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + if let Some(aaid) = get_field::<_, _, Option>(env, &opts, "aaid")? { + new_opts = new_opts.aaid(aaid.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.positions(opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPosition( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = if !opts.is_null() { + let mut new_opts = GetFundPositionOptions::new(); + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + if let Some(aaid) = get_field::<_, _, Option>(env, &opts, "aaid")? { + new_opts = new_opts.aaid(aaid.into()); + } + if let Some(start) = get_field::<_, _, Option>(env, &opts, "start")? { + new_opts = new_opts.start(start); + } + if let Some(end) = get_field::<_, _, Option>(env, &opts, "end")? { + new_opts = new_opts.end(end); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.position(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPositionPerformance( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.position_performance(counter_id).await?, + )) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPositionProfits( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = if !opts.is_null() { + let mut new_opts = GetFundPositionProfitsOptions::new(); + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + if let Some(aaid) = get_field::<_, _, Option>(env, &opts, "aaid")? { + new_opts = new_opts.aaid(aaid.into()); + } + if let Some(start) = get_field::<_, _, Option>(env, &opts, "start")? { + new_opts = new_opts.start(start); + } + if let Some(end) = get_field::<_, _, Option>(env, &opts, "end")? { + new_opts = new_opts.end(end); + } + if let Some(page) = get_field::<_, _, Option>(env, &opts, "page")? { + new_opts = new_opts.page(page.into()); + } + if let Some(size) = get_field::<_, _, Option>(env, &opts, "size")? { + new_opts = new_opts.size(size.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.position_profits(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPositionNav( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = read_nav_range_opts(env, &opts)?; + async_util::execute(env, callback, async move { + Ok(ObjectArray( + __owned_ctx.position_nav(counter_id, opts).await?, + )) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextPositionDividends( + mut env: JNIEnv, + _class: JClass, + context: i64, + counter_id: JString, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = FromJValue::from_jvalue(env, counter_id.into())?; + let opts = if !opts.is_null() { + let mut new_opts = GetFundPositionDividendsOptions::new(); + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + if let Some(aaid) = get_field::<_, _, Option>(env, &opts, "aaid")? { + new_opts = new_opts.aaid(aaid.into()); + } + if let Some(currency) = get_field::<_, _, Option>(env, &opts, "currency")? { + new_opts = new_opts.currency(currency); + } + if let Some(start) = get_field::<_, _, Option>(env, &opts, "start")? { + new_opts = new_opts.start(start.into()); + } + if let Some(end) = get_field::<_, _, Option>(env, &opts, "end")? { + new_opts = new_opts.end(end.into()); + } + if let Some(page) = get_field::<_, _, Option>(env, &opts, "page")? { + new_opts = new_opts.page(page.into()); + } + if let Some(size) = get_field::<_, _, Option>(env, &opts, "size")? { + new_opts = new_opts.size(size.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(__owned_ctx.position_dividends(counter_id, opts).await?) + })?; + Ok(()) + }) +} + +// ----- fund orders & trading ----- + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextOrders( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let opts = if !opts.is_null() { + let mut new_opts = GetFundOrdersOptions::new(); + let counter_ids: ObjectArray = get_field(env, &opts, "counterIds")?; + if !counter_ids.0.is_empty() { + new_opts = new_opts.counter_ids(counter_ids.0); + } + if let Some(actions) = get_field::<_, _, Option>(env, &opts, "actions")? { + new_opts = new_opts.actions(actions); + } + if let Some(states) = get_field::<_, _, Option>(env, &opts, "states")? { + new_opts = new_opts.states(states); + } + if let Some(currency) = get_field::<_, _, Option>(env, &opts, "currency")? { + new_opts = new_opts.currency(currency); + } + if let Some(start) = get_field::<_, _, Option>(env, &opts, "start")? { + new_opts = new_opts.start(start.into()); + } + if let Some(end) = get_field::<_, _, Option>(env, &opts, "end")? { + new_opts = new_opts.end(end.into()); + } + if let Some(page) = get_field::<_, _, Option>(env, &opts, "page")? { + new_opts = new_opts.page(page.into()); + } + if let Some(size) = get_field::<_, _, Option>(env, &opts, "size")? { + new_opts = new_opts.size(size.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.orders(opts).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextOrder( + mut env: JNIEnv, + _class: JClass, + context: i64, + order_id: i64, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + async_util::execute(env, callback, async move { + Ok(__owned_ctx.order(order_id).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextTransactions( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let opts = if !opts.is_null() { + let mut new_opts = GetFundTransactionsOptions::new(); + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + if let Some(business_type) = + get_field::<_, _, Option>(env, &opts, "businessType")? + { + new_opts = new_opts.business_type(business_type); + } + if let Some(category) = get_field::<_, _, Option>(env, &opts, "category")? { + new_opts = new_opts.category(category); + } + if let Some(currencies) = get_field::<_, _, Option>(env, &opts, "currencies")? { + new_opts = new_opts.currencies(currencies); + } + if let Some(start) = get_field::<_, _, Option>(env, &opts, "start")? { + new_opts = new_opts.start(start.into()); + } + if let Some(end) = get_field::<_, _, Option>(env, &opts, "end")? { + new_opts = new_opts.end(end.into()); + } + if let Some(page) = get_field::<_, _, Option>(env, &opts, "page")? { + new_opts = new_opts.page(page.into()); + } + if let Some(size) = get_field::<_, _, Option>(env, &opts, "size")? { + new_opts = new_opts.size(size.into()); + } + Some(new_opts) + } else { + None + }; + async_util::execute(env, callback, async move { + Ok(ObjectArray(__owned_ctx.transactions(opts).await?)) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextValidateOrder( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = get_field(env, &opts, "counterId")?; + let action: String = get_field(env, &opts, "action")?; + let currency: String = get_field(env, &opts, "currency")?; + let mut new_opts = ValidateFundOrderOptions::new(counter_id, action, currency); + if let Some(amount) = get_field::<_, _, Option>(env, &opts, "amount")? { + new_opts = new_opts.amount(amount); + } + if let Some(units) = get_field::<_, _, Option>(env, &opts, "units")? { + new_opts = new_opts.units(units); + } + if let Some(dividend_option) = + get_field::<_, _, Option>(env, &opts, "dividendOption")? + { + new_opts = new_opts.dividend_option(dividend_option.into()); + } + if let Some(fund_source) = get_field::<_, _, Option>(env, &opts, "fundSource")? + { + new_opts = new_opts.fund_source(fund_source.into()); + } + if let Some(account_channel) = + get_field::<_, _, Option>(env, &opts, "accountChannel")? + { + new_opts = new_opts.account_channel(account_channel); + } + async_util::execute(env, callback, async move { + Ok(__owned_ctx.validate_order(new_opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextSubmitOrder( + mut env: JNIEnv, + _class: JClass, + context: i64, + opts: JObject, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + let counter_id: String = get_field(env, &opts, "counterId")?; + let action: String = get_field(env, &opts, "action")?; + let currency: String = get_field(env, &opts, "currency")?; + let mut new_opts = SubmitFundOrderOptions::new(counter_id, action, currency); + if let Some(amount) = get_field::<_, _, Option>(env, &opts, "amount")? { + new_opts = new_opts.amount(amount); + } + if let Some(units) = get_field::<_, _, Option>(env, &opts, "units")? { + new_opts = new_opts.units(units); + } + if let Some(dividend_option) = + get_field::<_, _, Option>(env, &opts, "dividendOption")? + { + new_opts = new_opts.dividend_option(dividend_option.into()); + } + if let Some(fee) = get_field::<_, _, Option>(env, &opts, "fee")? { + new_opts = new_opts.fee(fee); + } + if let Some(is_sell_all) = read_opt_bool(env, &opts, "isSellAll")? { + new_opts = new_opts.is_sell_all(is_sell_all); + } + if let Some(remark) = get_field::<_, _, Option>(env, &opts, "remark")? { + new_opts = new_opts.remark(remark); + } + if let Some(trade_method) = + get_field::<_, _, Option>(env, &opts, "tradeMethod")? + { + new_opts = new_opts.trade_method(trade_method.into()); + } + async_util::execute(env, callback, async move { + Ok(__owned_ctx.submit_order(new_opts).await?) + })?; + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundContextCancelOrder( + mut env: JNIEnv, + _class: JClass, + context: i64, + order_id: i64, + callback: JObject, +) { + jni_result(&mut env, (), |env| { + let context = &*(context as *const ContextObj); + let __owned_ctx = context.ctx.clone(); + async_util::execute(env, callback, async move { + Ok(__owned_ctx.cancel_order(order_id).await?) + })?; + Ok(()) + }) +} diff --git a/java/src/init.rs b/java/src/init.rs index 832c27625..855aa87c0 100644 --- a/java/src/init.rs +++ b/java/src/init.rs @@ -228,6 +228,46 @@ pub extern "system" fn Java_com_longbridge_SdkNative_init<'a>( longbridge::trade::PushGridOrderChanged, crate::types::GridOrdersResponse, crate::types::GridTriggerHistoryResponse, + // FundContext (types live in `longbridge::fund`) + longbridge::fund::FundNavValue, + longbridge::fund::FundPerformancePoint, + longbridge::fund::HotFund, + longbridge::fund::FundBrief, + longbridge::fund::FundFilters, + longbridge::fund::FundAssetAllocationItem, + longbridge::fund::FundAssetAllocation, + longbridge::fund::FundDetail, + longbridge::fund::FundAnalysis, + longbridge::fund::FundAnalysisDetail, + longbridge::fund::FundTrendContrast, + longbridge::fund::FundTrend, + longbridge::fund::FundNamedContrast, + longbridge::fund::FundPerformanceComparison, + longbridge::fund::FundAnnualReturn, + longbridge::fund::FundQuarterlyReturn, + longbridge::fund::FundPerformance, + longbridge::fund::FundHolding, + longbridge::fund::FundHoldings, + longbridge::fund::FundStockHolding, + longbridge::fund::FundPosition, + longbridge::fund::FundPositions, + longbridge::fund::FundDatedValue, + longbridge::fund::FundUnitValue, + longbridge::fund::FundPositionDetailValues, + longbridge::fund::FundPositionDetail, + longbridge::fund::FundPositionPerformance, + longbridge::fund::FundPositionProfits, + longbridge::fund::FundPositionNav, + longbridge::fund::FundDividend, + longbridge::fund::FundDividends, + longbridge::fund::FundOrder, + longbridge::fund::FundOrderKeyword, + longbridge::fund::FundOrderStage, + longbridge::fund::FundOrderInfo, + longbridge::fund::FundOrderDetail, + longbridge::fund::FundTransaction, + longbridge::fund::FundOrderValidation, + longbridge::fund::FundOrderSubmitResponse, longbridge::content::TopicItem, longbridge::content::NewsItem, longbridge::content::TopicAuthor, diff --git a/java/src/lib.rs b/java/src/lib.rs index dd0fc9ec5..b2d8f77da 100644 --- a/java/src/lib.rs +++ b/java/src/lib.rs @@ -10,6 +10,7 @@ mod config; mod content_context; mod dca_context; mod error; +mod fund_context; mod fundamental_context; mod grid_context; mod http_client; diff --git a/java/src/types/classes.rs b/java/src/types/classes.rs index fd9453a64..0128dbd63 100644 --- a/java/src/types/classes.rs +++ b/java/src/types/classes.rs @@ -829,7 +829,7 @@ impl_java_class!( "com/longbridge/trade/FundPosition", longbridge::trade::FundPosition, [ - symbol, + counter_id, current_net_asset_value, net_asset_value_day, symbol_name, @@ -3830,3 +3830,623 @@ impl crate::types::IntoJValue for longbridge::agent::ConversationStreamEvent { } } } + +// ── Fund (mutual fund) types ─────────────────────────────────────── + +impl_java_class!( + "com/longbridge/fund/FundNavValue", + longbridge::fund::FundNavValue, + [ + change, + change_percent, + change_percent_format, + counter_id, + counter_name, + currency, + date_format, + isin, + last_update_time, + value, + value_format + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPerformancePoint", + longbridge::fund::FundPerformancePoint, + [date, last_done] +); + +impl_java_class!( + "com/longbridge/fund/HotFund", + longbridge::fund::HotFund, + [ + asset_class, + asset_class_name, + counter_id, + currency, + earning_rate, + #[java(objarray)] + fund_performances, + name, + purchase_amount, + recommendation_text, + risk_level, + risk_level_name, + time_interval + ] +); + +impl_java_class!( + "com/longbridge/fund/FundBrief", + longbridge::fund::FundBrief, + [ + asset_class, + asset_class_name, + code, + counter_id, + currency, + description, + earning_rate, + holding, + isin, + name, + product, + purchase_amount, + recommendation_text, + risk_level, + risk_level_name, + time_interval, + unit_value + ] +); + +impl_java_class!( + "com/longbridge/fund/FundFilters", + longbridge::fund::FundFilters, + [ + #[java(objarray)] + asset_class, + #[java(objarray)] + company, + #[java(objarray)] + currency, + #[java(objarray)] + industry_category_name, + #[java(objarray)] + risk_level + ] +); + +impl_java_class!( + "com/longbridge/fund/FundAssetAllocationItem", + longbridge::fund::FundAssetAllocationItem, + [code, counter_id, name, position_ratio] +); + +impl_java_class!( + "com/longbridge/fund/FundAssetAllocation", + longbridge::fund::FundAssetAllocation, + [ + asset_type, + #[java(objarray)] + lists, + report_date + ] +); + +impl_java_class!( + "com/longbridge/fund/FundDetail", + longbridge::fund::FundDetail, + [ + additional_purchase_amount, + affirm_day, + amount_affirm_day, + asset_allocation, + asset_class, + asset_class_name, + bill_purchase_rate, + channel, + close_period, + code, + currency, + cut_off_time, + derivatives, + done_day, + excess_return_fee, + gst_rate, + introduce, + is_cash_plus, + is_complex, + is_new_cash_plus, + is_yinghebao, + isin, + manage_rate, + manager, + min_hold_cash, + min_hold_share, + min_sell_share, + month_raise_day, + name, + nav_deadline, + no_load, + open_date, + open_period, + product, + product_information_locals, + profile, + purchasable, + purchase_affirm_day, + purchase_amount, + purchase_rate, + rating, + redeemable, + redemption_advance_day, + redemption_amount, + redemption_close_period_shows, + redemption_done_day, + redemption_open_day_shows, + risk_level, + risk_level_name, + verify_status, + virtual_currency, + year_to_date_yield, + ytd_yield_type + ] +); + +impl_java_class!( + "com/longbridge/fund/FundAnalysis", + longbridge::fund::FundAnalysis, + [ + actual_period, + cost_level, + return_ability, + risk_ability, + updated_at, + value_for_money, + visible + ] +); + +impl_java_class!( + "com/longbridge/fund/FundAnalysisDetail", + longbridge::fund::FundAnalysisDetail, + [ + actual_period, + #[java(priarray)] + available_periods, + cost_level, + return_ability, + risk_ability, + updated_at, + value_for_money, + visible + ] +); + +impl_java_class!( + "com/longbridge/fund/FundTrendContrast", + longbridge::fund::FundTrendContrast, + [ + benchmark_name, + #[java(objarray)] + performances + ] +); + +impl_java_class!( + "com/longbridge/fund/FundTrend", + longbridge::fund::FundTrend, + [ + actual_period, + #[java(priarray)] + available_periods, + #[java(objarray)] + category_average_performances, + contrast_performances, + #[java(objarray)] + fund_performances + ] +); + +impl_java_class!( + "com/longbridge/fund/FundNamedContrast", + longbridge::fund::FundNamedContrast, + [ + name, + #[java(objarray)] + performances + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPerformanceComparison", + longbridge::fund::FundPerformanceComparison, + [ + #[java(objarray)] + contrast_performances, + #[java(objarray)] + fund_performances + ] +); + +impl_java_class!( + "com/longbridge/fund/FundAnnualReturn", + longbridge::fund::FundAnnualReturn, + [change_percent, year] +); + +impl_java_class!( + "com/longbridge/fund/FundQuarterlyReturn", + longbridge::fund::FundQuarterlyReturn, + [change_percent, quarter, year] +); + +impl_java_class!( + "com/longbridge/fund/FundPerformance", + longbridge::fund::FundPerformance, + [ + annualized_return_five, + annualized_return_one, + annualized_return_ten, + annualized_return_three, + annualized_return_two, + counter_id, + fund_name, + performance_rank_five_years, + performance_rank_one_day, + performance_rank_one_month, + performance_rank_one_week, + performance_rank_one_year, + performance_rank_six_months, + performance_rank_ten_years, + performance_rank_three_months, + performance_rank_three_years, + performance_rank_two_years, + performance_rank_ytd, + performance_return_five_years, + performance_return_one_day, + performance_return_one_month, + performance_return_one_week, + performance_return_one_year, + performance_return_six_months, + performance_return_ten_years, + performance_return_three_months, + performance_return_three_years, + performance_return_two_years, + performance_return_ytd, + performance_total_five_years, + performance_total_one_day, + performance_total_one_month, + performance_total_one_week, + performance_total_one_year, + performance_total_six_months, + performance_total_ten_years, + performance_total_three_months, + performance_total_three_years, + performance_total_two_years, + performance_total_ytd, + seven_days_annualized, + ten_thousand_price, + update_time + ] +); + +impl_java_class!( + "com/longbridge/fund/FundHolding", + longbridge::fund::FundHolding, + [ + bond_type, + bond_type_name, + country_name, + holding_type, + industry_name, + market_value, + maturity_date, + name, + share_change, + share_change_percent, + shares, + weighting + ] +); + +impl_java_class!( + "com/longbridge/fund/FundHoldings", + longbridge::fund::FundHoldings, + [ + #[java(objarray)] + holdings, + report_date, + weighting + ] +); + +impl_java_class!( + "com/longbridge/fund/FundStockHolding", + longbridge::fund::FundStockHolding, + [ + code, + counter_id, + currency, + name, + position_ratio, + report_date + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPosition", + longbridge::fund::FundPosition, + [ + amount, + counter_id, + currency, + freeze_units, + holding_profit, + holding_units, + name, + recent_profit, + recent_trading_day, + sum_recent_profit + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPositions", + longbridge::fund::FundPositions, + [ + account_channel, + #[java(objarray)] + list, + pending_buy_orders, + recent_trading_day, + sold_pending_credit_orders + ] +); + +impl_java_class!( + "com/longbridge/fund/FundDatedValue", + longbridge::fund::FundDatedValue, + [date, value] +); + +impl_java_class!( + "com/longbridge/fund/FundUnitValue", + longbridge::fund::FundUnitValue, + [date, day_increase_rate, total_value, unit_value] +); + +impl_java_class!( + "com/longbridge/fund/FundPositionDetailValues", + longbridge::fund::FundPositionDetailValues, + [ + amount, + currency, + holding_cost, + holding_profit, + holding_profit_rate, + holding_units, + holding_value, + pending_buy_value, + pending_sell_value, + profit_amount_accum_td, + profit_amount_accum_td_rate, + recent_profit, + recent_tradingday, + recent_unit_value, + sold_pending_confirm_units + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPositionDetail", + longbridge::fund::FundPositionDetail, + [ + detail_values, + #[java(objarray)] + sum_profit, + #[java(objarray)] + ut_value + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPositionPerformance", + longbridge::fund::FundPositionPerformance, + [ + annualized_return_five, + annualized_return_one, + annualized_return_ten, + annualized_return_three, + annualized_return_two, + counter_id, + fund_name, + performance_return_five_years, + performance_return_one_day, + performance_return_one_month, + performance_return_one_week, + performance_return_one_year, + performance_return_six_months, + performance_return_ten_years, + performance_return_three_months, + performance_return_three_years, + performance_return_two_years, + performance_return_ytd, + update_time + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPositionProfits", + longbridge::fund::FundPositionProfits, + [ + currency, + #[java(objarray)] + history_value, + last_update_time, + sum_profit + ] +); + +impl_java_class!( + "com/longbridge/fund/FundPositionNav", + longbridge::fund::FundPositionNav, + [ + change, + change_percent, + counter_id, + counter_name, + last_update_time, + value + ] +); + +impl_java_class!( + "com/longbridge/fund/FundDividend", + longbridge::fund::FundDividend, + [amount, counter_id, currency, date, div_method, name] +); + +impl_java_class!( + "com/longbridge/fund/FundDividends", + longbridge::fund::FundDividends, + [ + currency, + #[java(objarray)] + div_cash_infos, + lastest_date, + total_div_cash + ] +); + +impl_java_class!( + "com/longbridge/fund/FundOrder", + longbridge::fund::FundOrder, + [ + action, + amount, + counter_id, + created_at, + currency, + fund_name, + id, + is_auto, + net_worth, + product_type, + state, + state_desc, + units + ] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderKeyword", + longbridge::fund::FundOrderKeyword, + [content, group, key, line_strategy, title] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderStage", + longbridge::fund::FundOrderStage, + [desc, key, link, link_text, progress, stage] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderInfo", + longbridge::fund::FundOrderInfo, + [ + aaid, + account_channel, + action, + amount, + channel, + counter_id, + created_at, + currency, + dividend_option, + eq_at, + fee, + fund_name, + fund_source, + histories, + id, + message, + net_worth, + price_at, + processed_at, + product_type, + repurchaseable, + sale_proceeds, + sales_charge, + sales_price, + sales_unit, + state, + state_desc, + status, + status_ex, + t_description, + time_partition, + total_amount, + transaction_at, + units, + withdraw_at, + withdrawable + ] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderDetail", + longbridge::fund::FundOrderDetail, + [ + #[java(objarray)] + keywords, + order, + #[java(objarray)] + stages + ] +); + +impl_java_class!( + "com/longbridge/fund/FundTransaction", + longbridge::fund::FundTransaction, + [ + amount, + category, + created_at, + currency, + description, + detail_created_at, + detail_type, + done_at, + quantity_description, + redirect_page, + redirect_page_v2, + ref_no, + stock_quantity, + tx_type, + type_name + ] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderValidation", + longbridge::fund::FundOrderValidation, + [ + auth_token, + eval_address, + fund_risk_level, + msg, + user_pi, + user_risk_level + ] +); + +impl_java_class!( + "com/longbridge/fund/FundOrderSubmitResponse", + longbridge::fund::FundOrderSubmitResponse, + [ + action, amount, counter_id, created_at, fund_name, id, msg, status, units + ] +); diff --git a/nodejs/index.d.ts b/nodejs/index.d.ts index 9dbf27e98..5ef828a38 100644 --- a/nodejs/index.d.ts +++ b/nodejs/index.d.ts @@ -822,12 +822,798 @@ export declare class FundamentalContext { usEtfFiles(symbol: string, size?: number | undefined | null): Promise } +/** + * Fund analysis (level 1). The ability/cost breakdowns are server-defined + * structures exposed as raw JSON strings. + */ +export declare class FundAnalysis { + toString(): string + toJSON(): any + /** Actual period */ + get actualPeriod(): number + /** Cost level (raw JSON string) */ + get costLevel(): string + /** Return ability (raw JSON string) */ + get returnAbility(): string + /** Risk ability (raw JSON string) */ + get riskAbility(): string + /** Updated at */ + get updatedAt(): string + /** Value for money (raw JSON string) */ + get valueForMoney(): string + /** Whether visible */ + get visible(): boolean +} + +/** + * Fund analysis detail (level 2). The ability/cost breakdowns are + * server-defined structures exposed as raw JSON strings. + */ +export declare class FundAnalysisDetail { + toString(): string + toJSON(): any + /** Actual period */ + get actualPeriod(): number + /** Available periods */ + get availablePeriods(): Array + /** Cost level (raw JSON string) */ + get costLevel(): string + /** Return ability (raw JSON string) */ + get returnAbility(): string + /** Risk ability (raw JSON string) */ + get riskAbility(): string + /** Updated at */ + get updatedAt(): string + /** Value for money (raw JSON string) */ + get valueForMoney(): string + /** Whether visible */ + get visible(): boolean +} + +/** A fund annual return entry. */ +export declare class FundAnnualReturn { + toString(): string + toJSON(): any + /** Change percent */ + get changePercent(): string + /** Year */ + get year(): number +} + +/** A fund's asset allocation. */ +export declare class FundAssetAllocation { + toString(): string + toJSON(): any + /** Asset type */ + get assetType(): number + /** Allocation entries */ + get lists(): Array + /** Report date */ + get reportDate(): string +} + +/** A single holding entry inside a fund's asset allocation. */ +export declare class FundAssetAllocationItem { + toString(): string + toJSON(): any + /** Security code */ + get code(): string + /** Security counter id */ + get counterId(): string + /** Name */ + get name(): string + /** Position ratio */ + get positionRatio(): string +} + +/** A fund entry in the fund list. */ +export declare class FundBrief { + toString(): string + toJSON(): any + /** Asset class */ + get assetClass(): number + /** Asset class name */ + get assetClassName(): string + /** Fund code */ + get code(): string + /** Fund counter id */ + get counterId(): string + /** Currency */ + get currency(): string + /** Description */ + get description(): string + /** Earning rate */ + get earningRate(): string + /** Whether the user is holding this fund */ + get holding(): boolean + /** ISIN */ + get isin(): string + /** Fund name */ + get name(): string + /** Product */ + get product(): string + /** Minimum purchase amount */ + get purchaseAmount(): string + /** Recommendation text */ + get recommendationText(): string + /** Risk level */ + get riskLevel(): number + /** Risk level name */ + get riskLevelName(): string + /** Time interval of the earning rate */ + get timeInterval(): string + /** Unit value */ + get unitValue(): string +} + +/** Fund (mutual fund) channel context. */ +export declare class FundContext { + /** Create a new `FundContext`. */ + static new(config: Config): FundContext + /** Get the hot-selling fund list. */ + hotFunds(): Promise> + /** Get the fund list. */ + funds(opts?: GetFundsOptions | undefined | null): Promise> + /** Get the fund list filter options. */ + filters(): Promise + /** Get fund detail. */ + detail(counterId: string): Promise + /** Get fund analysis (level 1). */ + analysis(counterId: string, opts?: GetFundAnalysisOptions | undefined | null): Promise + /** Get fund analysis detail (level 2). */ + analysisDetail(counterId: string, opts?: GetFundAnalysisOptions | undefined | null): Promise + /** Get fund trend chart. */ + trend(counterId: string, opts?: GetFundAnalysisOptions | undefined | null): Promise + /** Get fund annual returns. */ + annualReturns(counterId: string, opts?: FundPageOptions | undefined | null): Promise> + /** Get fund quarterly returns. */ + quarterlyReturns(counterId: string, opts?: FundPageOptions | undefined | null): Promise> + /** Get fund performance figures. */ + performance(counterId: string): Promise> + /** Get fund performance comparison. */ + performanceComparison(counterId: string, opts?: GetFundAnalysisOptions | undefined | null): Promise + /** Get fund latest net value. */ + nav(counterId: string): Promise> + /** Get fund historical net value (paged). */ + navHistory(counterId: string, opts?: FundPageOptions | undefined | null): Promise> + /** Get fund historical net value by relative time range. */ + navRange(counterId: string, opts?: FundNavRangeOptions | undefined | null): Promise> + /** Get a fund's top-10 holdings. */ + holdings(counterId: string, opts?: GetFundHoldingsOptions | undefined | null): Promise + /** Get the stocks held by a fund (reverse lookup). */ + stockHoldings(counterId: string, opts?: GetFundStockHoldingsOptions | undefined | null): Promise> + /** Get the user's fund positions overview. */ + positions(opts?: GetFundPositionsOptions | undefined | null): Promise + /** Get the user's single fund position detail. */ + position(counterId: string, opts?: GetFundPositionOptions | undefined | null): Promise + /** Get the performance figures of a held fund. */ + positionPerformance(counterId: string): Promise> + /** Get the cumulative-profit series of a held fund. */ + positionProfits(counterId: string, opts?: GetFundPositionProfitsOptions | undefined | null): Promise + /** Get the net-value history of a held fund. */ + positionNav(counterId: string, opts?: FundNavRangeOptions | undefined | null): Promise> + /** Get the dividend records of a held fund. */ + positionDividends(counterId: string, opts?: GetFundPositionDividendsOptions | undefined | null): Promise + /** Get the user's fund orders (also serves as the trade/execution record). */ + orders(opts?: GetFundOrdersOptions | undefined | null): Promise> + /** Get a fund order detail. */ + order(orderId: number): Promise + /** Get the user's fund transactions (cash-flow records). */ + transactions(opts?: GetFundTransactionsOptions | undefined | null): Promise> + /** Validate a fund order before submitting. */ + validateOrder(opts: ValidateFundOrderOptions): Promise + /** Submit a fund order (buy / sell). */ + submitOrder(opts: SubmitFundOrderOptions): Promise + /** Cancel (withdraw) a fund order. */ + cancelOrder(orderId: number): Promise +} + +/** A dated value point. */ +export declare class FundDatedValue { + toString(): string + toJSON(): any + /** Date (unix seconds) */ + get date(): number + /** Value */ + get value(): string +} + +/** Fund detail. */ +export declare class FundDetail { + toString(): string + toJSON(): any + /** Additional purchase amount */ + get additionalPurchaseAmount(): string + /** Affirm day */ + get affirmDay(): number + /** Amount affirm day */ + get amountAffirmDay(): string + /** Asset allocation */ + get assetAllocation(): FundAssetAllocation | null + /** Asset class */ + get assetClass(): number + /** Asset class name */ + get assetClassName(): string + /** Bill purchase rate */ + get billPurchaseRate(): string + /** Channel */ + get channel(): string + /** Close period */ + get closePeriod(): string + /** Fund code */ + get code(): string + /** Currency */ + get currency(): string + /** Cut off time */ + get cutOffTime(): string + /** Whether it is a derivative */ + get derivatives(): boolean + /** Done day */ + get doneDay(): number + /** Excess return fee */ + get excessReturnFee(): string + /** GST rate */ + get gstRate(): string + /** Introduction */ + get introduce(): string + /** Whether it is a cash-plus fund */ + get isCashPlus(): boolean + /** Whether it is a complex product */ + get isComplex(): boolean + /** Whether it is a new cash-plus fund */ + get isNewCashPlus(): boolean + /** Whether it is a Yinghebao fund */ + get isYinghebao(): boolean + /** ISIN */ + get isin(): string + /** Management rate */ + get manageRate(): string + /** Manager */ + get manager(): string + /** Minimum holding cash */ + get minHoldCash(): string + /** Minimum holding share */ + get minHoldShare(): string + /** Minimum sell share */ + get minSellShare(): string + /** Month raise day */ + get monthRaiseDay(): string + /** Fund name */ + get name(): string + /** Net value deadline */ + get navDeadline(): string + /** Whether it is no-load */ + get noLoad(): boolean + /** Open date */ + get openDate(): string + /** Open period */ + get openPeriod(): string + /** Product */ + get product(): string + /** Product information locals */ + get productInformationLocals(): string + /** Profile */ + get profile(): string + /** Whether purchasable */ + get purchasable(): number + /** Purchase affirm day */ + get purchaseAffirmDay(): string + /** Minimum purchase amount */ + get purchaseAmount(): string + /** Purchase rate */ + get purchaseRate(): string + /** Rating */ + get rating(): number + /** Whether redeemable */ + get redeemable(): number + /** Redemption advance day */ + get redemptionAdvanceDay(): string + /** Redemption amount */ + get redemptionAmount(): string + /** Redemption close period text */ + get redemptionClosePeriodShows(): string + /** Redemption done day */ + get redemptionDoneDay(): string + /** Redemption open day text */ + get redemptionOpenDayShows(): string + /** Risk level */ + get riskLevel(): number + /** Risk level name */ + get riskLevelName(): string + /** Verify status */ + get verifyStatus(): number + /** Whether it is a virtual currency fund */ + get virtualCurrency(): boolean + /** Year to date yield */ + get yearToDateYield(): string + /** Year to date yield type */ + get ytdYieldType(): number +} + +/** A cash dividend record for a held fund. */ +export declare class FundDividend { + toString(): string + toJSON(): any + /** Amount */ + get amount(): string + /** Fund counter id */ + get counterId(): string + /** Currency */ + get currency(): string + /** Date (unix seconds) */ + get date(): number + /** Dividend method */ + get divMethod(): string + /** Fund name */ + get name(): string +} + +/** The user's dividend records for a held fund. */ +export declare class FundDividends { + toString(): string + toJSON(): any + /** Currency */ + get currency(): string + /** Dividend records */ + get divCashInfos(): Array + /** Latest dividend date (unix seconds) */ + get lastestDate(): number + /** Total cash dividend */ + get totalDivCash(): string +} + +/** + * Fund list filter options. Each list holds server-defined option objects + * serialized as raw JSON strings. + */ +export declare class FundFilters { + toString(): string + toJSON(): any + /** Asset class options (each a raw JSON string) */ + get assetClass(): Array + /** Company options (each a raw JSON string) */ + get company(): Array + /** Currency options (each a raw JSON string) */ + get currency(): Array + /** Industry category options (each a raw JSON string) */ + get industryCategoryName(): Array + /** Risk level options (each a raw JSON string) */ + get riskLevel(): Array +} + +/** A single fund holding (top-10 holdings). */ +export declare class FundHolding { + toString(): string + toJSON(): any + /** Bond type */ + get bondType(): string + /** Bond type name */ + get bondTypeName(): string + /** Country name */ + get countryName(): string + /** Holding type */ + get holdingType(): string + /** Industry name */ + get industryName(): string + /** Market value */ + get marketValue(): string + /** Maturity date */ + get maturityDate(): string + /** Name */ + get name(): string + /** Share change */ + get shareChange(): string + /** Share change percent */ + get shareChangePercent(): string + /** Shares */ + get shares(): string + /** Weighting */ + get weighting(): string +} + +/** + * A single fund position held by the user. + * + * Named `FundHoldingPosition` to avoid a name clash with the trade channel's + * existing `FundPosition` class. + */ +export declare class FundHoldingPosition { + toString(): string + toJSON(): any + /** Holding amount */ + get amount(): string + /** Fund counter id */ + get counterId(): string + /** Currency */ + get currency(): string + /** Frozen units */ + get freezeUnits(): string + /** Holding profit */ + get holdingProfit(): string + /** Holding units */ + get holdingUnits(): string + /** Fund name */ + get name(): string + /** Recent profit */ + get recentProfit(): string + /** Recent trading day (unix seconds) */ + get recentTradingDay(): number + /** Accumulated recent profit */ + get sumRecentProfit(): string +} + +/** A fund's top-10 holdings. */ +export declare class FundHoldings { + toString(): string + toJSON(): any + /** Holding entries */ + get holdings(): Array + /** Report date */ + get reportDate(): string + /** Total weighting */ + get weighting(): string +} + +/** + * A named contrast performance series. The performance points are + * server-defined structures exposed as raw JSON strings. + */ +export declare class FundNamedContrast { + toString(): string + toJSON(): any + /** Series name */ + get name(): string + /** Performance points (each a raw JSON string) */ + get performances(): Array +} + +/** A fund net-asset-value data point (latest / historical). */ +export declare class FundNavValue { + toString(): string + toJSON(): any + /** Net value change */ + get change(): string + /** Net value change percent */ + get changePercent(): string + /** Formatted change percent */ + get changePercentFormat(): string + /** Fund counter id */ + get counterId(): string + /** Fund name */ + get counterName(): string + /** Currency */ + get currency(): string + /** Formatted date */ + get dateFormat(): string + /** ISIN */ + get isin(): string + /** Last update time (unix seconds) */ + get lastUpdateTime(): number + /** Net value */ + get value(): string + /** Formatted net value */ + get valueFormat(): string +} + +/** A fund order (list view). */ +export declare class FundOrder { + toString(): string + toJSON(): any + /** Action (buy/sell) */ + get action(): string + /** Amount */ + get amount(): string + /** Fund counter id */ + get counterId(): string + /** Created at (unix seconds) */ + get createdAt(): number + /** Currency */ + get currency(): string + /** Fund name */ + get fundName(): string + /** Order id */ + get id(): number + /** Whether it is an auto (DCA) order */ + get isAuto(): boolean + /** Net worth */ + get netWorth(): string + /** Product type */ + get productType(): string + /** State */ + get state(): string + /** State description */ + get stateDesc(): string + /** Units */ + get units(): string +} + +/** Fund order detail. */ +export declare class FundOrderDetail { + toString(): string + toJSON(): any + /** Keyword blocks */ + get keywords(): Array + /** The order */ + get order(): FundOrderInfo | null + /** Processing stages */ + get stages(): Array +} + +/** The full information of a fund order. */ +export declare class FundOrderInfo { + toString(): string + toJSON(): any + /** Account id */ + get aaid(): number + /** Account channel */ + get accountChannel(): string + /** Action (buy/sell) */ + get action(): string + /** Amount */ + get amount(): string + /** Channel */ + get channel(): string + /** Fund counter id */ + get counterId(): string + /** Created at (unix seconds) */ + get createdAt(): number + /** Currency */ + get currency(): string + /** Dividend option */ + get dividendOption(): string + /** Equity time (unix seconds) */ + get eqAt(): number + /** Fee */ + get fee(): string + /** Fund name */ + get fundName(): string + /** Fund source */ + get fundSource(): string + /** Histories */ + get histories(): string + /** Order id */ + get id(): number + /** Message */ + get message(): string + /** Net worth */ + get netWorth(): string + /** Price time (unix seconds) */ + get priceAt(): number + /** Processed at (unix seconds) */ + get processedAt(): number + /** Product type */ + get productType(): string + /** Whether repurchaseable */ + get repurchaseable(): boolean + /** Sale proceeds */ + get saleProceeds(): string + /** Sales charge */ + get salesCharge(): string + /** Sales price */ + get salesPrice(): string + /** Sales unit */ + get salesUnit(): string + /** State */ + get state(): string + /** State description */ + get stateDesc(): string + /** Status */ + get status(): number + /** Extended status */ + get statusEx(): number + /** T+ description */ + get tDescription(): string + /** Time partition */ + get timePartition(): string + /** Total amount */ + get totalAmount(): string + /** Transaction at (unix seconds) */ + get transactionAt(): number + /** Units */ + get units(): string + /** Withdraw at (unix seconds) */ + get withdrawAt(): number + /** Whether withdrawable */ + get withdrawable(): boolean +} + +/** A keyword block in a fund order detail. */ +export declare class FundOrderKeyword { + toString(): string + toJSON(): any + /** Content */ + get content(): string + /** Group */ + get group(): string + /** Key */ + get key(): string + /** Line strategy */ + get lineStrategy(): string + /** Title */ + get title(): string +} + +/** A processing stage in a fund order detail. */ +export declare class FundOrderStage { + toString(): string + toJSON(): any + /** Description */ + get desc(): string + /** Key */ + get key(): string + /** Link */ + get link(): string + /** Link text */ + get linkText(): string + /** Progress */ + get progress(): string + /** Stage */ + get stage(): string +} + +/** The result of submitting a fund order. */ +export declare class FundOrderSubmitResponse { + toString(): string + toJSON(): any + /** Action (buy/sell) */ + get action(): string + /** Amount */ + get amount(): string + /** Fund counter id */ + get counterId(): string + /** Created at (unix seconds) */ + get createdAt(): number + /** Fund name */ + get fundName(): string + /** Order id */ + get id(): number + /** Message */ + get msg(): string + /** Status */ + get status(): number + /** Units */ + get units(): string +} + +/** The result of validating a fund order. */ +export declare class FundOrderValidation { + toString(): string + toJSON(): any + /** Auth token to carry into submit */ + get authToken(): string + /** Risk-assessment eval address */ + get evalAddress(): string + /** Fund risk level */ + get fundRiskLevel(): number + /** Message */ + get msg(): string + /** User PI status */ + get userPi(): number + /** User risk level */ + get userRiskLevel(): number +} + +/** A fund's detailed performance figures. */ +export declare class FundPerformance { + toString(): string + toJSON(): any + /** Annualized return (5y) */ + get annualizedReturnFive(): string + /** Annualized return (1y) */ + get annualizedReturnOne(): string + /** Annualized return (10y) */ + get annualizedReturnTen(): string + /** Annualized return (3y) */ + get annualizedReturnThree(): string + /** Annualized return (2y) */ + get annualizedReturnTwo(): string + /** Fund counter id */ + get counterId(): string + /** Fund name */ + get fundName(): string + /** Rank (5y) */ + get performanceRankFiveYears(): number + /** Rank (1d) */ + get performanceRankOneDay(): number + /** Rank (1m) */ + get performanceRankOneMonth(): number + /** Rank (1w) */ + get performanceRankOneWeek(): number + /** Rank (1y) */ + get performanceRankOneYear(): number + /** Rank (6m) */ + get performanceRankSixMonths(): number + /** Rank (10y) */ + get performanceRankTenYears(): number + /** Rank (3m) */ + get performanceRankThreeMonths(): number + /** Rank (3y) */ + get performanceRankThreeYears(): number + /** Rank (2y) */ + get performanceRankTwoYears(): number + /** Rank (ytd) */ + get performanceRankYtd(): number + /** Return (5y) */ + get performanceReturnFiveYears(): string + /** Return (1d) */ + get performanceReturnOneDay(): string + /** Return (1m) */ + get performanceReturnOneMonth(): string + /** Return (1w) */ + get performanceReturnOneWeek(): string + /** Return (1y) */ + get performanceReturnOneYear(): string + /** Return (6m) */ + get performanceReturnSixMonths(): string + /** Return (10y) */ + get performanceReturnTenYears(): string + /** Return (3m) */ + get performanceReturnThreeMonths(): string + /** Return (3y) */ + get performanceReturnThreeYears(): string + /** Return (2y) */ + get performanceReturnTwoYears(): string + /** Return (ytd) */ + get performanceReturnYtd(): string + /** Total peers (5y) */ + get performanceTotalFiveYears(): number + /** Total peers (1d) */ + get performanceTotalOneDay(): number + /** Total peers (1m) */ + get performanceTotalOneMonth(): number + /** Total peers (1w) */ + get performanceTotalOneWeek(): number + /** Total peers (1y) */ + get performanceTotalOneYear(): number + /** Total peers (6m) */ + get performanceTotalSixMonths(): number + /** Total peers (10y) */ + get performanceTotalTenYears(): number + /** Total peers (3m) */ + get performanceTotalThreeMonths(): number + /** Total peers (3y) */ + get performanceTotalThreeYears(): number + /** Total peers (2y) */ + get performanceTotalTwoYears(): number + /** Total peers (ytd) */ + get performanceTotalYtd(): number + /** Seven days annualized */ + get sevenDaysAnnualized(): string + /** Ten thousand price */ + get tenThousandPrice(): string + /** Update time (unix seconds) */ + get updateTime(): number +} + +/** + * Fund performance comparison. The fund performance series is a + * server-defined structure exposed as raw JSON strings. + */ +export declare class FundPerformanceComparison { + toString(): string + toJSON(): any + /** Contrast performance series */ + get contrastPerformances(): Array + /** Fund performances (each a raw JSON string) */ + get fundPerformances(): Array +} + +/** A recent performance point used by the hot-fund list. */ +export declare class FundPerformancePoint { + toString(): string + toJSON(): any + /** Date */ + get date(): string + /** Last done value */ + get lastDone(): string +} + /** Fund position */ export declare class FundPosition { toString(): string toJSON(): any - /** Fund ISIN code */ - get symbol(): string + /** Fund counter id (the ISIN is the last `/`-separated segment) */ + get counterId(): string /** Current equity */ get currentNetAssetValue(): Decimal /** Current equity time */ @@ -852,6 +1638,146 @@ export declare class FundPositionChannel { get positions(): Array } +/** Detail of a single fund position. */ +export declare class FundPositionDetail { + toString(): string + toJSON(): any + /** Detail values */ + get detailValues(): FundPositionDetailValues | null + /** Accumulated profit series */ + get sumProfit(): Array + /** Unit value series */ + get utValue(): Array +} + +/** Detail values of a single fund position. */ +export declare class FundPositionDetailValues { + toString(): string + toJSON(): any + /** Amount */ + get amount(): string + /** Currency */ + get currency(): string + /** Holding cost */ + get holdingCost(): string + /** Holding profit */ + get holdingProfit(): string + /** Holding profit rate */ + get holdingProfitRate(): string + /** Holding units */ + get holdingUnits(): string + /** Holding value */ + get holdingValue(): string + /** Pending buy value */ + get pendingBuyValue(): string + /** Pending sell value */ + get pendingSellValue(): string + /** Accumulated profit (to date) */ + get profitAmountAccumTd(): string + /** Accumulated profit rate (to date) */ + get profitAmountAccumTdRate(): string + /** Recent profit */ + get recentProfit(): string + /** Recent trading day (unix seconds) */ + get recentTradingday(): number + /** Recent unit value */ + get recentUnitValue(): string + /** Sold pending-confirm units */ + get soldPendingConfirmUnits(): string +} + +/** A held-fund net-value point (position view). */ +export declare class FundPositionNav { + toString(): string + toJSON(): any + /** Net value change */ + get change(): string + /** Net value change percent */ + get changePercent(): string + /** Fund counter id */ + get counterId(): string + /** Fund name */ + get counterName(): string + /** Last update time (unix seconds) */ + get lastUpdateTime(): number + /** Net value */ + get value(): string +} + +/** Performance figures for a held fund. */ +export declare class FundPositionPerformance { + toString(): string + toJSON(): any + /** Annualized return (5y) */ + get annualizedReturnFive(): string + /** Annualized return (1y) */ + get annualizedReturnOne(): string + /** Annualized return (10y) */ + get annualizedReturnTen(): string + /** Annualized return (3y) */ + get annualizedReturnThree(): string + /** Annualized return (2y) */ + get annualizedReturnTwo(): string + /** Fund counter id */ + get counterId(): string + /** Fund name */ + get fundName(): string + /** Return (5y) */ + get performanceReturnFiveYears(): string + /** Return (1d) */ + get performanceReturnOneDay(): string + /** Return (1m) */ + get performanceReturnOneMonth(): string + /** Return (1w) */ + get performanceReturnOneWeek(): string + /** Return (1y) */ + get performanceReturnOneYear(): string + /** Return (6m) */ + get performanceReturnSixMonths(): string + /** Return (10y) */ + get performanceReturnTenYears(): string + /** Return (3m) */ + get performanceReturnThreeMonths(): string + /** Return (3y) */ + get performanceReturnThreeYears(): string + /** Return (2y) */ + get performanceReturnTwoYears(): string + /** Return (ytd) */ + get performanceReturnYtd(): string + /** Update time (unix seconds) */ + get updateTime(): number +} + +/** The user's cumulative profit for a held fund. */ +export declare class FundPositionProfits { + toString(): string + toJSON(): any + /** Currency */ + get currency(): string + /** Profit series */ + get historyValue(): Array + /** Last update time (unix seconds) */ + get lastUpdateTime(): number + /** Total profit */ + get sumProfit(): string +} + +/** The user's fund positions overview. */ +export declare class FundPositions { + toString(): string + toJSON(): any + /** Account channel */ + get accountChannel(): string + /** Position entries */ + get list(): Array + /** Pending buy orders amount */ + get pendingBuyOrders(): string + /** Recent trading day (unix seconds) */ + get recentTradingDay(): number + /** Sold pending credit orders amount */ + get soldPendingCreditOrders(): string +} + /** Fund positions response */ export declare class FundPositionsResponse { toString(): string @@ -860,6 +1786,118 @@ export declare class FundPositionsResponse { get channels(): Array } +/** A fund quarterly return entry. */ +export declare class FundQuarterlyReturn { + toString(): string + toJSON(): any + /** Change percent */ + get changePercent(): string + /** Quarter */ + get quarter(): number + /** Year */ + get year(): number +} + +/** A stock held by the fund (reverse lookup). */ +export declare class FundStockHolding { + toString(): string + toJSON(): any + /** Stock code */ + get code(): string + /** Stock counter id */ + get counterId(): string + /** Currency */ + get currency(): string + /** Stock name */ + get name(): string + /** Position ratio */ + get positionRatio(): string + /** Report date */ + get reportDate(): string +} + +/** A fund transaction / cash-flow record. */ +export declare class FundTransaction { + toString(): string + toJSON(): any + /** Amount */ + get amount(): string + /** Category */ + get category(): string + /** Created at (unix seconds) */ + get createdAt(): number + /** Currency */ + get currency(): string + /** Description */ + get description(): string + /** Detail created at (unix seconds) */ + get detailCreatedAt(): number + /** Detail type */ + get detailType(): string + /** Done at (unix seconds) */ + get doneAt(): number + /** Quantity description */ + get quantityDescription(): string + /** Redirect page */ + get redirectPage(): string + /** Redirect page (v2) */ + get redirectPageV2(): string + /** Reference number */ + get refNo(): string + /** Stock quantity */ + get stockQuantity(): string + /** Transaction type */ + get txType(): string + /** Type name */ + get typeName(): string +} + +/** + * Fund trend chart. The performance series are server-defined structures + * exposed as raw JSON strings. + */ +export declare class FundTrend { + toString(): string + toJSON(): any + /** Actual period */ + get actualPeriod(): number + /** Available periods */ + get availablePeriods(): Array + /** Category average performances (each a raw JSON string) */ + get categoryAveragePerformances(): Array + /** Benchmark contrast performances */ + get contrastPerformances(): FundTrendContrast | null + /** Fund performances (each a raw JSON string) */ + get fundPerformances(): Array +} + +/** + * A benchmark contrast series in a fund trend chart. The performance points + * are server-defined structures exposed as raw JSON strings. + */ +export declare class FundTrendContrast { + toString(): string + toJSON(): any + /** Benchmark name */ + get benchmarkName(): string + /** Performance points (each a raw JSON string) */ + get performances(): Array +} + +/** A fund unit-value point (position view). */ +export declare class FundUnitValue { + toString(): string + toJSON(): any + /** Date (unix seconds) */ + get date(): number + /** Day increase rate */ + get dayIncreaseRate(): string + /** Total value */ + get totalValue(): string + /** Unit value */ + get unitValue(): string +} + /** A price-step (bid-size) rule entry from the symbol-info response. */ export declare class GridBidSize { toString(): string @@ -1207,6 +2245,36 @@ export declare class HistoryMarketTemperatureResponse { get records(): Array } +/** A hot-selling fund entry. */ +export declare class HotFund { + toString(): string + toJSON(): any + /** Asset class */ + get assetClass(): number + /** Asset class name */ + get assetClassName(): string + /** Fund counter id */ + get counterId(): string + /** Currency */ + get currency(): string + /** Earning rate */ + get earningRate(): string + /** Recent performance points */ + get fundPerformances(): Array + /** Fund name */ + get name(): string + /** Minimum purchase amount */ + get purchaseAmount(): string + /** Recommendation text */ + get recommendationText(): string + /** Risk level */ + get riskLevel(): number + /** Risk level name */ + get riskLevelName(): string + /** Time interval of the earning rate */ + get timeInterval(): string +} + export declare class HttpClient { /** * Create a new `HttpClient` using API Key authentication @@ -5332,6 +6400,22 @@ export interface FundHolders { lists: Array } +/** Net-value range options (relative months / years before now). */ +export interface FundNavRangeOptions { + /** Number of months before now */ + monthBefore?: number + /** Number of years before now */ + yearBefore?: number +} + +/** Paging options (page / size). */ +export interface FundPageOptions { + /** Page number */ + page?: number + /** Page size */ + size?: number +} + /** Options for get all executions request */ export interface GetAllExecutionsOptions { /** Security symbol */ @@ -5362,6 +6446,128 @@ export interface GetCashFlowOptions { size?: number } +/** Options for the fund analysis / trend / comparison endpoints. */ +export interface GetFundAnalysisOptions { + /** Analysis period */ + period?: number +} + +/** Options for the fund holdings endpoint. */ +export interface GetFundHoldingsOptions { + /** Scene */ + scene?: number +} + +/** Options for the fund orders list. */ +export interface GetFundOrdersOptions { + /** Filter by fund counter ids */ + counterIds?: Array + /** Filter by actions (comma-separated) */ + actions?: string + /** Filter by states (comma-separated) */ + states?: string + /** Filter by currency */ + currency?: string + /** Range start (unix seconds) */ + start?: number + /** Range end (unix seconds) */ + end?: number + /** Page number */ + page?: number + /** Page size */ + size?: number +} + +/** Options for a single fund position dividend list. */ +export interface GetFundPositionDividendsOptions { + /** Account channel */ + accountChannel?: string + /** Account id */ + aaid?: number + /** Currency */ + currency?: string + /** Range start (unix seconds) */ + start?: number + /** Range end (unix seconds) */ + end?: number + /** Page number */ + page?: number + /** Page size */ + size?: number +} + +/** Options for a single fund position detail. */ +export interface GetFundPositionOptions { + /** Account channel */ + accountChannel?: string + /** Account id */ + aaid?: number + /** Range start */ + start?: string + /** Range end */ + end?: string +} + +/** Options for a single fund position cumulative-profit list. */ +export interface GetFundPositionProfitsOptions { + /** Account channel */ + accountChannel?: string + /** Account id */ + aaid?: number + /** Range start */ + start?: string + /** Range end */ + end?: string + /** Page number */ + page?: number + /** Page size */ + size?: number +} + +/** Options for the fund positions overview endpoint. */ +export interface GetFundPositionsOptions { + /** Account channel */ + accountChannel?: string + /** Account id */ + aaid?: number +} + +/** Options for the fund list. */ +export interface GetFundsOptions { + /** Server-defined filter object, as a raw JSON string */ + filter?: string + /** Quick-filter ids */ + quickIds?: Array + /** Earning-rate time intervals */ + timeInterval?: Array +} + +/** Options for the fund stock-holdings (reverse) endpoint. */ +export interface GetFundStockHoldingsOptions { + /** Maximum number of stocks to return */ + limit?: number +} + +/** Options for the fund transactions (cash-flow) list. */ +export interface GetFundTransactionsOptions { + /** Account channel */ + accountChannel?: string + /** Business type */ + businessType?: string + /** Category */ + category?: string + /** Currencies (comma-separated) */ + currencies?: string + /** Range start (unix seconds) */ + start?: number + /** Range end (unix seconds) */ + end?: number + /** Page number */ + page?: number + /** Page size */ + size?: number +} + /** Options for get grid trading order detail request */ export interface GetGridOrderDetailOptions { /** Grid master order id */ @@ -7375,6 +8581,30 @@ export interface SubmitAttachedParams { activateRth?: OutsideRTH } +/** Options for submitting a fund order. */ +export interface SubmitFundOrderOptions { + /** Fund counter id */ + counterId: string + /** Action (buy/sell) */ + action: string + /** Currency */ + currency: string + /** Amount (for amount-based orders) */ + amount?: string + /** Units (for unit-based orders) */ + units?: string + /** Dividend option */ + dividendOption?: number + /** Fee */ + fee?: string + /** Whether to sell all */ + isSellAll?: boolean + /** Remark */ + remark?: string + /** Trade method */ + tradeMethod?: number +} + /** Options for submit grid trading order request */ export interface SubmitGridOrderOptions { /** Security code */ @@ -8208,6 +9438,26 @@ export interface USValuationOverview { aiSummary: string } +/** Options for validating a fund order. */ +export interface ValidateFundOrderOptions { + /** Fund counter id */ + counterId: string + /** Action (buy/sell) */ + action: string + /** Currency */ + currency: string + /** Amount (for amount-based orders) */ + amount?: string + /** Units (for unit-based orders) */ + units?: string + /** Dividend option */ + dividendOption?: number + /** Fund source */ + fundSource?: number + /** Account channel */ + accountChannel?: string +} + /** One security's valuation comparison item. */ export interface ValuationComparisonItem { /** Symbol (e.g. `"AAPL.US"`) */ diff --git a/nodejs/index.js b/nodejs/index.js index 7b13527fa..efec33ac5 100644 --- a/nodejs/index.js +++ b/nodejs/index.js @@ -601,9 +601,48 @@ module.exports.Execution = nativeBinding.Execution module.exports.FilingItem = nativeBinding.FilingItem module.exports.FrozenTransactionFee = nativeBinding.FrozenTransactionFee module.exports.FundamentalContext = nativeBinding.FundamentalContext +module.exports.FundAnalysis = nativeBinding.FundAnalysis +module.exports.FundAnalysisDetail = nativeBinding.FundAnalysisDetail +module.exports.FundAnnualReturn = nativeBinding.FundAnnualReturn +module.exports.FundAssetAllocation = nativeBinding.FundAssetAllocation +module.exports.FundAssetAllocationItem = nativeBinding.FundAssetAllocationItem +module.exports.FundBrief = nativeBinding.FundBrief +module.exports.FundContext = nativeBinding.FundContext +module.exports.FundDatedValue = nativeBinding.FundDatedValue +module.exports.FundDetail = nativeBinding.FundDetail +module.exports.FundDividend = nativeBinding.FundDividend +module.exports.FundDividends = nativeBinding.FundDividends +module.exports.FundFilters = nativeBinding.FundFilters +module.exports.FundHolding = nativeBinding.FundHolding +module.exports.FundHoldingPosition = nativeBinding.FundHoldingPosition +module.exports.FundHoldings = nativeBinding.FundHoldings +module.exports.FundNamedContrast = nativeBinding.FundNamedContrast +module.exports.FundNavValue = nativeBinding.FundNavValue +module.exports.FundOrder = nativeBinding.FundOrder +module.exports.FundOrderDetail = nativeBinding.FundOrderDetail +module.exports.FundOrderInfo = nativeBinding.FundOrderInfo +module.exports.FundOrderKeyword = nativeBinding.FundOrderKeyword +module.exports.FundOrderStage = nativeBinding.FundOrderStage +module.exports.FundOrderSubmitResponse = nativeBinding.FundOrderSubmitResponse +module.exports.FundOrderValidation = nativeBinding.FundOrderValidation +module.exports.FundPerformance = nativeBinding.FundPerformance +module.exports.FundPerformanceComparison = nativeBinding.FundPerformanceComparison +module.exports.FundPerformancePoint = nativeBinding.FundPerformancePoint module.exports.FundPosition = nativeBinding.FundPosition module.exports.FundPositionChannel = nativeBinding.FundPositionChannel +module.exports.FundPositionDetail = nativeBinding.FundPositionDetail +module.exports.FundPositionDetailValues = nativeBinding.FundPositionDetailValues +module.exports.FundPositionNav = nativeBinding.FundPositionNav +module.exports.FundPositionPerformance = nativeBinding.FundPositionPerformance +module.exports.FundPositionProfits = nativeBinding.FundPositionProfits +module.exports.FundPositions = nativeBinding.FundPositions module.exports.FundPositionsResponse = nativeBinding.FundPositionsResponse +module.exports.FundQuarterlyReturn = nativeBinding.FundQuarterlyReturn +module.exports.FundStockHolding = nativeBinding.FundStockHolding +module.exports.FundTransaction = nativeBinding.FundTransaction +module.exports.FundTrend = nativeBinding.FundTrend +module.exports.FundTrendContrast = nativeBinding.FundTrendContrast +module.exports.FundUnitValue = nativeBinding.FundUnitValue module.exports.GridBidSize = nativeBinding.GridBidSize module.exports.GridChannelInfo = nativeBinding.GridChannelInfo module.exports.GridContext = nativeBinding.GridContext @@ -615,6 +654,7 @@ module.exports.GridOrderSubOrder = nativeBinding.GridOrderSubOrder module.exports.GridSymbolInfo = nativeBinding.GridSymbolInfo module.exports.GridTriggerHistoryResponse = nativeBinding.GridTriggerHistoryResponse module.exports.HistoryMarketTemperatureResponse = nativeBinding.HistoryMarketTemperatureResponse +module.exports.HotFund = nativeBinding.HotFund module.exports.HttpClient = nativeBinding.HttpClient module.exports.IntradayLine = nativeBinding.IntradayLine module.exports.IssuerInfo = nativeBinding.IssuerInfo diff --git a/nodejs/src/fund/context.rs b/nodejs/src/fund/context.rs new file mode 100644 index 000000000..1a8e56bdf --- /dev/null +++ b/nodejs/src/fund/context.rs @@ -0,0 +1,423 @@ +use std::sync::Arc; + +use napi::Result; + +use crate::{ + config::Config, + error::ErrorNewType, + fund::{ + requests::{ + FundNavRangeOptions, FundPageOptions, GetFundAnalysisOptions, GetFundHoldingsOptions, + GetFundOrdersOptions, GetFundPositionDividendsOptions, GetFundPositionOptions, + GetFundPositionProfitsOptions, GetFundPositionsOptions, GetFundStockHoldingsOptions, + GetFundTransactionsOptions, GetFundsOptions, SubmitFundOrderOptions, + ValidateFundOrderOptions, + }, + types::{ + FundAnalysis, FundAnalysisDetail, FundAnnualReturn, FundBrief, FundDetail, + FundDividends, FundFilters, FundHoldings, FundNavValue, FundOrder, FundOrderDetail, + FundOrderSubmitResponse, FundOrderValidation, FundPerformance, + FundPerformanceComparison, FundPositionDetail, FundPositionNav, + FundPositionPerformance, FundPositionProfits, FundPositions, FundQuarterlyReturn, + FundStockHolding, FundTransaction, FundTrend, HotFund, + }, + }, +}; + +/// Fund (mutual fund) channel context. +#[napi_derive::napi] +#[derive(Clone)] +pub struct FundContext { + ctx: longbridge::fund::FundContext, +} + +#[napi_derive::napi] +impl FundContext { + /// Create a new `FundContext`. + #[napi] + pub fn new(config: &Config) -> FundContext { + Self { + ctx: longbridge::fund::FundContext::new(Arc::new(config.0.clone())), + } + } + + // ----- fund catalog / market data (scope: quote) ----- + + /// Get the hot-selling fund list. + #[napi] + pub async fn hot_funds(&self) -> Result> { + self.ctx + .hot_funds() + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the fund list. + #[napi] + pub async fn funds(&self, opts: Option) -> Result> { + let opts = match opts { + Some(opts) => Some(opts.try_into()?), + None => None, + }; + self.ctx + .funds(opts) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the fund list filter options. + #[napi] + pub async fn filters(&self) -> Result { + self.ctx.filters().await.map_err(ErrorNewType)?.try_into() + } + + /// Get fund detail. + #[napi] + pub async fn detail(&self, counter_id: String) -> Result { + self.ctx + .detail(counter_id) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund analysis (level 1). + #[napi] + pub async fn analysis( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .analysis(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund analysis detail (level 2). + #[napi] + pub async fn analysis_detail( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .analysis_detail(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund trend chart. + #[napi] + pub async fn trend( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .trend(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund annual returns. + #[napi] + pub async fn annual_returns( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .annual_returns(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund quarterly returns. + #[napi] + pub async fn quarterly_returns( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .quarterly_returns(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund performance figures. + #[napi] + pub async fn performance(&self, counter_id: String) -> Result> { + self.ctx + .performance(counter_id) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund performance comparison. + #[napi] + pub async fn performance_comparison( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .performance_comparison(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund latest net value. + #[napi] + pub async fn nav(&self, counter_id: String) -> Result> { + self.ctx + .nav(counter_id) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund historical net value (paged). + #[napi] + pub async fn nav_history( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .nav_history(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund historical net value by relative time range. + #[napi] + pub async fn nav_range( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .nav_range(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get a fund's top-10 holdings. + #[napi] + pub async fn holdings( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .holdings(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the stocks held by a fund (reverse lookup). + #[napi] + pub async fn stock_holdings( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .stock_holdings(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + // ----- user fund positions (scope: portfolio-asset) ----- + + /// Get the user's fund positions overview. + #[napi] + pub async fn positions(&self, opts: Option) -> Result { + self.ctx + .positions(opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the user's single fund position detail. + #[napi] + pub async fn position( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .position(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the performance figures of a held fund. + #[napi] + pub async fn position_performance( + &self, + counter_id: String, + ) -> Result> { + self.ctx + .position_performance(counter_id) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the cumulative-profit series of a held fund. + #[napi] + pub async fn position_profits( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .position_profits(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the net-value history of a held fund. + #[napi] + pub async fn position_nav( + &self, + counter_id: String, + opts: Option, + ) -> Result> { + self.ctx + .position_nav(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the dividend records of a held fund. + #[napi] + pub async fn position_dividends( + &self, + counter_id: String, + opts: Option, + ) -> Result { + self.ctx + .position_dividends(counter_id, opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .try_into() + } + + // ----- fund orders & trading (scope: order) ----- + + /// Get the user's fund orders (also serves as the trade/execution record). + #[napi] + pub async fn orders(&self, opts: Option) -> Result> { + self.ctx + .orders(opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get a fund order detail. + #[napi] + pub async fn order(&self, order_id: i64) -> Result { + self.ctx + .order(order_id) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the user's fund transactions (cash-flow records). + #[napi] + pub async fn transactions( + &self, + opts: Option, + ) -> Result> { + self.ctx + .transactions(opts.map(Into::into)) + .await + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Validate a fund order before submitting. + #[napi] + pub async fn validate_order( + &self, + opts: ValidateFundOrderOptions, + ) -> Result { + self.ctx + .validate_order(opts.into()) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Submit a fund order (buy / sell). + #[napi] + pub async fn submit_order( + &self, + opts: SubmitFundOrderOptions, + ) -> Result { + self.ctx + .submit_order(opts.into()) + .await + .map_err(ErrorNewType)? + .try_into() + } + + /// Cancel (withdraw) a fund order. + #[napi] + pub async fn cancel_order(&self, order_id: i64) -> Result<()> { + self.ctx + .cancel_order(order_id) + .await + .map_err(ErrorNewType)?; + Ok(()) + } +} diff --git a/nodejs/src/fund/mod.rs b/nodejs/src/fund/mod.rs new file mode 100644 index 000000000..a61c0a4ca --- /dev/null +++ b/nodejs/src/fund/mod.rs @@ -0,0 +1,3 @@ +mod context; +mod requests; +mod types; diff --git a/nodejs/src/fund/requests.rs b/nodejs/src/fund/requests.rs new file mode 100644 index 000000000..ba4db508b --- /dev/null +++ b/nodejs/src/fund/requests.rs @@ -0,0 +1,497 @@ +//! napi request-option structs for the fund endpoints. +//! +//! Each struct mirrors a builder-style options type from the Rust core and +//! converts into it. The fund `counter_id` is passed as a separate method +//! argument, so these structs only carry query-string / request-body fields. + +use napi::bindgen_prelude::*; + +/// Options for the fund list. +#[napi_derive::napi(object)] +pub struct GetFundsOptions { + /// Server-defined filter object, as a raw JSON string + pub filter: Option, + /// Quick-filter ids + pub quick_ids: Option>, + /// Earning-rate time intervals + pub time_interval: Option>, +} + +impl ::std::convert::TryFrom for longbridge::fund::GetFundsOptions { + type Error = Error; + + fn try_from(opts: GetFundsOptions) -> Result { + let mut opts2 = longbridge::fund::GetFundsOptions::new(); + if let Some(filter) = opts.filter { + let value: serde_json::Value = serde_json::from_str(&filter) + .map_err(|err| Error::from_reason(format!("invalid filter JSON: {err}")))?; + opts2 = opts2.filter(value); + } + if let Some(quick_ids) = opts.quick_ids { + opts2 = opts2.quick_ids(quick_ids); + } + if let Some(time_interval) = opts.time_interval { + opts2 = opts2.time_interval(time_interval); + } + Ok(opts2) + } +} + +/// Options for the fund analysis / trend / comparison endpoints. +#[napi_derive::napi(object)] +pub struct GetFundAnalysisOptions { + /// Analysis period + pub period: Option, +} + +impl From for longbridge::fund::GetFundAnalysisOptions { + #[inline] + fn from(opts: GetFundAnalysisOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundAnalysisOptions::new(); + if let Some(period) = opts.period { + opts2 = opts2.period(period); + } + opts2 + } +} + +/// Paging options (page / size). +#[napi_derive::napi(object)] +pub struct FundPageOptions { + /// Page number + pub page: Option, + /// Page size + pub size: Option, +} + +impl From for longbridge::fund::FundPageOptions { + #[inline] + fn from(opts: FundPageOptions) -> Self { + let mut opts2 = longbridge::fund::FundPageOptions::new(); + if let Some(page) = opts.page { + opts2 = opts2.page(page); + } + if let Some(size) = opts.size { + opts2 = opts2.size(size); + } + opts2 + } +} + +/// Net-value range options (relative months / years before now). +#[napi_derive::napi(object)] +pub struct FundNavRangeOptions { + /// Number of months before now + pub month_before: Option, + /// Number of years before now + pub year_before: Option, +} + +impl From for longbridge::fund::FundNavRangeOptions { + #[inline] + fn from(opts: FundNavRangeOptions) -> Self { + let mut opts2 = longbridge::fund::FundNavRangeOptions::new(); + if let Some(month_before) = opts.month_before { + opts2 = opts2.month_before(month_before); + } + if let Some(year_before) = opts.year_before { + opts2 = opts2.year_before(year_before); + } + opts2 + } +} + +/// Options for the fund holdings endpoint. +#[napi_derive::napi(object)] +pub struct GetFundHoldingsOptions { + /// Scene + pub scene: Option, +} + +impl From for longbridge::fund::GetFundHoldingsOptions { + #[inline] + fn from(opts: GetFundHoldingsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundHoldingsOptions::new(); + if let Some(scene) = opts.scene { + opts2 = opts2.scene(scene); + } + opts2 + } +} + +/// Options for the fund stock-holdings (reverse) endpoint. +#[napi_derive::napi(object)] +pub struct GetFundStockHoldingsOptions { + /// Maximum number of stocks to return + pub limit: Option, +} + +impl From for longbridge::fund::GetFundStockHoldingsOptions { + #[inline] + fn from(opts: GetFundStockHoldingsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundStockHoldingsOptions::new(); + if let Some(limit) = opts.limit { + opts2 = opts2.limit(limit); + } + opts2 + } +} + +/// Options for the fund positions overview endpoint. +#[napi_derive::napi(object)] +pub struct GetFundPositionsOptions { + /// Account channel + pub account_channel: Option, + /// Account id + pub aaid: Option, +} + +impl From for longbridge::fund::GetFundPositionsOptions { + #[inline] + fn from(opts: GetFundPositionsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundPositionsOptions::new(); + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + if let Some(aaid) = opts.aaid { + opts2 = opts2.aaid(aaid); + } + opts2 + } +} + +/// Options for a single fund position detail. +#[napi_derive::napi(object)] +pub struct GetFundPositionOptions { + /// Account channel + pub account_channel: Option, + /// Account id + pub aaid: Option, + /// Range start + pub start: Option, + /// Range end + pub end: Option, +} + +impl From for longbridge::fund::GetFundPositionOptions { + #[inline] + fn from(opts: GetFundPositionOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundPositionOptions::new(); + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + if let Some(aaid) = opts.aaid { + opts2 = opts2.aaid(aaid); + } + if let Some(start) = opts.start { + opts2 = opts2.start(start); + } + if let Some(end) = opts.end { + opts2 = opts2.end(end); + } + opts2 + } +} + +/// Options for a single fund position cumulative-profit list. +#[napi_derive::napi(object)] +pub struct GetFundPositionProfitsOptions { + /// Account channel + pub account_channel: Option, + /// Account id + pub aaid: Option, + /// Range start + pub start: Option, + /// Range end + pub end: Option, + /// Page number + pub page: Option, + /// Page size + pub size: Option, +} + +impl From for longbridge::fund::GetFundPositionProfitsOptions { + #[inline] + fn from(opts: GetFundPositionProfitsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundPositionProfitsOptions::new(); + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + if let Some(aaid) = opts.aaid { + opts2 = opts2.aaid(aaid); + } + if let Some(start) = opts.start { + opts2 = opts2.start(start); + } + if let Some(end) = opts.end { + opts2 = opts2.end(end); + } + if let Some(page) = opts.page { + opts2 = opts2.page(page); + } + if let Some(size) = opts.size { + opts2 = opts2.size(size); + } + opts2 + } +} + +/// Options for a single fund position dividend list. +#[napi_derive::napi(object)] +pub struct GetFundPositionDividendsOptions { + /// Account channel + pub account_channel: Option, + /// Account id + pub aaid: Option, + /// Currency + pub currency: Option, + /// Range start (unix seconds) + pub start: Option, + /// Range end (unix seconds) + pub end: Option, + /// Page number + pub page: Option, + /// Page size + pub size: Option, +} + +impl From for longbridge::fund::GetFundPositionDividendsOptions { + #[inline] + fn from(opts: GetFundPositionDividendsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundPositionDividendsOptions::new(); + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + if let Some(aaid) = opts.aaid { + opts2 = opts2.aaid(aaid); + } + if let Some(currency) = opts.currency { + opts2 = opts2.currency(currency); + } + if let Some(start) = opts.start { + opts2 = opts2.start(start); + } + if let Some(end) = opts.end { + opts2 = opts2.end(end); + } + if let Some(page) = opts.page { + opts2 = opts2.page(page); + } + if let Some(size) = opts.size { + opts2 = opts2.size(size); + } + opts2 + } +} + +/// Options for the fund orders list. +#[napi_derive::napi(object)] +pub struct GetFundOrdersOptions { + /// Filter by fund counter ids + pub counter_ids: Option>, + /// Filter by actions (comma-separated) + pub actions: Option, + /// Filter by states (comma-separated) + pub states: Option, + /// Filter by currency + pub currency: Option, + /// Range start (unix seconds) + pub start: Option, + /// Range end (unix seconds) + pub end: Option, + /// Page number + pub page: Option, + /// Page size + pub size: Option, +} + +impl From for longbridge::fund::GetFundOrdersOptions { + #[inline] + fn from(opts: GetFundOrdersOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundOrdersOptions::new(); + if let Some(counter_ids) = opts.counter_ids { + opts2 = opts2.counter_ids(counter_ids); + } + if let Some(actions) = opts.actions { + opts2 = opts2.actions(actions); + } + if let Some(states) = opts.states { + opts2 = opts2.states(states); + } + if let Some(currency) = opts.currency { + opts2 = opts2.currency(currency); + } + if let Some(start) = opts.start { + opts2 = opts2.start(start); + } + if let Some(end) = opts.end { + opts2 = opts2.end(end); + } + if let Some(page) = opts.page { + opts2 = opts2.page(page); + } + if let Some(size) = opts.size { + opts2 = opts2.size(size); + } + opts2 + } +} + +/// Options for the fund transactions (cash-flow) list. +#[napi_derive::napi(object)] +pub struct GetFundTransactionsOptions { + /// Account channel + pub account_channel: Option, + /// Business type + pub business_type: Option, + /// Category + pub category: Option, + /// Currencies (comma-separated) + pub currencies: Option, + /// Range start (unix seconds) + pub start: Option, + /// Range end (unix seconds) + pub end: Option, + /// Page number + pub page: Option, + /// Page size + pub size: Option, +} + +impl From for longbridge::fund::GetFundTransactionsOptions { + #[inline] + fn from(opts: GetFundTransactionsOptions) -> Self { + let mut opts2 = longbridge::fund::GetFundTransactionsOptions::new(); + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + if let Some(business_type) = opts.business_type { + opts2 = opts2.business_type(business_type); + } + if let Some(category) = opts.category { + opts2 = opts2.category(category); + } + if let Some(currencies) = opts.currencies { + opts2 = opts2.currencies(currencies); + } + if let Some(start) = opts.start { + opts2 = opts2.start(start); + } + if let Some(end) = opts.end { + opts2 = opts2.end(end); + } + if let Some(page) = opts.page { + opts2 = opts2.page(page); + } + if let Some(size) = opts.size { + opts2 = opts2.size(size); + } + opts2 + } +} + +/// Options for validating a fund order. +#[napi_derive::napi(object)] +pub struct ValidateFundOrderOptions { + /// Fund counter id + pub counter_id: String, + /// Action (buy/sell) + pub action: String, + /// Currency + pub currency: String, + /// Amount (for amount-based orders) + pub amount: Option, + /// Units (for unit-based orders) + pub units: Option, + /// Dividend option + pub dividend_option: Option, + /// Fund source + pub fund_source: Option, + /// Account channel + pub account_channel: Option, +} + +impl From for longbridge::fund::ValidateFundOrderOptions { + #[inline] + fn from(opts: ValidateFundOrderOptions) -> Self { + let mut opts2 = longbridge::fund::ValidateFundOrderOptions::new( + opts.counter_id, + opts.action, + opts.currency, + ); + if let Some(amount) = opts.amount { + opts2 = opts2.amount(amount); + } + if let Some(units) = opts.units { + opts2 = opts2.units(units); + } + if let Some(dividend_option) = opts.dividend_option { + opts2 = opts2.dividend_option(dividend_option); + } + if let Some(fund_source) = opts.fund_source { + opts2 = opts2.fund_source(fund_source); + } + if let Some(account_channel) = opts.account_channel { + opts2 = opts2.account_channel(account_channel); + } + opts2 + } +} + +/// Options for submitting a fund order. +#[napi_derive::napi(object)] +pub struct SubmitFundOrderOptions { + /// Fund counter id + pub counter_id: String, + /// Action (buy/sell) + pub action: String, + /// Currency + pub currency: String, + /// Amount (for amount-based orders) + pub amount: Option, + /// Units (for unit-based orders) + pub units: Option, + /// Dividend option + pub dividend_option: Option, + /// Fee + pub fee: Option, + /// Whether to sell all + pub is_sell_all: Option, + /// Remark + pub remark: Option, + /// Trade method + pub trade_method: Option, +} + +impl From for longbridge::fund::SubmitFundOrderOptions { + #[inline] + fn from(opts: SubmitFundOrderOptions) -> Self { + let mut opts2 = longbridge::fund::SubmitFundOrderOptions::new( + opts.counter_id, + opts.action, + opts.currency, + ); + if let Some(amount) = opts.amount { + opts2 = opts2.amount(amount); + } + if let Some(units) = opts.units { + opts2 = opts2.units(units); + } + if let Some(dividend_option) = opts.dividend_option { + opts2 = opts2.dividend_option(dividend_option); + } + if let Some(fee) = opts.fee { + opts2 = opts2.fee(fee); + } + if let Some(is_sell_all) = opts.is_sell_all { + opts2 = opts2.is_sell_all(is_sell_all); + } + if let Some(remark) = opts.remark { + opts2 = opts2.remark(remark); + } + if let Some(trade_method) = opts.trade_method { + opts2 = opts2.trade_method(trade_method); + } + opts2 + } +} diff --git a/nodejs/src/fund/types.rs b/nodejs/src/fund/types.rs new file mode 100644 index 000000000..a382b111a --- /dev/null +++ b/nodejs/src/fund/types.rs @@ -0,0 +1,1572 @@ +// `to_string` here backs the JS `toString()` method for the raw-JSON "any" +// fields; it is an intentional inherent method, not a `Display` impl. +#![allow(clippy::inherent_to_string)] + +use longbridge_nodejs_macros::JsObject; + +use crate::utils::ToJSON; + +/// Convert a `serde_json::Value` into its raw JSON string. +#[inline] +fn value_to_json_string(value: serde_json::Value) -> String { + value.to_string() +} + +/// Convert a list of `serde_json::Value` into a list of raw JSON strings. +#[inline] +fn values_to_json_strings(values: Vec) -> Vec { + values.into_iter().map(value_to_json_string).collect() +} + +/// A fund net-asset-value data point (latest / historical). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundNavValue")] +pub struct FundNavValue { + /// Net value change + change: String, + /// Net value change percent + change_percent: String, + /// Formatted change percent + change_percent_format: String, + /// Fund counter id + counter_id: String, + /// Fund name + counter_name: String, + /// Currency + currency: String, + /// Formatted date + date_format: String, + /// ISIN + isin: String, + /// Last update time (unix seconds) + last_update_time: i64, + /// Net value + value: String, + /// Formatted net value + value_format: String, +} + +/// A recent performance point used by the hot-fund list. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPerformancePoint")] +pub struct FundPerformancePoint { + /// Date + date: String, + /// Last done value + last_done: String, +} + +/// A hot-selling fund entry. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::HotFund")] +pub struct HotFund { + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Earning rate + earning_rate: String, + /// Recent performance points + #[js(array)] + fund_performances: Vec, + /// Fund name + name: String, + /// Minimum purchase amount + purchase_amount: String, + /// Recommendation text + recommendation_text: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Time interval of the earning rate + time_interval: String, +} + +/// A fund entry in the fund list. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundBrief")] +pub struct FundBrief { + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Fund code + code: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Description + description: String, + /// Earning rate + earning_rate: String, + /// Whether the user is holding this fund + holding: bool, + /// ISIN + isin: String, + /// Fund name + name: String, + /// Product + product: String, + /// Minimum purchase amount + purchase_amount: String, + /// Recommendation text + recommendation_text: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Time interval of the earning rate + time_interval: String, + /// Unit value + unit_value: String, +} + +/// A single holding entry inside a fund's asset allocation. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundAssetAllocationItem")] +pub struct FundAssetAllocationItem { + /// Security code + code: String, + /// Security counter id + counter_id: String, + /// Name + name: String, + /// Position ratio + position_ratio: String, +} + +/// A fund's asset allocation. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundAssetAllocation")] +pub struct FundAssetAllocation { + /// Asset type + asset_type: i32, + /// Allocation entries + #[js(array)] + lists: Vec, + /// Report date + report_date: String, +} + +/// Fund detail. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundDetail")] +pub struct FundDetail { + /// Additional purchase amount + additional_purchase_amount: String, + /// Affirm day + affirm_day: i32, + /// Amount affirm day + amount_affirm_day: String, + /// Asset allocation + #[js(opt)] + asset_allocation: Option, + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Bill purchase rate + bill_purchase_rate: String, + /// Channel + channel: String, + /// Close period + close_period: String, + /// Fund code + code: String, + /// Currency + currency: String, + /// Cut off time + cut_off_time: String, + /// Whether it is a derivative + derivatives: bool, + /// Done day + done_day: i32, + /// Excess return fee + excess_return_fee: String, + /// GST rate + gst_rate: String, + /// Introduction + introduce: String, + /// Whether it is a cash-plus fund + is_cash_plus: bool, + /// Whether it is a complex product + is_complex: bool, + /// Whether it is a new cash-plus fund + is_new_cash_plus: bool, + /// Whether it is a Yinghebao fund + is_yinghebao: bool, + /// ISIN + isin: String, + /// Management rate + manage_rate: String, + /// Manager + manager: String, + /// Minimum holding cash + min_hold_cash: String, + /// Minimum holding share + min_hold_share: String, + /// Minimum sell share + min_sell_share: String, + /// Month raise day + month_raise_day: String, + /// Fund name + name: String, + /// Net value deadline + nav_deadline: String, + /// Whether it is no-load + no_load: bool, + /// Open date + open_date: String, + /// Open period + open_period: String, + /// Product + product: String, + /// Product information locals + product_information_locals: String, + /// Profile + profile: String, + /// Whether purchasable + purchasable: i32, + /// Purchase affirm day + purchase_affirm_day: String, + /// Minimum purchase amount + purchase_amount: String, + /// Purchase rate + purchase_rate: String, + /// Rating + rating: i32, + /// Whether redeemable + redeemable: i32, + /// Redemption advance day + redemption_advance_day: String, + /// Redemption amount + redemption_amount: String, + /// Redemption close period text + redemption_close_period_shows: String, + /// Redemption done day + redemption_done_day: String, + /// Redemption open day text + redemption_open_day_shows: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Verify status + verify_status: i32, + /// Whether it is a virtual currency fund + virtual_currency: bool, + /// Year to date yield + year_to_date_yield: String, + /// Year to date yield type + ytd_yield_type: i32, +} + +/// A fund annual return entry. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundAnnualReturn")] +pub struct FundAnnualReturn { + /// Change percent + change_percent: String, + /// Year + year: i32, +} + +/// A fund quarterly return entry. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundQuarterlyReturn")] +pub struct FundQuarterlyReturn { + /// Change percent + change_percent: String, + /// Quarter + quarter: i32, + /// Year + year: i32, +} + +/// A fund's detailed performance figures. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPerformance")] +pub struct FundPerformance { + /// Annualized return (5y) + annualized_return_five: String, + /// Annualized return (1y) + annualized_return_one: String, + /// Annualized return (10y) + annualized_return_ten: String, + /// Annualized return (3y) + annualized_return_three: String, + /// Annualized return (2y) + annualized_return_two: String, + /// Fund counter id + counter_id: String, + /// Fund name + fund_name: String, + /// Rank (5y) + performance_rank_five_years: i32, + /// Rank (1d) + performance_rank_one_day: i32, + /// Rank (1m) + performance_rank_one_month: i32, + /// Rank (1w) + performance_rank_one_week: i32, + /// Rank (1y) + performance_rank_one_year: i32, + /// Rank (6m) + performance_rank_six_months: i32, + /// Rank (10y) + performance_rank_ten_years: i32, + /// Rank (3m) + performance_rank_three_months: i32, + /// Rank (3y) + performance_rank_three_years: i32, + /// Rank (2y) + performance_rank_two_years: i32, + /// Rank (ytd) + performance_rank_ytd: i32, + /// Return (5y) + performance_return_five_years: String, + /// Return (1d) + performance_return_one_day: String, + /// Return (1m) + performance_return_one_month: String, + /// Return (1w) + performance_return_one_week: String, + /// Return (1y) + performance_return_one_year: String, + /// Return (6m) + performance_return_six_months: String, + /// Return (10y) + performance_return_ten_years: String, + /// Return (3m) + performance_return_three_months: String, + /// Return (3y) + performance_return_three_years: String, + /// Return (2y) + performance_return_two_years: String, + /// Return (ytd) + performance_return_ytd: String, + /// Total peers (5y) + performance_total_five_years: i32, + /// Total peers (1d) + performance_total_one_day: i32, + /// Total peers (1m) + performance_total_one_month: i32, + /// Total peers (1w) + performance_total_one_week: i32, + /// Total peers (1y) + performance_total_one_year: i32, + /// Total peers (6m) + performance_total_six_months: i32, + /// Total peers (10y) + performance_total_ten_years: i32, + /// Total peers (3m) + performance_total_three_months: i32, + /// Total peers (3y) + performance_total_three_years: i32, + /// Total peers (2y) + performance_total_two_years: i32, + /// Total peers (ytd) + performance_total_ytd: i32, + /// Seven days annualized + seven_days_annualized: String, + /// Ten thousand price + ten_thousand_price: String, + /// Update time (unix seconds) + update_time: i64, +} + +/// A single fund holding (top-10 holdings). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundHolding")] +pub struct FundHolding { + /// Bond type + bond_type: String, + /// Bond type name + bond_type_name: String, + /// Country name + country_name: String, + /// Holding type + holding_type: String, + /// Industry name + industry_name: String, + /// Market value + market_value: String, + /// Maturity date + maturity_date: String, + /// Name + name: String, + /// Share change + share_change: String, + /// Share change percent + share_change_percent: String, + /// Shares + shares: String, + /// Weighting + weighting: String, +} + +/// A fund's top-10 holdings. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundHoldings")] +pub struct FundHoldings { + /// Holding entries + #[js(array)] + holdings: Vec, + /// Report date + report_date: String, + /// Total weighting + weighting: String, +} + +/// A stock held by the fund (reverse lookup). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundStockHolding")] +pub struct FundStockHolding { + /// Stock code + code: String, + /// Stock counter id + counter_id: String, + /// Currency + currency: String, + /// Stock name + name: String, + /// Position ratio + position_ratio: String, + /// Report date + report_date: String, +} + +/// A single fund position held by the user. +/// +/// Named `FundHoldingPosition` to avoid a name clash with the trade channel's +/// existing `FundPosition` class. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPosition")] +pub struct FundHoldingPosition { + /// Holding amount + amount: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Frozen units + freeze_units: String, + /// Holding profit + holding_profit: String, + /// Holding units + holding_units: String, + /// Fund name + name: String, + /// Recent profit + recent_profit: String, + /// Recent trading day (unix seconds) + recent_trading_day: i64, + /// Accumulated recent profit + sum_recent_profit: String, +} + +/// The user's fund positions overview. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositions")] +pub struct FundPositions { + /// Account channel + account_channel: String, + /// Position entries + #[js(array)] + list: Vec, + /// Pending buy orders amount + pending_buy_orders: String, + /// Recent trading day (unix seconds) + recent_trading_day: i64, + /// Sold pending credit orders amount + sold_pending_credit_orders: String, +} + +/// A dated value point. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundDatedValue")] +pub struct FundDatedValue { + /// Date (unix seconds) + date: i64, + /// Value + value: String, +} + +/// A fund unit-value point (position view). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundUnitValue")] +pub struct FundUnitValue { + /// Date (unix seconds) + date: i64, + /// Day increase rate + day_increase_rate: String, + /// Total value + total_value: String, + /// Unit value + unit_value: String, +} + +/// Detail values of a single fund position. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositionDetailValues")] +pub struct FundPositionDetailValues { + /// Amount + amount: String, + /// Currency + currency: String, + /// Holding cost + holding_cost: String, + /// Holding profit + holding_profit: String, + /// Holding profit rate + holding_profit_rate: String, + /// Holding units + holding_units: String, + /// Holding value + holding_value: String, + /// Pending buy value + pending_buy_value: String, + /// Pending sell value + pending_sell_value: String, + /// Accumulated profit (to date) + profit_amount_accum_td: String, + /// Accumulated profit rate (to date) + profit_amount_accum_td_rate: String, + /// Recent profit + recent_profit: String, + /// Recent trading day (unix seconds) + recent_tradingday: i64, + /// Recent unit value + recent_unit_value: String, + /// Sold pending-confirm units + sold_pending_confirm_units: String, +} + +/// Detail of a single fund position. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositionDetail")] +pub struct FundPositionDetail { + /// Detail values + #[js(opt)] + detail_values: Option, + /// Accumulated profit series + #[js(array)] + sum_profit: Vec, + /// Unit value series + #[js(array)] + ut_value: Vec, +} + +/// Performance figures for a held fund. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositionPerformance")] +pub struct FundPositionPerformance { + /// Annualized return (5y) + annualized_return_five: String, + /// Annualized return (1y) + annualized_return_one: String, + /// Annualized return (10y) + annualized_return_ten: String, + /// Annualized return (3y) + annualized_return_three: String, + /// Annualized return (2y) + annualized_return_two: String, + /// Fund counter id + counter_id: String, + /// Fund name + fund_name: String, + /// Return (5y) + performance_return_five_years: String, + /// Return (1d) + performance_return_one_day: String, + /// Return (1m) + performance_return_one_month: String, + /// Return (1w) + performance_return_one_week: String, + /// Return (1y) + performance_return_one_year: String, + /// Return (6m) + performance_return_six_months: String, + /// Return (10y) + performance_return_ten_years: String, + /// Return (3m) + performance_return_three_months: String, + /// Return (3y) + performance_return_three_years: String, + /// Return (2y) + performance_return_two_years: String, + /// Return (ytd) + performance_return_ytd: String, + /// Update time (unix seconds) + update_time: i64, +} + +/// The user's cumulative profit for a held fund. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositionProfits")] +pub struct FundPositionProfits { + /// Currency + currency: String, + /// Profit series + #[js(array)] + history_value: Vec, + /// Last update time (unix seconds) + last_update_time: i64, + /// Total profit + sum_profit: String, +} + +/// A held-fund net-value point (position view). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundPositionNav")] +pub struct FundPositionNav { + /// Net value change + change: String, + /// Net value change percent + change_percent: String, + /// Fund counter id + counter_id: String, + /// Fund name + counter_name: String, + /// Last update time (unix seconds) + last_update_time: i64, + /// Net value + value: String, +} + +/// A cash dividend record for a held fund. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundDividend")] +pub struct FundDividend { + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Date (unix seconds) + date: i64, + /// Dividend method + div_method: String, + /// Fund name + name: String, +} + +/// The user's dividend records for a held fund. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundDividends")] +pub struct FundDividends { + /// Currency + currency: String, + /// Dividend records + #[js(array)] + div_cash_infos: Vec, + /// Latest dividend date (unix seconds) + lastest_date: i64, + /// Total cash dividend + total_div_cash: String, +} + +/// A fund order (list view). +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrder")] +pub struct FundOrder { + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Fund name + fund_name: String, + /// Order id + id: i64, + /// Whether it is an auto (DCA) order + is_auto: bool, + /// Net worth + net_worth: String, + /// Product type + product_type: String, + /// State + state: String, + /// State description + state_desc: String, + /// Units + units: String, +} + +/// A keyword block in a fund order detail. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderKeyword")] +pub struct FundOrderKeyword { + /// Content + content: String, + /// Group + group: String, + /// Key + key: String, + /// Line strategy + line_strategy: String, + /// Title + title: String, +} + +/// A processing stage in a fund order detail. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderStage")] +pub struct FundOrderStage { + /// Description + desc: String, + /// Key + key: String, + /// Link + link: String, + /// Link text + link_text: String, + /// Progress + progress: String, + /// Stage + stage: String, +} + +/// The full information of a fund order. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderInfo")] +pub struct FundOrderInfo { + /// Account id + aaid: i64, + /// Account channel + account_channel: String, + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Channel + channel: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Dividend option + dividend_option: String, + /// Equity time (unix seconds) + eq_at: i64, + /// Fee + fee: String, + /// Fund name + fund_name: String, + /// Fund source + fund_source: String, + /// Histories + histories: String, + /// Order id + id: i64, + /// Message + message: String, + /// Net worth + net_worth: String, + /// Price time (unix seconds) + price_at: i64, + /// Processed at (unix seconds) + processed_at: i64, + /// Product type + product_type: String, + /// Whether repurchaseable + repurchaseable: bool, + /// Sale proceeds + sale_proceeds: String, + /// Sales charge + sales_charge: String, + /// Sales price + sales_price: String, + /// Sales unit + sales_unit: String, + /// State + state: String, + /// State description + state_desc: String, + /// Status + status: i32, + /// Extended status + status_ex: i32, + /// T+ description + t_description: String, + /// Time partition + time_partition: String, + /// Total amount + total_amount: String, + /// Transaction at (unix seconds) + transaction_at: i64, + /// Units + units: String, + /// Withdraw at (unix seconds) + withdraw_at: i64, + /// Whether withdrawable + withdrawable: bool, +} + +/// Fund order detail. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderDetail")] +pub struct FundOrderDetail { + /// Keyword blocks + #[js(array)] + keywords: Vec, + /// The order + #[js(opt)] + order: Option, + /// Processing stages + #[js(array)] + stages: Vec, +} + +/// A fund transaction / cash-flow record. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundTransaction")] +pub struct FundTransaction { + /// Amount + amount: String, + /// Category + category: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Description + description: String, + /// Detail created at (unix seconds) + detail_created_at: i64, + /// Detail type + detail_type: String, + /// Done at (unix seconds) + done_at: i64, + /// Quantity description + quantity_description: String, + /// Redirect page + redirect_page: String, + /// Redirect page (v2) + redirect_page_v2: String, + /// Reference number + ref_no: String, + /// Stock quantity + stock_quantity: String, + /// Transaction type + tx_type: String, + /// Type name + type_name: String, +} + +/// The result of validating a fund order. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderValidation")] +pub struct FundOrderValidation { + /// Auth token to carry into submit + auth_token: String, + /// Risk-assessment eval address + eval_address: String, + /// Fund risk level + fund_risk_level: i32, + /// Message + msg: String, + /// User PI status + user_pi: i32, + /// User risk level + user_risk_level: i32, +} + +/// The result of submitting a fund order. +#[napi_derive::napi] +#[derive(Debug, JsObject, Clone)] +#[js(remote = "longbridge::fund::FundOrderSubmitResponse")] +pub struct FundOrderSubmitResponse { + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Fund name + fund_name: String, + /// Order id + id: i64, + /// Message + msg: String, + /// Status + status: i32, + /// Units + units: String, +} + +// --------------------------------------------------------------------------- +// Hand-written types. +// +// These carry server-defined ("any") JSON structures that the SDK models as +// `serde_json::Value`. The binding exposes each such value as a raw JSON +// `string` (and lists of them as `Array`), so they cannot use the +// `JsObject` derive (which relies on `TryInto` per field). +// --------------------------------------------------------------------------- + +/// Fund list filter options. Each list holds server-defined option objects +/// serialized as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundFilters { + asset_class: Vec, + company: Vec, + currency: Vec, + industry_category_name: Vec, + risk_level: Vec, +} + +impl ::std::convert::TryFrom for FundFilters { + type Error = ::napi::Error; + + fn try_from(v: longbridge::fund::FundFilters) -> ::std::result::Result { + Ok(Self { + asset_class: values_to_json_strings(v.asset_class), + company: values_to_json_strings(v.company), + currency: values_to_json_strings(v.currency), + industry_category_name: values_to_json_strings(v.industry_category_name), + risk_level: values_to_json_strings(v.risk_level), + }) + } +} + +#[napi_derive::napi] +impl FundFilters { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Asset class options (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn asset_class(&self) -> Vec { + self.asset_class.clone() + } + + /// Company options (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn company(&self) -> Vec { + self.company.clone() + } + + /// Currency options (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn currency(&self) -> Vec { + self.currency.clone() + } + + /// Industry category options (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn industry_category_name(&self) -> Vec { + self.industry_category_name.clone() + } + + /// Risk level options (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn risk_level(&self) -> Vec { + self.risk_level.clone() + } +} + +impl ToJSON for FundFilters { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("assetClass".to_string(), self.asset_class.to_json()), + ("company".to_string(), self.company.to_json()), + ("currency".to_string(), self.currency.to_json()), + ( + "industryCategoryName".to_string(), + self.industry_category_name.to_json(), + ), + ("riskLevel".to_string(), self.risk_level.to_json()), + ] + .into_iter() + .collect(), + ) + } +} + +/// Fund analysis (level 1). The ability/cost breakdowns are server-defined +/// structures exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundAnalysis { + actual_period: i32, + cost_level: String, + return_ability: String, + risk_ability: String, + updated_at: String, + value_for_money: String, + visible: bool, +} + +impl ::std::convert::TryFrom for FundAnalysis { + type Error = ::napi::Error; + + fn try_from(v: longbridge::fund::FundAnalysis) -> ::std::result::Result { + Ok(Self { + actual_period: v.actual_period, + cost_level: value_to_json_string(v.cost_level), + return_ability: value_to_json_string(v.return_ability), + risk_ability: value_to_json_string(v.risk_ability), + updated_at: v.updated_at, + value_for_money: value_to_json_string(v.value_for_money), + visible: v.visible, + }) + } +} + +#[napi_derive::napi] +impl FundAnalysis { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Actual period + #[napi(getter)] + #[inline] + pub fn actual_period(&self) -> i32 { + self.actual_period + } + + /// Cost level (raw JSON string) + #[napi(getter)] + #[inline] + pub fn cost_level(&self) -> String { + self.cost_level.clone() + } + + /// Return ability (raw JSON string) + #[napi(getter)] + #[inline] + pub fn return_ability(&self) -> String { + self.return_ability.clone() + } + + /// Risk ability (raw JSON string) + #[napi(getter)] + #[inline] + pub fn risk_ability(&self) -> String { + self.risk_ability.clone() + } + + /// Updated at + #[napi(getter)] + #[inline] + pub fn updated_at(&self) -> String { + self.updated_at.clone() + } + + /// Value for money (raw JSON string) + #[napi(getter)] + #[inline] + pub fn value_for_money(&self) -> String { + self.value_for_money.clone() + } + + /// Whether visible + #[napi(getter)] + #[inline] + pub fn visible(&self) -> bool { + self.visible + } +} + +impl ToJSON for FundAnalysis { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("actualPeriod".to_string(), self.actual_period.to_json()), + ("costLevel".to_string(), self.cost_level.to_json()), + ("returnAbility".to_string(), self.return_ability.to_json()), + ("riskAbility".to_string(), self.risk_ability.to_json()), + ("updatedAt".to_string(), self.updated_at.to_json()), + ("valueForMoney".to_string(), self.value_for_money.to_json()), + ("visible".to_string(), self.visible.to_json()), + ] + .into_iter() + .collect(), + ) + } +} + +/// Fund analysis detail (level 2). The ability/cost breakdowns are +/// server-defined structures exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundAnalysisDetail { + actual_period: i32, + available_periods: Vec, + cost_level: String, + return_ability: String, + risk_ability: String, + updated_at: String, + value_for_money: String, + visible: bool, +} + +impl ::std::convert::TryFrom for FundAnalysisDetail { + type Error = ::napi::Error; + + fn try_from( + v: longbridge::fund::FundAnalysisDetail, + ) -> ::std::result::Result { + Ok(Self { + actual_period: v.actual_period, + available_periods: v.available_periods, + cost_level: value_to_json_string(v.cost_level), + return_ability: value_to_json_string(v.return_ability), + risk_ability: value_to_json_string(v.risk_ability), + updated_at: v.updated_at, + value_for_money: value_to_json_string(v.value_for_money), + visible: v.visible, + }) + } +} + +#[napi_derive::napi] +impl FundAnalysisDetail { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Actual period + #[napi(getter)] + #[inline] + pub fn actual_period(&self) -> i32 { + self.actual_period + } + + /// Available periods + #[napi(getter)] + #[inline] + pub fn available_periods(&self) -> Vec { + self.available_periods.clone() + } + + /// Cost level (raw JSON string) + #[napi(getter)] + #[inline] + pub fn cost_level(&self) -> String { + self.cost_level.clone() + } + + /// Return ability (raw JSON string) + #[napi(getter)] + #[inline] + pub fn return_ability(&self) -> String { + self.return_ability.clone() + } + + /// Risk ability (raw JSON string) + #[napi(getter)] + #[inline] + pub fn risk_ability(&self) -> String { + self.risk_ability.clone() + } + + /// Updated at + #[napi(getter)] + #[inline] + pub fn updated_at(&self) -> String { + self.updated_at.clone() + } + + /// Value for money (raw JSON string) + #[napi(getter)] + #[inline] + pub fn value_for_money(&self) -> String { + self.value_for_money.clone() + } + + /// Whether visible + #[napi(getter)] + #[inline] + pub fn visible(&self) -> bool { + self.visible + } +} + +impl ToJSON for FundAnalysisDetail { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("actualPeriod".to_string(), self.actual_period.to_json()), + ( + "availablePeriods".to_string(), + self.available_periods.to_json(), + ), + ("costLevel".to_string(), self.cost_level.to_json()), + ("returnAbility".to_string(), self.return_ability.to_json()), + ("riskAbility".to_string(), self.risk_ability.to_json()), + ("updatedAt".to_string(), self.updated_at.to_json()), + ("valueForMoney".to_string(), self.value_for_money.to_json()), + ("visible".to_string(), self.visible.to_json()), + ] + .into_iter() + .collect(), + ) + } +} + +/// A benchmark contrast series in a fund trend chart. The performance points +/// are server-defined structures exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundTrendContrast { + benchmark_name: String, + performances: Vec, +} + +impl ::std::convert::From for FundTrendContrast { + fn from(v: longbridge::fund::FundTrendContrast) -> Self { + Self { + benchmark_name: v.benchmark_name, + performances: values_to_json_strings(v.performances), + } + } +} + +#[napi_derive::napi] +impl FundTrendContrast { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Benchmark name + #[napi(getter)] + #[inline] + pub fn benchmark_name(&self) -> String { + self.benchmark_name.clone() + } + + /// Performance points (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn performances(&self) -> Vec { + self.performances.clone() + } +} + +impl ToJSON for FundTrendContrast { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("benchmarkName".to_string(), self.benchmark_name.to_json()), + ("performances".to_string(), self.performances.to_json()), + ] + .into_iter() + .collect(), + ) + } +} + +/// Fund trend chart. The performance series are server-defined structures +/// exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundTrend { + actual_period: i32, + available_periods: Vec, + category_average_performances: Vec, + contrast_performances: Option, + fund_performances: Vec, +} + +impl ::std::convert::TryFrom for FundTrend { + type Error = ::napi::Error; + + fn try_from(v: longbridge::fund::FundTrend) -> ::std::result::Result { + Ok(Self { + actual_period: v.actual_period, + available_periods: v.available_periods, + category_average_performances: values_to_json_strings(v.category_average_performances), + contrast_performances: v.contrast_performances.map(Into::into), + fund_performances: values_to_json_strings(v.fund_performances), + }) + } +} + +#[napi_derive::napi] +impl FundTrend { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Actual period + #[napi(getter)] + #[inline] + pub fn actual_period(&self) -> i32 { + self.actual_period + } + + /// Available periods + #[napi(getter)] + #[inline] + pub fn available_periods(&self) -> Vec { + self.available_periods.clone() + } + + /// Category average performances (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn category_average_performances(&self) -> Vec { + self.category_average_performances.clone() + } + + /// Benchmark contrast performances + #[napi(getter)] + #[inline] + pub fn contrast_performances(&self) -> Option { + self.contrast_performances.clone() + } + + /// Fund performances (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn fund_performances(&self) -> Vec { + self.fund_performances.clone() + } +} + +impl ToJSON for FundTrend { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("actualPeriod".to_string(), self.actual_period.to_json()), + ( + "availablePeriods".to_string(), + self.available_periods.to_json(), + ), + ( + "categoryAveragePerformances".to_string(), + self.category_average_performances.to_json(), + ), + ( + "contrastPerformances".to_string(), + self.contrast_performances.to_json(), + ), + ( + "fundPerformances".to_string(), + self.fund_performances.to_json(), + ), + ] + .into_iter() + .collect(), + ) + } +} + +/// A named contrast performance series. The performance points are +/// server-defined structures exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundNamedContrast { + name: String, + performances: Vec, +} + +impl ::std::convert::From for FundNamedContrast { + fn from(v: longbridge::fund::FundNamedContrast) -> Self { + Self { + name: v.name, + performances: values_to_json_strings(v.performances), + } + } +} + +#[napi_derive::napi] +impl FundNamedContrast { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Series name + #[napi(getter)] + #[inline] + pub fn name(&self) -> String { + self.name.clone() + } + + /// Performance points (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn performances(&self) -> Vec { + self.performances.clone() + } +} + +impl ToJSON for FundNamedContrast { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ("name".to_string(), self.name.to_json()), + ("performances".to_string(), self.performances.to_json()), + ] + .into_iter() + .collect(), + ) + } +} + +/// Fund performance comparison. The fund performance series is a +/// server-defined structure exposed as raw JSON strings. +#[napi_derive::napi] +#[derive(Debug, Clone)] +pub struct FundPerformanceComparison { + contrast_performances: Vec, + fund_performances: Vec, +} + +impl ::std::convert::TryFrom + for FundPerformanceComparison +{ + type Error = ::napi::Error; + + fn try_from( + v: longbridge::fund::FundPerformanceComparison, + ) -> ::std::result::Result { + Ok(Self { + contrast_performances: v + .contrast_performances + .into_iter() + .map(FundNamedContrast::from) + .collect(), + fund_performances: values_to_json_strings(v.fund_performances), + }) + } +} + +#[napi_derive::napi] +impl FundPerformanceComparison { + #[napi] + pub fn to_string(&self) -> String { + ::std::format!("{:?}", self) + } + + #[napi(js_name = "toJSON")] + pub fn to_json(&self) -> serde_json::Value { + ::to_json(self) + } + + /// Contrast performance series + #[napi(getter)] + #[inline] + pub fn contrast_performances(&self) -> Vec { + self.contrast_performances.clone() + } + + /// Fund performances (each a raw JSON string) + #[napi(getter)] + #[inline] + pub fn fund_performances(&self) -> Vec { + self.fund_performances.clone() + } +} + +impl ToJSON for FundPerformanceComparison { + fn to_json(&self) -> serde_json::Value { + serde_json::Value::Object( + [ + ( + "contrastPerformances".to_string(), + self.contrast_performances.to_json(), + ), + ( + "fundPerformances".to_string(), + self.fund_performances.to_json(), + ), + ] + .into_iter() + .collect(), + ) + } +} diff --git a/nodejs/src/lib.rs b/nodejs/src/lib.rs index bec5c382b..3b09633e1 100644 --- a/nodejs/src/lib.rs +++ b/nodejs/src/lib.rs @@ -9,6 +9,7 @@ mod content; mod dca; mod decimal; mod error; +mod fund; mod fundamental; mod grid; mod http_client; diff --git a/nodejs/src/trade/types.rs b/nodejs/src/trade/types.rs index ccaf76ac8..87739a385 100644 --- a/nodejs/src/trade/types.rs +++ b/nodejs/src/trade/types.rs @@ -898,8 +898,8 @@ pub struct FundPositionChannel { #[derive(Debug, JsObject, Clone)] #[js(remote = "longbridge::trade::FundPosition")] pub struct FundPosition { - /// Fund ISIN code - symbol: String, + /// Fund counter id (the ISIN is the last `/`-separated segment) + counter_id: String, /// Current equity current_net_asset_value: Decimal, /// Current equity time diff --git a/python/pysrc/longbridge/openapi.pyi b/python/pysrc/longbridge/openapi.pyi index 90721a89a..aa4b7708a 100644 --- a/python/pysrc/longbridge/openapi.pyi +++ b/python/pysrc/longbridge/openapi.pyi @@ -7218,9 +7218,9 @@ class FundPosition: Fund position """ - symbol: str + counter_id: str """ - Fund ISIN code + Fund counter id (the ISIN is the last ``/``-separated segment) """ current_net_asset_value: Decimal @@ -9015,6 +9015,2207 @@ class GridContext: Grid symbol info """ +class FundNavValue: + """ + A fund net-asset-value data point (latest / historical) + """ + + change: str + """ + Net value change + """ + change_percent: str + """ + Net value change percent + """ + change_percent_format: str + """ + Formatted change percent + """ + counter_id: str + """ + Fund counter id + """ + counter_name: str + """ + Fund name + """ + currency: str + """ + Currency + """ + date_format: str + """ + Formatted date + """ + isin: str + """ + ISIN + """ + last_update_time: int + """ + Last update time (unix seconds) + """ + value: str + """ + Net value + """ + value_format: str + """ + Formatted net value + """ + +class FundPerformancePoint: + """ + A recent performance point used by the hot-fund list + """ + + date: str + """ + Date + """ + last_done: str + """ + Last done value + """ + +class HotFund: + """ + A hot-selling fund entry + """ + + asset_class: int + """ + Asset class + """ + asset_class_name: str + """ + Asset class name + """ + counter_id: str + """ + Fund counter id + """ + currency: str + """ + Currency + """ + earning_rate: str + """ + Earning rate + """ + fund_performances: List[FundPerformancePoint] + """ + Recent performance points + """ + name: str + """ + Fund name + """ + purchase_amount: str + """ + Minimum purchase amount + """ + recommendation_text: str + """ + Recommendation text + """ + risk_level: int + """ + Risk level + """ + risk_level_name: str + """ + Risk level name + """ + time_interval: str + """ + Time interval of the earning rate + """ + +class FundBrief: + """ + A fund entry in the fund list + """ + + asset_class: int + """ + Asset class + """ + asset_class_name: str + """ + Asset class name + """ + code: str + """ + Fund code + """ + counter_id: str + """ + Fund counter id + """ + currency: str + """ + Currency + """ + description: str + """ + Description + """ + earning_rate: str + """ + Earning rate + """ + holding: bool + """ + Whether the user is holding this fund + """ + isin: str + """ + ISIN + """ + name: str + """ + Fund name + """ + product: str + """ + Product + """ + purchase_amount: str + """ + Minimum purchase amount + """ + recommendation_text: str + """ + Recommendation text + """ + risk_level: int + """ + Risk level + """ + risk_level_name: str + """ + Risk level name + """ + time_interval: str + """ + Time interval of the earning rate + """ + unit_value: str + """ + Unit value + """ + +class FundFilters: + """ + Fund list filter options. Each list holds server-defined option objects + exposed as raw JSON strings. + """ + + asset_class: List[str] + """ + Asset class options (raw JSON strings) + """ + company: List[str] + """ + Company options (raw JSON strings) + """ + currency: List[str] + """ + Currency options (raw JSON strings) + """ + industry_category_name: List[str] + """ + Industry category options (raw JSON strings) + """ + risk_level: List[str] + """ + Risk level options (raw JSON strings) + """ + +class FundAssetAllocationItem: + """ + A single holding entry inside a fund's asset allocation + """ + + code: str + """ + Security code + """ + counter_id: str + """ + Security counter id + """ + name: str + """ + Name + """ + position_ratio: str + """ + Position ratio + """ + +class FundAssetAllocation: + """ + A fund's asset allocation + """ + + asset_type: int + """ + Asset type + """ + lists: List[FundAssetAllocationItem] + """ + Allocation entries + """ + report_date: str + """ + Report date + """ + +class FundDetail: + """ + Fund detail + """ + + additional_purchase_amount: str + """ + Additional purchase amount + """ + affirm_day: int + """ + Affirm day + """ + amount_affirm_day: str + """ + Amount affirm day + """ + asset_allocation: Optional[FundAssetAllocation] + """ + Asset allocation + """ + asset_class: int + """ + Asset class + """ + asset_class_name: str + """ + Asset class name + """ + bill_purchase_rate: str + """ + Bill purchase rate + """ + channel: str + """ + Channel + """ + close_period: str + """ + Close period + """ + code: str + """ + Fund code + """ + currency: str + """ + Currency + """ + cut_off_time: str + """ + Cut off time + """ + derivatives: bool + """ + Whether it is a derivative + """ + done_day: int + """ + Done day + """ + excess_return_fee: str + """ + Excess return fee + """ + gst_rate: str + """ + GST rate + """ + introduce: str + """ + Introduction + """ + is_cash_plus: bool + """ + Whether it is a cash-plus fund + """ + is_complex: bool + """ + Whether it is a complex product + """ + is_new_cash_plus: bool + """ + Whether it is a new cash-plus fund + """ + is_yinghebao: bool + """ + Whether it is a Yinghebao fund + """ + isin: str + """ + ISIN + """ + manage_rate: str + """ + Management rate + """ + manager: str + """ + Manager + """ + min_hold_cash: str + """ + Minimum holding cash + """ + min_hold_share: str + """ + Minimum holding share + """ + min_sell_share: str + """ + Minimum sell share + """ + month_raise_day: str + """ + Month raise day + """ + name: str + """ + Fund name + """ + nav_deadline: str + """ + Net value deadline + """ + no_load: bool + """ + Whether it is no-load + """ + open_date: str + """ + Open date + """ + open_period: str + """ + Open period + """ + product: str + """ + Product + """ + product_information_locals: str + """ + Product information locals + """ + profile: str + """ + Profile + """ + purchasable: int + """ + Whether purchasable + """ + purchase_affirm_day: str + """ + Purchase affirm day + """ + purchase_amount: str + """ + Minimum purchase amount + """ + purchase_rate: str + """ + Purchase rate + """ + rating: int + """ + Rating + """ + redeemable: int + """ + Whether redeemable + """ + redemption_advance_day: str + """ + Redemption advance day + """ + redemption_amount: str + """ + Redemption amount + """ + redemption_close_period_shows: str + """ + Redemption close period text + """ + redemption_done_day: str + """ + Redemption done day + """ + redemption_open_day_shows: str + """ + Redemption open day text + """ + risk_level: int + """ + Risk level + """ + risk_level_name: str + """ + Risk level name + """ + verify_status: int + """ + Verify status + """ + virtual_currency: bool + """ + Whether it is a virtual currency fund + """ + year_to_date_yield: str + """ + Year to date yield + """ + ytd_yield_type: int + """ + Year to date yield type + """ + +class FundAnalysis: + """ + Fund analysis (level 1) + """ + + actual_period: int + """ + Actual period + """ + cost_level: str + """ + Cost level (server-defined structure, raw JSON string) + """ + return_ability: str + """ + Return ability (server-defined structure, raw JSON string) + """ + risk_ability: str + """ + Risk ability (server-defined structure, raw JSON string) + """ + updated_at: str + """ + Updated at + """ + value_for_money: str + """ + Value for money (server-defined structure, raw JSON string) + """ + visible: bool + """ + Whether visible + """ + +class FundAnalysisDetail: + """ + Fund analysis detail (level 2) + """ + + actual_period: int + """ + Actual period + """ + available_periods: List[int] + """ + Available periods + """ + cost_level: str + """ + Cost level (server-defined structure, raw JSON string) + """ + return_ability: str + """ + Return ability (server-defined structure, raw JSON string) + """ + risk_ability: str + """ + Risk ability (server-defined structure, raw JSON string) + """ + updated_at: str + """ + Updated at + """ + value_for_money: str + """ + Value for money (server-defined structure, raw JSON string) + """ + visible: bool + """ + Whether visible + """ + +class FundTrendContrast: + """ + A benchmark contrast series in a fund trend chart + """ + + benchmark_name: str + """ + Benchmark name + """ + performances: List[str] + """ + Performance points (server-defined structure, raw JSON strings) + """ + +class FundTrend: + """ + Fund trend chart + """ + + actual_period: int + """ + Actual period + """ + available_periods: List[int] + """ + Available periods + """ + category_average_performances: List[str] + """ + Category average performances (server-defined structure, raw JSON strings) + """ + contrast_performances: Optional[FundTrendContrast] + """ + Benchmark contrast performances + """ + fund_performances: List[str] + """ + Fund performances (server-defined structure, raw JSON strings) + """ + +class FundNamedContrast: + """ + A named contrast performance series + """ + + name: str + """ + Series name + """ + performances: List[str] + """ + Performance points (server-defined structure, raw JSON strings) + """ + +class FundPerformanceComparison: + """ + Fund performance comparison + """ + + contrast_performances: List[FundNamedContrast] + """ + Contrast performance series + """ + fund_performances: List[str] + """ + Fund performances (server-defined structure, raw JSON strings) + """ + +class FundAnnualReturn: + """ + A fund annual return entry + """ + + change_percent: str + """ + Change percent + """ + year: int + """ + Year + """ + +class FundQuarterlyReturn: + """ + A fund quarterly return entry + """ + + change_percent: str + """ + Change percent + """ + quarter: int + """ + Quarter + """ + year: int + """ + Year + """ + +class FundPerformance: + """ + A fund's detailed performance figures + """ + + annualized_return_five: str + """ + Annualized return (5y) + """ + annualized_return_one: str + """ + Annualized return (1y) + """ + annualized_return_ten: str + """ + Annualized return (10y) + """ + annualized_return_three: str + """ + Annualized return (3y) + """ + annualized_return_two: str + """ + Annualized return (2y) + """ + counter_id: str + """ + Fund counter id + """ + fund_name: str + """ + Fund name + """ + performance_rank_five_years: int + """ + Rank (5y) + """ + performance_rank_one_day: int + """ + Rank (1d) + """ + performance_rank_one_month: int + """ + Rank (1m) + """ + performance_rank_one_week: int + """ + Rank (1w) + """ + performance_rank_one_year: int + """ + Rank (1y) + """ + performance_rank_six_months: int + """ + Rank (6m) + """ + performance_rank_ten_years: int + """ + Rank (10y) + """ + performance_rank_three_months: int + """ + Rank (3m) + """ + performance_rank_three_years: int + """ + Rank (3y) + """ + performance_rank_two_years: int + """ + Rank (2y) + """ + performance_rank_ytd: int + """ + Rank (ytd) + """ + performance_return_five_years: str + """ + Return (5y) + """ + performance_return_one_day: str + """ + Return (1d) + """ + performance_return_one_month: str + """ + Return (1m) + """ + performance_return_one_week: str + """ + Return (1w) + """ + performance_return_one_year: str + """ + Return (1y) + """ + performance_return_six_months: str + """ + Return (6m) + """ + performance_return_ten_years: str + """ + Return (10y) + """ + performance_return_three_months: str + """ + Return (3m) + """ + performance_return_three_years: str + """ + Return (3y) + """ + performance_return_two_years: str + """ + Return (2y) + """ + performance_return_ytd: str + """ + Return (ytd) + """ + performance_total_five_years: int + """ + Total peers (5y) + """ + performance_total_one_day: int + """ + Total peers (1d) + """ + performance_total_one_month: int + """ + Total peers (1m) + """ + performance_total_one_week: int + """ + Total peers (1w) + """ + performance_total_one_year: int + """ + Total peers (1y) + """ + performance_total_six_months: int + """ + Total peers (6m) + """ + performance_total_ten_years: int + """ + Total peers (10y) + """ + performance_total_three_months: int + """ + Total peers (3m) + """ + performance_total_three_years: int + """ + Total peers (3y) + """ + performance_total_two_years: int + """ + Total peers (2y) + """ + performance_total_ytd: int + """ + Total peers (ytd) + """ + seven_days_annualized: str + """ + Seven days annualized + """ + ten_thousand_price: str + """ + Ten thousand price + """ + update_time: int + """ + Update time (unix seconds) + """ + +class FundHolding: + """ + A single fund holding (top-10 holdings) + """ + + bond_type: str + """ + Bond type + """ + bond_type_name: str + """ + Bond type name + """ + country_name: str + """ + Country name + """ + holding_type: str + """ + Holding type + """ + industry_name: str + """ + Industry name + """ + market_value: str + """ + Market value + """ + maturity_date: str + """ + Maturity date + """ + name: str + """ + Name + """ + share_change: str + """ + Share change + """ + share_change_percent: str + """ + Share change percent + """ + shares: str + """ + Shares + """ + weighting: str + """ + Weighting + """ + +class FundHoldings: + """ + A fund's top-10 holdings + """ + + holdings: List[FundHolding] + """ + Holding entries + """ + report_date: str + """ + Report date + """ + weighting: str + """ + Total weighting + """ + +class FundStockHolding: + """ + A stock held by the fund (reverse lookup) + """ + + code: str + """ + Stock code + """ + counter_id: str + """ + Stock counter id + """ + currency: str + """ + Currency + """ + name: str + """ + Stock name + """ + position_ratio: str + """ + Position ratio + """ + report_date: str + """ + Report date + """ + +class FundHoldingPosition: + """ + A single fund position held by the user + + Named ``FundHoldingPosition`` to avoid a name clash with the trade + channel's existing ``FundPosition`` class. + """ + + amount: str + """ + Holding amount + """ + counter_id: str + """ + Fund counter id + """ + currency: str + """ + Currency + """ + freeze_units: str + """ + Frozen units + """ + holding_profit: str + """ + Holding profit + """ + holding_units: str + """ + Holding units + """ + name: str + """ + Fund name + """ + recent_profit: str + """ + Recent profit + """ + recent_trading_day: int + """ + Recent trading day (unix seconds) + """ + sum_recent_profit: str + """ + Accumulated recent profit + """ + +class FundPositions: + """ + The user's fund positions overview + """ + + account_channel: str + """ + Account channel + """ + list: List[FundHoldingPosition] + """ + Position entries + """ + pending_buy_orders: str + """ + Pending buy orders amount + """ + recent_trading_day: int + """ + Recent trading day (unix seconds) + """ + sold_pending_credit_orders: str + """ + Sold pending credit orders amount + """ + +class FundDatedValue: + """ + A dated value point + """ + + date: int + """ + Date (unix seconds) + """ + value: str + """ + Value + """ + +class FundUnitValue: + """ + A fund unit-value point (position view) + """ + + date: int + """ + Date (unix seconds) + """ + day_increase_rate: str + """ + Day increase rate + """ + total_value: str + """ + Total value + """ + unit_value: str + """ + Unit value + """ + +class FundPositionDetailValues: + """ + Detail values of a single fund position + """ + + amount: str + """ + Amount + """ + currency: str + """ + Currency + """ + holding_cost: str + """ + Holding cost + """ + holding_profit: str + """ + Holding profit + """ + holding_profit_rate: str + """ + Holding profit rate + """ + holding_units: str + """ + Holding units + """ + holding_value: str + """ + Holding value + """ + pending_buy_value: str + """ + Pending buy value + """ + pending_sell_value: str + """ + Pending sell value + """ + profit_amount_accum_td: str + """ + Accumulated profit (to date) + """ + profit_amount_accum_td_rate: str + """ + Accumulated profit rate (to date) + """ + recent_profit: str + """ + Recent profit + """ + recent_tradingday: int + """ + Recent trading day (unix seconds) + """ + recent_unit_value: str + """ + Recent unit value + """ + sold_pending_confirm_units: str + """ + Sold pending-confirm units + """ + +class FundPositionDetail: + """ + Detail of a single fund position + """ + + detail_values: Optional[FundPositionDetailValues] + """ + Detail values + """ + sum_profit: List[FundDatedValue] + """ + Accumulated profit series + """ + ut_value: List[FundUnitValue] + """ + Unit value series + """ + +class FundPositionPerformance: + """ + Performance figures for a held fund + """ + + annualized_return_five: str + """ + Annualized return (5y) + """ + annualized_return_one: str + """ + Annualized return (1y) + """ + annualized_return_ten: str + """ + Annualized return (10y) + """ + annualized_return_three: str + """ + Annualized return (3y) + """ + annualized_return_two: str + """ + Annualized return (2y) + """ + counter_id: str + """ + Fund counter id + """ + fund_name: str + """ + Fund name + """ + performance_return_five_years: str + """ + Return (5y) + """ + performance_return_one_day: str + """ + Return (1d) + """ + performance_return_one_month: str + """ + Return (1m) + """ + performance_return_one_week: str + """ + Return (1w) + """ + performance_return_one_year: str + """ + Return (1y) + """ + performance_return_six_months: str + """ + Return (6m) + """ + performance_return_ten_years: str + """ + Return (10y) + """ + performance_return_three_months: str + """ + Return (3m) + """ + performance_return_three_years: str + """ + Return (3y) + """ + performance_return_two_years: str + """ + Return (2y) + """ + performance_return_ytd: str + """ + Return (ytd) + """ + update_time: int + """ + Update time (unix seconds) + """ + +class FundPositionProfits: + """ + The user's cumulative profit for a held fund + """ + + currency: str + """ + Currency + """ + history_value: List[FundDatedValue] + """ + Profit series + """ + last_update_time: int + """ + Last update time (unix seconds) + """ + sum_profit: str + """ + Total profit + """ + +class FundPositionNav: + """ + A held-fund net-value point (position view) + """ + + change: str + """ + Net value change + """ + change_percent: str + """ + Net value change percent + """ + counter_id: str + """ + Fund counter id + """ + counter_name: str + """ + Fund name + """ + last_update_time: int + """ + Last update time (unix seconds) + """ + value: str + """ + Net value + """ + +class FundDividend: + """ + A cash dividend record for a held fund + """ + + amount: str + """ + Amount + """ + counter_id: str + """ + Fund counter id + """ + currency: str + """ + Currency + """ + date: int + """ + Date (unix seconds) + """ + div_method: str + """ + Dividend method + """ + name: str + """ + Fund name + """ + +class FundDividends: + """ + The user's dividend records for a held fund + """ + + currency: str + """ + Currency + """ + div_cash_infos: List[FundDividend] + """ + Dividend records + """ + lastest_date: int + """ + Latest dividend date (unix seconds) + """ + total_div_cash: str + """ + Total cash dividend + """ + +class FundOrder: + """ + A fund order (list view) + """ + + action: str + """ + Action (buy/sell) + """ + amount: str + """ + Amount + """ + counter_id: str + """ + Fund counter id + """ + created_at: int + """ + Created at (unix seconds) + """ + currency: str + """ + Currency + """ + fund_name: str + """ + Fund name + """ + id: int + """ + Order id + """ + is_auto: bool + """ + Whether it is an auto (DCA) order + """ + net_worth: str + """ + Net worth + """ + product_type: str + """ + Product type + """ + state: str + """ + State + """ + state_desc: str + """ + State description + """ + units: str + """ + Units + """ + +class FundOrderKeyword: + """ + A keyword block in a fund order detail + """ + + content: str + """ + Content + """ + group: str + """ + Group + """ + key: str + """ + Key + """ + line_strategy: str + """ + Line strategy + """ + title: str + """ + Title + """ + +class FundOrderStage: + """ + A processing stage in a fund order detail + """ + + desc: str + """ + Description + """ + key: str + """ + Key + """ + link: str + """ + Link + """ + link_text: str + """ + Link text + """ + progress: str + """ + Progress + """ + stage: str + """ + Stage + """ + +class FundOrderInfo: + """ + The full information of a fund order + """ + + aaid: int + """ + Account id + """ + account_channel: str + """ + Account channel + """ + action: str + """ + Action (buy/sell) + """ + amount: str + """ + Amount + """ + channel: str + """ + Channel + """ + counter_id: str + """ + Fund counter id + """ + created_at: int + """ + Created at (unix seconds) + """ + currency: str + """ + Currency + """ + dividend_option: str + """ + Dividend option + """ + eq_at: int + """ + Equity time (unix seconds) + """ + fee: str + """ + Fee + """ + fund_name: str + """ + Fund name + """ + fund_source: str + """ + Fund source + """ + histories: str + """ + Histories + """ + id: int + """ + Order id + """ + message: str + """ + Message + """ + net_worth: str + """ + Net worth + """ + price_at: int + """ + Price time (unix seconds) + """ + processed_at: int + """ + Processed at (unix seconds) + """ + product_type: str + """ + Product type + """ + repurchaseable: bool + """ + Whether repurchaseable + """ + sale_proceeds: str + """ + Sale proceeds + """ + sales_charge: str + """ + Sales charge + """ + sales_price: str + """ + Sales price + """ + sales_unit: str + """ + Sales unit + """ + state: str + """ + State + """ + state_desc: str + """ + State description + """ + status: int + """ + Status + """ + status_ex: int + """ + Extended status + """ + t_description: str + """ + T+ description + """ + time_partition: str + """ + Time partition + """ + total_amount: str + """ + Total amount + """ + transaction_at: int + """ + Transaction at (unix seconds) + """ + units: str + """ + Units + """ + withdraw_at: int + """ + Withdraw at (unix seconds) + """ + withdrawable: bool + """ + Whether withdrawable + """ + +class FundOrderDetail: + """ + Fund order detail + """ + + keywords: List[FundOrderKeyword] + """ + Keyword blocks + """ + order: Optional[FundOrderInfo] + """ + The order + """ + stages: List[FundOrderStage] + """ + Processing stages + """ + +class FundTransaction: + """ + A fund transaction / cash-flow record + """ + + amount: str + """ + Amount + """ + category: str + """ + Category + """ + created_at: int + """ + Created at (unix seconds) + """ + currency: str + """ + Currency + """ + description: str + """ + Description + """ + detail_created_at: int + """ + Detail created at (unix seconds) + """ + detail_type: str + """ + Detail type + """ + done_at: int + """ + Done at (unix seconds) + """ + quantity_description: str + """ + Quantity description + """ + redirect_page: str + """ + Redirect page + """ + redirect_page_v2: str + """ + Redirect page (v2) + """ + ref_no: str + """ + Reference number + """ + stock_quantity: str + """ + Stock quantity + """ + tx_type: str + """ + Transaction type + """ + type_name: str + """ + Type name + """ + +class FundOrderValidation: + """ + The result of validating a fund order + """ + + auth_token: str + """ + Auth token to carry into submit + """ + eval_address: str + """ + Risk-assessment eval address + """ + fund_risk_level: int + """ + Fund risk level + """ + msg: str + """ + Message + """ + user_pi: int + """ + User PI status + """ + user_risk_level: int + """ + User risk level + """ + +class FundOrderSubmitResponse: + """ + The result of submitting a fund order + """ + + action: str + """ + Action (buy/sell) + """ + amount: str + """ + Amount + """ + counter_id: str + """ + Fund counter id + """ + created_at: int + """ + Created at (unix seconds) + """ + fund_name: str + """ + Fund name + """ + id: int + """ + Order id + """ + msg: str + """ + Message + """ + status: int + """ + Status + """ + units: str + """ + Units + """ + +class FundContext: + """ + Fund (mutual fund) channel context (REST-only). + + Args: + config: Configuration object + """ + + def __init__(self, config: Config) -> None: ... + def hot_funds(self) -> List[HotFund]: + """ + Get the hot-selling fund list + + Returns: + Hot-selling fund list + """ + + def funds( + self, + filter: Optional[str] = None, + quick_ids: Optional[List[int]] = None, + time_interval: Optional[List[str]] = None, + ) -> List[FundBrief]: + """ + Get the fund list + + Args: + filter: Server-defined filter object encoded as a JSON string + quick_ids: Quick-filter ids + time_interval: Earning-rate time intervals + + Returns: + Fund list + """ + + def filters(self) -> FundFilters: + """ + Get the fund list filter options + + Returns: + Fund list filter options + """ + + def detail(self, counter_id: str) -> FundDetail: + """ + Get fund detail + + Args: + counter_id: Fund counter id + + Returns: + Fund detail + """ + + def analysis(self, counter_id: str, period: Optional[int] = None) -> FundAnalysis: + """ + Get fund analysis (level 1) + + Args: + counter_id: Fund counter id + period: Analysis period + + Returns: + Fund analysis + """ + + def analysis_detail( + self, counter_id: str, period: Optional[int] = None + ) -> FundAnalysisDetail: + """ + Get fund analysis detail (level 2) + + Args: + counter_id: Fund counter id + period: Analysis period + + Returns: + Fund analysis detail + """ + + def trend(self, counter_id: str, period: Optional[int] = None) -> FundTrend: + """ + Get fund trend chart + + Args: + counter_id: Fund counter id + period: Analysis period + + Returns: + Fund trend chart + """ + + def annual_returns( + self, counter_id: str, page: Optional[int] = None, size: Optional[int] = None + ) -> List[FundAnnualReturn]: + """ + Get fund annual returns + + Args: + counter_id: Fund counter id + page: Page number + size: Page size + + Returns: + Fund annual returns + """ + + def quarterly_returns( + self, counter_id: str, page: Optional[int] = None, size: Optional[int] = None + ) -> List[FundQuarterlyReturn]: + """ + Get fund quarterly returns + + Args: + counter_id: Fund counter id + page: Page number + size: Page size + + Returns: + Fund quarterly returns + """ + + def performance(self, counter_id: str) -> List[FundPerformance]: + """ + Get fund performance figures + + Args: + counter_id: Fund counter id + + Returns: + Fund performance figures + """ + + def performance_comparison( + self, counter_id: str, period: Optional[int] = None + ) -> FundPerformanceComparison: + """ + Get fund performance comparison + + Args: + counter_id: Fund counter id + period: Analysis period + + Returns: + Fund performance comparison + """ + + def nav(self, counter_id: str) -> List[FundNavValue]: + """ + Get fund latest net value + + Args: + counter_id: Fund counter id + + Returns: + Fund latest net value + """ + + def nav_history( + self, counter_id: str, page: Optional[int] = None, size: Optional[int] = None + ) -> List[FundNavValue]: + """ + Get fund historical net value (paged) + + Args: + counter_id: Fund counter id + page: Page number + size: Page size + + Returns: + Fund historical net value + """ + + def nav_range( + self, + counter_id: str, + month_before: Optional[int] = None, + year_before: Optional[int] = None, + ) -> List[FundNavValue]: + """ + Get fund historical net value by relative time range + + Args: + counter_id: Fund counter id + month_before: Number of months before now + year_before: Number of years before now + + Returns: + Fund historical net value + """ + + def holdings(self, counter_id: str, scene: Optional[int] = None) -> FundHoldings: + """ + Get a fund's top-10 holdings + + Args: + counter_id: Fund counter id + scene: Scene + + Returns: + Fund top-10 holdings + """ + + def stock_holdings( + self, counter_id: str, limit: Optional[int] = None + ) -> List[FundStockHolding]: + """ + Get the stocks held by a fund (reverse lookup) + + Args: + counter_id: Fund counter id + limit: Maximum number of stocks to return + + Returns: + Stocks held by the fund + """ + + def positions( + self, account_channel: Optional[str] = None, aaid: Optional[int] = None + ) -> FundPositions: + """ + Get the user's fund positions overview + + Args: + account_channel: Account channel + aaid: Account id + + Returns: + Fund positions overview + """ + + def position( + self, + counter_id: str, + account_channel: Optional[str] = None, + aaid: Optional[int] = None, + start: Optional[str] = None, + end: Optional[str] = None, + ) -> FundPositionDetail: + """ + Get the user's single fund position detail + + Args: + counter_id: Fund counter id + account_channel: Account channel + aaid: Account id + start: Range start + end: Range end + + Returns: + Fund position detail + """ + + def position_performance(self, counter_id: str) -> List[FundPositionPerformance]: + """ + Get the performance figures of a held fund + + Args: + counter_id: Fund counter id + + Returns: + Performance figures of the held fund + """ + + def position_profits( + self, + counter_id: str, + account_channel: Optional[str] = None, + aaid: Optional[int] = None, + start: Optional[str] = None, + end: Optional[str] = None, + page: Optional[int] = None, + size: Optional[int] = None, + ) -> FundPositionProfits: + """ + Get the cumulative-profit series of a held fund + + Args: + counter_id: Fund counter id + account_channel: Account channel + aaid: Account id + start: Range start + end: Range end + page: Page number + size: Page size + + Returns: + Cumulative-profit series of the held fund + """ + + def position_nav( + self, + counter_id: str, + month_before: Optional[int] = None, + year_before: Optional[int] = None, + ) -> List[FundPositionNav]: + """ + Get the net-value history of a held fund + + Args: + counter_id: Fund counter id + month_before: Number of months before now + year_before: Number of years before now + + Returns: + Net-value history of the held fund + """ + + def position_dividends( + self, + counter_id: str, + account_channel: Optional[str] = None, + aaid: Optional[int] = None, + currency: Optional[str] = None, + start: Optional[int] = None, + end: Optional[int] = None, + page: Optional[int] = None, + size: Optional[int] = None, + ) -> FundDividends: + """ + Get the dividend records of a held fund + + Args: + counter_id: Fund counter id + account_channel: Account channel + aaid: Account id + currency: Currency + start: Range start (unix seconds) + end: Range end (unix seconds) + page: Page number + size: Page size + + Returns: + Dividend records of the held fund + """ + + def orders( + self, + counter_ids: Optional[List[str]] = None, + actions: Optional[str] = None, + states: Optional[str] = None, + currency: Optional[str] = None, + start: Optional[int] = None, + end: Optional[int] = None, + page: Optional[int] = None, + size: Optional[int] = None, + ) -> List[FundOrder]: + """ + Get the user's fund orders (also serves as the trade/execution record) + + Args: + counter_ids: Filter by fund counter ids + actions: Filter by actions (comma-separated) + states: Filter by states (comma-separated) + currency: Filter by currency + start: Range start (unix seconds) + end: Range end (unix seconds) + page: Page number + size: Page size + + Returns: + Fund orders + """ + + def order(self, order_id: int) -> FundOrderDetail: + """ + Get a fund order detail + + Args: + order_id: Order id + + Returns: + Fund order detail + """ + + def transactions( + self, + account_channel: Optional[str] = None, + business_type: Optional[str] = None, + category: Optional[str] = None, + currencies: Optional[str] = None, + start: Optional[int] = None, + end: Optional[int] = None, + page: Optional[int] = None, + size: Optional[int] = None, + ) -> List[FundTransaction]: + """ + Get the user's fund transactions (cash-flow records) + + Args: + account_channel: Account channel + business_type: Business type + category: Category + currencies: Currencies (comma-separated) + start: Range start (unix seconds) + end: Range end (unix seconds) + page: Page number + size: Page size + + Returns: + Fund transactions + """ + + def validate_order( + self, + counter_id: str, + action: str, + currency: str, + amount: Optional[str] = None, + units: Optional[str] = None, + dividend_option: Optional[int] = None, + fund_source: Optional[int] = None, + account_channel: Optional[str] = None, + ) -> FundOrderValidation: + """ + Validate a fund order before submitting + + Args: + counter_id: Fund counter id + action: Action (buy/sell) + currency: Currency + amount: Amount (for amount-based orders) + units: Units (for unit-based orders) + dividend_option: Dividend option + fund_source: Fund source + account_channel: Account channel + + Returns: + Fund order validation result + """ + + def submit_order( + self, + counter_id: str, + action: str, + currency: str, + amount: Optional[str] = None, + units: Optional[str] = None, + dividend_option: Optional[int] = None, + fee: Optional[str] = None, + is_sell_all: Optional[bool] = None, + remark: Optional[str] = None, + trade_method: Optional[int] = None, + ) -> FundOrderSubmitResponse: + """ + Submit a fund order (buy / sell) + + Args: + counter_id: Fund counter id + action: Action (buy/sell) + currency: Currency + amount: Amount (for amount-based orders) + units: Units (for unit-based orders) + dividend_option: Dividend option + fee: Fee + is_sell_all: Whether to sell all + remark: Remark + trade_method: Trade method + + Returns: + Fund order submit result + """ + + def cancel_order(self, order_id: int) -> None: + """ + Cancel (withdraw) a fund order + + Args: + order_id: Order id + """ + class AsyncTradeContext: """ Async trade context for use with asyncio. Create via `AsyncTradeContext.create(config)` and await inside asyncio. diff --git a/python/src/fund/context.rs b/python/src/fund/context.rs new file mode 100644 index 000000000..259bad57d --- /dev/null +++ b/python/src/fund/context.rs @@ -0,0 +1,641 @@ +use std::sync::Arc; + +use longbridge::{ + blocking::FundContextSync, + fund::{ + FundNavRangeOptions, FundPageOptions, GetFundAnalysisOptions, GetFundHoldingsOptions, + GetFundOrdersOptions, GetFundPositionDividendsOptions, GetFundPositionOptions, + GetFundPositionProfitsOptions, GetFundPositionsOptions, GetFundStockHoldingsOptions, + GetFundTransactionsOptions, GetFundsOptions, SubmitFundOrderOptions, + ValidateFundOrderOptions, + }, +}; +use pyo3::{PyResult, exceptions::PyValueError, pyclass, pymethods}; + +use crate::{ + config::Config, + error::ErrorNewType, + fund::types::{ + FundAnalysis, FundAnalysisDetail, FundAnnualReturn, FundBrief, FundDetail, FundDividends, + FundFilters, FundHoldings, FundNavValue, FundOrder, FundOrderDetail, + FundOrderSubmitResponse, FundOrderValidation, FundPerformance, FundPerformanceComparison, + FundPositionDetail, FundPositionNav, FundPositionPerformance, FundPositionProfits, + FundPositions, FundQuarterlyReturn, FundStockHolding, FundTransaction, FundTrend, HotFund, + }, +}; + +/// Fund (mutual fund) channel context (REST-only). +#[pyclass] +pub(crate) struct FundContext { + ctx: FundContextSync, +} + +#[pymethods] +impl FundContext { + #[new] + fn new(config: &Config) -> PyResult { + Ok(Self { + ctx: FundContextSync::new(Arc::new(config.0.clone())).map_err(ErrorNewType)?, + }) + } + + // ----- fund catalog / market data (scope: quote) ----- + + /// Get the hot-selling fund list + fn hot_funds(&self) -> PyResult> { + self.ctx + .hot_funds() + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the fund list + #[pyo3(signature = (filter = None, quick_ids = None, time_interval = None))] + fn funds( + &self, + filter: Option, + quick_ids: Option>, + time_interval: Option>, + ) -> PyResult> { + let mut opts = GetFundsOptions::new(); + if let Some(filter) = filter { + let value: serde_json::Value = serde_json::from_str(&filter) + .map_err(|err| PyValueError::new_err(format!("invalid filter JSON: {err}")))?; + opts = opts.filter(value); + } + if let Some(quick_ids) = quick_ids { + opts = opts.quick_ids(quick_ids); + } + if let Some(time_interval) = time_interval { + opts = opts.time_interval(time_interval); + } + self.ctx + .funds(opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the fund list filter options + fn filters(&self) -> PyResult { + self.ctx.filters().map_err(ErrorNewType)?.try_into() + } + + /// Get fund detail + fn detail(&self, counter_id: String) -> PyResult { + self.ctx + .detail(counter_id) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund analysis (level 1) + #[pyo3(signature = (counter_id, period = None))] + fn analysis(&self, counter_id: String, period: Option) -> PyResult { + let mut opts = GetFundAnalysisOptions::new(); + if let Some(period) = period { + opts = opts.period(period); + } + self.ctx + .analysis(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund analysis detail (level 2) + #[pyo3(signature = (counter_id, period = None))] + fn analysis_detail( + &self, + counter_id: String, + period: Option, + ) -> PyResult { + let mut opts = GetFundAnalysisOptions::new(); + if let Some(period) = period { + opts = opts.period(period); + } + self.ctx + .analysis_detail(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund trend chart + #[pyo3(signature = (counter_id, period = None))] + fn trend(&self, counter_id: String, period: Option) -> PyResult { + let mut opts = GetFundAnalysisOptions::new(); + if let Some(period) = period { + opts = opts.period(period); + } + self.ctx + .trend(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund annual returns + #[pyo3(signature = (counter_id, page = None, size = None))] + fn annual_returns( + &self, + counter_id: String, + page: Option, + size: Option, + ) -> PyResult> { + let opts = build_page_options(page, size); + self.ctx + .annual_returns(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund quarterly returns + #[pyo3(signature = (counter_id, page = None, size = None))] + fn quarterly_returns( + &self, + counter_id: String, + page: Option, + size: Option, + ) -> PyResult> { + let opts = build_page_options(page, size); + self.ctx + .quarterly_returns(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund performance figures + fn performance(&self, counter_id: String) -> PyResult> { + self.ctx + .performance(counter_id) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund performance comparison + #[pyo3(signature = (counter_id, period = None))] + fn performance_comparison( + &self, + counter_id: String, + period: Option, + ) -> PyResult { + let mut opts = GetFundAnalysisOptions::new(); + if let Some(period) = period { + opts = opts.period(period); + } + self.ctx + .performance_comparison(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get fund latest net value + fn nav(&self, counter_id: String) -> PyResult> { + self.ctx + .nav(counter_id) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund historical net value (paged) + #[pyo3(signature = (counter_id, page = None, size = None))] + fn nav_history( + &self, + counter_id: String, + page: Option, + size: Option, + ) -> PyResult> { + let opts = build_page_options(page, size); + self.ctx + .nav_history(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get fund historical net value by relative time range + #[pyo3(signature = (counter_id, month_before = None, year_before = None))] + fn nav_range( + &self, + counter_id: String, + month_before: Option, + year_before: Option, + ) -> PyResult> { + let opts = build_nav_range_options(month_before, year_before); + self.ctx + .nav_range(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get a fund's top-10 holdings + #[pyo3(signature = (counter_id, scene = None))] + fn holdings(&self, counter_id: String, scene: Option) -> PyResult { + let mut opts = GetFundHoldingsOptions::new(); + if let Some(scene) = scene { + opts = opts.scene(scene); + } + self.ctx + .holdings(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the stocks held by a fund (reverse lookup) + #[pyo3(signature = (counter_id, limit = None))] + fn stock_holdings( + &self, + counter_id: String, + limit: Option, + ) -> PyResult> { + let mut opts = GetFundStockHoldingsOptions::new(); + if let Some(limit) = limit { + opts = opts.limit(limit); + } + self.ctx + .stock_holdings(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + // ----- user fund positions (scope: portfolio-asset) ----- + + /// Get the user's fund positions overview + #[pyo3(signature = (account_channel = None, aaid = None))] + fn positions( + &self, + account_channel: Option, + aaid: Option, + ) -> PyResult { + let mut opts = GetFundPositionsOptions::new(); + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + if let Some(aaid) = aaid { + opts = opts.aaid(aaid); + } + self.ctx.positions(opts).map_err(ErrorNewType)?.try_into() + } + + /// Get the user's single fund position detail + #[pyo3(signature = (counter_id, account_channel = None, aaid = None, start = None, end = None))] + fn position( + &self, + counter_id: String, + account_channel: Option, + aaid: Option, + start: Option, + end: Option, + ) -> PyResult { + let mut opts = GetFundPositionOptions::new(); + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + if let Some(aaid) = aaid { + opts = opts.aaid(aaid); + } + if let Some(start) = start { + opts = opts.start(start); + } + if let Some(end) = end { + opts = opts.end(end); + } + self.ctx + .position(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the performance figures of a held fund + fn position_performance(&self, counter_id: String) -> PyResult> { + self.ctx + .position_performance(counter_id) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the cumulative-profit series of a held fund + #[pyo3(signature = (counter_id, account_channel = None, aaid = None, start = None, end = None, page = None, size = None))] + #[allow(clippy::too_many_arguments)] + fn position_profits( + &self, + counter_id: String, + account_channel: Option, + aaid: Option, + start: Option, + end: Option, + page: Option, + size: Option, + ) -> PyResult { + let mut opts = GetFundPositionProfitsOptions::new(); + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + if let Some(aaid) = aaid { + opts = opts.aaid(aaid); + } + if let Some(start) = start { + opts = opts.start(start); + } + if let Some(end) = end { + opts = opts.end(end); + } + if let Some(page) = page { + opts = opts.page(page); + } + if let Some(size) = size { + opts = opts.size(size); + } + self.ctx + .position_profits(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Get the net-value history of a held fund + #[pyo3(signature = (counter_id, month_before = None, year_before = None))] + fn position_nav( + &self, + counter_id: String, + month_before: Option, + year_before: Option, + ) -> PyResult> { + let opts = build_nav_range_options(month_before, year_before); + self.ctx + .position_nav(counter_id, opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get the dividend records of a held fund + #[pyo3(signature = (counter_id, account_channel = None, aaid = None, currency = None, start = None, end = None, page = None, size = None))] + #[allow(clippy::too_many_arguments)] + fn position_dividends( + &self, + counter_id: String, + account_channel: Option, + aaid: Option, + currency: Option, + start: Option, + end: Option, + page: Option, + size: Option, + ) -> PyResult { + let mut opts = GetFundPositionDividendsOptions::new(); + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + if let Some(aaid) = aaid { + opts = opts.aaid(aaid); + } + if let Some(currency) = currency { + opts = opts.currency(currency); + } + if let Some(start) = start { + opts = opts.start(start); + } + if let Some(end) = end { + opts = opts.end(end); + } + if let Some(page) = page { + opts = opts.page(page); + } + if let Some(size) = size { + opts = opts.size(size); + } + self.ctx + .position_dividends(counter_id, opts) + .map_err(ErrorNewType)? + .try_into() + } + + // ----- fund orders & trading (scope: order) ----- + + /// Get the user's fund orders (also serves as the trade/execution record) + #[pyo3(signature = (counter_ids = None, actions = None, states = None, currency = None, start = None, end = None, page = None, size = None))] + #[allow(clippy::too_many_arguments)] + fn orders( + &self, + counter_ids: Option>, + actions: Option, + states: Option, + currency: Option, + start: Option, + end: Option, + page: Option, + size: Option, + ) -> PyResult> { + let mut opts = GetFundOrdersOptions::new(); + if let Some(counter_ids) = counter_ids { + opts = opts.counter_ids(counter_ids); + } + if let Some(actions) = actions { + opts = opts.actions(actions); + } + if let Some(states) = states { + opts = opts.states(states); + } + if let Some(currency) = currency { + opts = opts.currency(currency); + } + if let Some(start) = start { + opts = opts.start(start); + } + if let Some(end) = end { + opts = opts.end(end); + } + if let Some(page) = page { + opts = opts.page(page); + } + if let Some(size) = size { + opts = opts.size(size); + } + self.ctx + .orders(opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Get a fund order detail + fn order(&self, order_id: i64) -> PyResult { + self.ctx.order(order_id).map_err(ErrorNewType)?.try_into() + } + + /// Get the user's fund transactions (cash-flow records) + #[pyo3(signature = (account_channel = None, business_type = None, category = None, currencies = None, start = None, end = None, page = None, size = None))] + #[allow(clippy::too_many_arguments)] + fn transactions( + &self, + account_channel: Option, + business_type: Option, + category: Option, + currencies: Option, + start: Option, + end: Option, + page: Option, + size: Option, + ) -> PyResult> { + let mut opts = GetFundTransactionsOptions::new(); + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + if let Some(business_type) = business_type { + opts = opts.business_type(business_type); + } + if let Some(category) = category { + opts = opts.category(category); + } + if let Some(currencies) = currencies { + opts = opts.currencies(currencies); + } + if let Some(start) = start { + opts = opts.start(start); + } + if let Some(end) = end { + opts = opts.end(end); + } + if let Some(page) = page { + opts = opts.page(page); + } + if let Some(size) = size { + opts = opts.size(size); + } + self.ctx + .transactions(opts) + .map_err(ErrorNewType)? + .into_iter() + .map(TryInto::try_into) + .collect() + } + + /// Validate a fund order before submitting + #[pyo3(signature = (counter_id, action, currency, amount = None, units = None, dividend_option = None, fund_source = None, account_channel = None))] + #[allow(clippy::too_many_arguments)] + fn validate_order( + &self, + counter_id: String, + action: String, + currency: String, + amount: Option, + units: Option, + dividend_option: Option, + fund_source: Option, + account_channel: Option, + ) -> PyResult { + let mut opts = ValidateFundOrderOptions::new(counter_id, action, currency); + if let Some(amount) = amount { + opts = opts.amount(amount); + } + if let Some(units) = units { + opts = opts.units(units); + } + if let Some(dividend_option) = dividend_option { + opts = opts.dividend_option(dividend_option); + } + if let Some(fund_source) = fund_source { + opts = opts.fund_source(fund_source); + } + if let Some(account_channel) = account_channel { + opts = opts.account_channel(account_channel); + } + self.ctx + .validate_order(opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Submit a fund order (buy / sell) + #[pyo3(signature = (counter_id, action, currency, amount = None, units = None, dividend_option = None, fee = None, is_sell_all = None, remark = None, trade_method = None))] + #[allow(clippy::too_many_arguments)] + fn submit_order( + &self, + counter_id: String, + action: String, + currency: String, + amount: Option, + units: Option, + dividend_option: Option, + fee: Option, + is_sell_all: Option, + remark: Option, + trade_method: Option, + ) -> PyResult { + let mut opts = SubmitFundOrderOptions::new(counter_id, action, currency); + if let Some(amount) = amount { + opts = opts.amount(amount); + } + if let Some(units) = units { + opts = opts.units(units); + } + if let Some(dividend_option) = dividend_option { + opts = opts.dividend_option(dividend_option); + } + if let Some(fee) = fee { + opts = opts.fee(fee); + } + if let Some(is_sell_all) = is_sell_all { + opts = opts.is_sell_all(is_sell_all); + } + if let Some(remark) = remark { + opts = opts.remark(remark); + } + if let Some(trade_method) = trade_method { + opts = opts.trade_method(trade_method); + } + self.ctx + .submit_order(opts) + .map_err(ErrorNewType)? + .try_into() + } + + /// Cancel (withdraw) a fund order + fn cancel_order(&self, order_id: i64) -> PyResult<()> { + self.ctx.cancel_order(order_id).map_err(ErrorNewType)?; + Ok(()) + } +} + +fn build_page_options(page: Option, size: Option) -> FundPageOptions { + let mut opts = FundPageOptions::new(); + if let Some(page) = page { + opts = opts.page(page); + } + if let Some(size) = size { + opts = opts.size(size); + } + opts +} + +fn build_nav_range_options( + month_before: Option, + year_before: Option, +) -> FundNavRangeOptions { + let mut opts = FundNavRangeOptions::new(); + if let Some(month_before) = month_before { + opts = opts.month_before(month_before); + } + if let Some(year_before) = year_before { + opts = opts.year_before(year_before); + } + opts +} diff --git a/python/src/fund/mod.rs b/python/src/fund/mod.rs new file mode 100644 index 000000000..301b3efa6 --- /dev/null +++ b/python/src/fund/mod.rs @@ -0,0 +1,49 @@ +mod context; +pub(crate) mod types; + +use pyo3::prelude::*; + +pub(crate) fn register_types(parent: &Bound) -> PyResult<()> { + use types::*; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + parent.add_class::()?; + Ok(()) +} diff --git a/python/src/fund/types.rs b/python/src/fund/types.rs new file mode 100644 index 000000000..278863ffc --- /dev/null +++ b/python/src/fund/types.rs @@ -0,0 +1,1106 @@ +use std::{ + convert::Infallible, + fmt::{self, Debug}, +}; + +use longbridge_python_macros::PyObject; +use pyo3::{ + Bound, IntoPyObject, Python, pyclass, + types::{PyString, PyStringMethods}, +}; +use serde_json::Value; + +// ── Fund (mutual fund) types +// ───────────────────────────────────────────────────── + +/// A raw, server-defined JSON value exposed to Python as a JSON `str`. +/// +/// Several fund endpoints return objects whose schema is defined by the server +/// and may change over time (filter option lists, analysis blocks, trend / +/// comparison performance series). To avoid pinning these to a fixed shape the +/// Rust core keeps them as `serde_json::Value`; here they are surfaced verbatim +/// to Python as JSON-encoded strings that the caller can `json.loads(...)`. +#[derive(Clone)] +pub(crate) struct PyJson(pub(crate) String); + +impl Debug for PyJson { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl TryFrom for PyJson { + type Error = pyo3::PyErr; + + fn try_from(value: Value) -> Result { + Ok(PyJson(value.to_string())) + } +} + +impl<'py> IntoPyObject<'py> for PyJson { + type Target = PyString; + type Output = Bound<'py, Self::Target>; + type Error = Infallible; + + fn into_pyobject(self, py: Python<'py>) -> Result { + Ok(PyString::new(py, &self.0)) + } +} + +impl<'a, 'py> pyo3::FromPyObject<'a, 'py> for PyJson { + type Error = pyo3::PyErr; + + fn extract(ob: pyo3::Borrowed<'a, 'py, pyo3::PyAny>) -> pyo3::PyResult { + let s: Bound = ob.extract()?; + Ok(PyJson(s.to_str()?.to_string())) + } +} + +/// A fund net-asset-value data point (latest / historical). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundNavValue")] +pub(crate) struct FundNavValue { + /// Net value change + change: String, + /// Net value change percent + change_percent: String, + /// Formatted change percent + change_percent_format: String, + /// Fund counter id + counter_id: String, + /// Fund name + counter_name: String, + /// Currency + currency: String, + /// Formatted date + date_format: String, + /// ISIN + isin: String, + /// Last update time (unix seconds) + last_update_time: i64, + /// Net value + value: String, + /// Formatted net value + value_format: String, +} + +/// A recent performance point used by the hot-fund list. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPerformancePoint")] +pub(crate) struct FundPerformancePoint { + /// Date + date: String, + /// Last done value + last_done: String, +} + +/// A hot-selling fund entry. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::HotFund")] +pub(crate) struct HotFund { + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Earning rate + earning_rate: String, + /// Recent performance points + #[py(array)] + fund_performances: Vec, + /// Fund name + name: String, + /// Minimum purchase amount + purchase_amount: String, + /// Recommendation text + recommendation_text: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Time interval of the earning rate + time_interval: String, +} + +/// A fund entry in the fund list. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundBrief")] +pub(crate) struct FundBrief { + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Fund code + code: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Description + description: String, + /// Earning rate + earning_rate: String, + /// Whether the user is holding this fund + holding: bool, + /// ISIN + isin: String, + /// Fund name + name: String, + /// Product + product: String, + /// Minimum purchase amount + purchase_amount: String, + /// Recommendation text + recommendation_text: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Time interval of the earning rate + time_interval: String, + /// Unit value + unit_value: String, +} + +/// Fund list filter options. Each list holds server-defined option objects +/// exposed as raw JSON strings. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundFilters")] +pub(crate) struct FundFilters { + /// Asset class options (raw JSON strings) + #[py(array)] + asset_class: Vec, + /// Company options (raw JSON strings) + #[py(array)] + company: Vec, + /// Currency options (raw JSON strings) + #[py(array)] + currency: Vec, + /// Industry category options (raw JSON strings) + #[py(array)] + industry_category_name: Vec, + /// Risk level options (raw JSON strings) + #[py(array)] + risk_level: Vec, +} + +/// A single holding entry inside a fund's asset allocation. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundAssetAllocationItem")] +pub(crate) struct FundAssetAllocationItem { + /// Security code + code: String, + /// Security counter id + counter_id: String, + /// Name + name: String, + /// Position ratio + position_ratio: String, +} + +/// A fund's asset allocation. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundAssetAllocation")] +pub(crate) struct FundAssetAllocation { + /// Asset type + asset_type: i32, + /// Allocation entries + #[py(array)] + lists: Vec, + /// Report date + report_date: String, +} + +/// Fund detail. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundDetail")] +pub(crate) struct FundDetail { + /// Additional purchase amount + additional_purchase_amount: String, + /// Affirm day + affirm_day: i32, + /// Amount affirm day + amount_affirm_day: String, + /// Asset allocation + #[py(opt)] + asset_allocation: Option, + /// Asset class + asset_class: i32, + /// Asset class name + asset_class_name: String, + /// Bill purchase rate + bill_purchase_rate: String, + /// Channel + channel: String, + /// Close period + close_period: String, + /// Fund code + code: String, + /// Currency + currency: String, + /// Cut off time + cut_off_time: String, + /// Whether it is a derivative + derivatives: bool, + /// Done day + done_day: i32, + /// Excess return fee + excess_return_fee: String, + /// GST rate + gst_rate: String, + /// Introduction + introduce: String, + /// Whether it is a cash-plus fund + is_cash_plus: bool, + /// Whether it is a complex product + is_complex: bool, + /// Whether it is a new cash-plus fund + is_new_cash_plus: bool, + /// Whether it is a Yinghebao fund + is_yinghebao: bool, + /// ISIN + isin: String, + /// Management rate + manage_rate: String, + /// Manager + manager: String, + /// Minimum holding cash + min_hold_cash: String, + /// Minimum holding share + min_hold_share: String, + /// Minimum sell share + min_sell_share: String, + /// Month raise day + month_raise_day: String, + /// Fund name + name: String, + /// Net value deadline + nav_deadline: String, + /// Whether it is no-load + no_load: bool, + /// Open date + open_date: String, + /// Open period + open_period: String, + /// Product + product: String, + /// Product information locals + product_information_locals: String, + /// Profile + profile: String, + /// Whether purchasable + purchasable: i32, + /// Purchase affirm day + purchase_affirm_day: String, + /// Minimum purchase amount + purchase_amount: String, + /// Purchase rate + purchase_rate: String, + /// Rating + rating: i32, + /// Whether redeemable + redeemable: i32, + /// Redemption advance day + redemption_advance_day: String, + /// Redemption amount + redemption_amount: String, + /// Redemption close period text + redemption_close_period_shows: String, + /// Redemption done day + redemption_done_day: String, + /// Redemption open day text + redemption_open_day_shows: String, + /// Risk level + risk_level: i32, + /// Risk level name + risk_level_name: String, + /// Verify status + verify_status: i32, + /// Whether it is a virtual currency fund + virtual_currency: bool, + /// Year to date yield + year_to_date_yield: String, + /// Year to date yield type + ytd_yield_type: i32, +} + +/// Fund analysis (level 1). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundAnalysis")] +pub(crate) struct FundAnalysis { + /// Actual period + actual_period: i32, + /// Cost level (server-defined structure, raw JSON string) + cost_level: PyJson, + /// Return ability (server-defined structure, raw JSON string) + return_ability: PyJson, + /// Risk ability (server-defined structure, raw JSON string) + risk_ability: PyJson, + /// Updated at + updated_at: String, + /// Value for money (server-defined structure, raw JSON string) + value_for_money: PyJson, + /// Whether visible + visible: bool, +} + +/// Fund analysis detail (level 2). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundAnalysisDetail")] +pub(crate) struct FundAnalysisDetail { + /// Actual period + actual_period: i32, + /// Available periods + available_periods: Vec, + /// Cost level (server-defined structure, raw JSON string) + cost_level: PyJson, + /// Return ability (server-defined structure, raw JSON string) + return_ability: PyJson, + /// Risk ability (server-defined structure, raw JSON string) + risk_ability: PyJson, + /// Updated at + updated_at: String, + /// Value for money (server-defined structure, raw JSON string) + value_for_money: PyJson, + /// Whether visible + visible: bool, +} + +/// A benchmark contrast series in a fund trend chart. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundTrendContrast")] +pub(crate) struct FundTrendContrast { + /// Benchmark name + benchmark_name: String, + /// Performance points (server-defined structure, raw JSON strings) + #[py(array)] + performances: Vec, +} + +/// Fund trend chart. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundTrend")] +pub(crate) struct FundTrend { + /// Actual period + actual_period: i32, + /// Available periods + available_periods: Vec, + /// Category average performances (server-defined structure, raw JSON + /// strings) + #[py(array)] + category_average_performances: Vec, + /// Benchmark contrast performances + #[py(opt)] + contrast_performances: Option, + /// Fund performances (server-defined structure, raw JSON strings) + #[py(array)] + fund_performances: Vec, +} + +/// A named contrast performance series. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundNamedContrast")] +pub(crate) struct FundNamedContrast { + /// Series name + name: String, + /// Performance points (server-defined structure, raw JSON strings) + #[py(array)] + performances: Vec, +} + +/// Fund performance comparison. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPerformanceComparison")] +pub(crate) struct FundPerformanceComparison { + /// Contrast performance series + #[py(array)] + contrast_performances: Vec, + /// Fund performances (server-defined structure, raw JSON strings) + #[py(array)] + fund_performances: Vec, +} + +/// A fund annual return entry. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundAnnualReturn")] +pub(crate) struct FundAnnualReturn { + /// Change percent + change_percent: String, + /// Year + year: i32, +} + +/// A fund quarterly return entry. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundQuarterlyReturn")] +pub(crate) struct FundQuarterlyReturn { + /// Change percent + change_percent: String, + /// Quarter + quarter: i32, + /// Year + year: i32, +} + +/// A fund's detailed performance figures. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPerformance")] +pub(crate) struct FundPerformance { + /// Annualized return (5y) + annualized_return_five: String, + /// Annualized return (1y) + annualized_return_one: String, + /// Annualized return (10y) + annualized_return_ten: String, + /// Annualized return (3y) + annualized_return_three: String, + /// Annualized return (2y) + annualized_return_two: String, + /// Fund counter id + counter_id: String, + /// Fund name + fund_name: String, + /// Rank (5y) + performance_rank_five_years: i32, + /// Rank (1d) + performance_rank_one_day: i32, + /// Rank (1m) + performance_rank_one_month: i32, + /// Rank (1w) + performance_rank_one_week: i32, + /// Rank (1y) + performance_rank_one_year: i32, + /// Rank (6m) + performance_rank_six_months: i32, + /// Rank (10y) + performance_rank_ten_years: i32, + /// Rank (3m) + performance_rank_three_months: i32, + /// Rank (3y) + performance_rank_three_years: i32, + /// Rank (2y) + performance_rank_two_years: i32, + /// Rank (ytd) + performance_rank_ytd: i32, + /// Return (5y) + performance_return_five_years: String, + /// Return (1d) + performance_return_one_day: String, + /// Return (1m) + performance_return_one_month: String, + /// Return (1w) + performance_return_one_week: String, + /// Return (1y) + performance_return_one_year: String, + /// Return (6m) + performance_return_six_months: String, + /// Return (10y) + performance_return_ten_years: String, + /// Return (3m) + performance_return_three_months: String, + /// Return (3y) + performance_return_three_years: String, + /// Return (2y) + performance_return_two_years: String, + /// Return (ytd) + performance_return_ytd: String, + /// Total peers (5y) + performance_total_five_years: i32, + /// Total peers (1d) + performance_total_one_day: i32, + /// Total peers (1m) + performance_total_one_month: i32, + /// Total peers (1w) + performance_total_one_week: i32, + /// Total peers (1y) + performance_total_one_year: i32, + /// Total peers (6m) + performance_total_six_months: i32, + /// Total peers (10y) + performance_total_ten_years: i32, + /// Total peers (3m) + performance_total_three_months: i32, + /// Total peers (3y) + performance_total_three_years: i32, + /// Total peers (2y) + performance_total_two_years: i32, + /// Total peers (ytd) + performance_total_ytd: i32, + /// Seven days annualized + seven_days_annualized: String, + /// Ten thousand price + ten_thousand_price: String, + /// Update time (unix seconds) + update_time: i64, +} + +/// A single fund holding (top-10 holdings). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundHolding")] +pub(crate) struct FundHolding { + /// Bond type + bond_type: String, + /// Bond type name + bond_type_name: String, + /// Country name + country_name: String, + /// Holding type + holding_type: String, + /// Industry name + industry_name: String, + /// Market value + market_value: String, + /// Maturity date + maturity_date: String, + /// Name + name: String, + /// Share change + share_change: String, + /// Share change percent + share_change_percent: String, + /// Shares + shares: String, + /// Weighting + weighting: String, +} + +/// A fund's top-10 holdings. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundHoldings")] +pub(crate) struct FundHoldings { + /// Holding entries + #[py(array)] + holdings: Vec, + /// Report date + report_date: String, + /// Total weighting + weighting: String, +} + +/// A stock held by the fund (reverse lookup). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundStockHolding")] +pub(crate) struct FundStockHolding { + /// Stock code + code: String, + /// Stock counter id + counter_id: String, + /// Currency + currency: String, + /// Stock name + name: String, + /// Position ratio + position_ratio: String, + /// Report date + report_date: String, +} + +/// A single fund position held by the user. +/// +/// Named `FundHoldingPosition` to avoid a name clash with the trade channel's +/// existing `FundPosition` class. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPosition")] +pub(crate) struct FundHoldingPosition { + /// Holding amount + amount: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Frozen units + freeze_units: String, + /// Holding profit + holding_profit: String, + /// Holding units + holding_units: String, + /// Fund name + name: String, + /// Recent profit + recent_profit: String, + /// Recent trading day (unix seconds) + recent_trading_day: i64, + /// Accumulated recent profit + sum_recent_profit: String, +} + +/// The user's fund positions overview. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositions")] +pub(crate) struct FundPositions { + /// Account channel + account_channel: String, + /// Position entries + #[py(array)] + list: Vec, + /// Pending buy orders amount + pending_buy_orders: String, + /// Recent trading day (unix seconds) + recent_trading_day: i64, + /// Sold pending credit orders amount + sold_pending_credit_orders: String, +} + +/// A dated value point. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundDatedValue")] +pub(crate) struct FundDatedValue { + /// Date (unix seconds) + date: i64, + /// Value + value: String, +} + +/// A fund unit-value point (position view). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundUnitValue")] +pub(crate) struct FundUnitValue { + /// Date (unix seconds) + date: i64, + /// Day increase rate + day_increase_rate: String, + /// Total value + total_value: String, + /// Unit value + unit_value: String, +} + +/// Detail values of a single fund position. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositionDetailValues")] +pub(crate) struct FundPositionDetailValues { + /// Amount + amount: String, + /// Currency + currency: String, + /// Holding cost + holding_cost: String, + /// Holding profit + holding_profit: String, + /// Holding profit rate + holding_profit_rate: String, + /// Holding units + holding_units: String, + /// Holding value + holding_value: String, + /// Pending buy value + pending_buy_value: String, + /// Pending sell value + pending_sell_value: String, + /// Accumulated profit (to date) + profit_amount_accum_td: String, + /// Accumulated profit rate (to date) + profit_amount_accum_td_rate: String, + /// Recent profit + recent_profit: String, + /// Recent trading day (unix seconds) + recent_tradingday: i64, + /// Recent unit value + recent_unit_value: String, + /// Sold pending-confirm units + sold_pending_confirm_units: String, +} + +/// Detail of a single fund position. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositionDetail")] +pub(crate) struct FundPositionDetail { + /// Detail values + #[py(opt)] + detail_values: Option, + /// Accumulated profit series + #[py(array)] + sum_profit: Vec, + /// Unit value series + #[py(array)] + ut_value: Vec, +} + +/// Performance figures for a held fund. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositionPerformance")] +pub(crate) struct FundPositionPerformance { + /// Annualized return (5y) + annualized_return_five: String, + /// Annualized return (1y) + annualized_return_one: String, + /// Annualized return (10y) + annualized_return_ten: String, + /// Annualized return (3y) + annualized_return_three: String, + /// Annualized return (2y) + annualized_return_two: String, + /// Fund counter id + counter_id: String, + /// Fund name + fund_name: String, + /// Return (5y) + performance_return_five_years: String, + /// Return (1d) + performance_return_one_day: String, + /// Return (1m) + performance_return_one_month: String, + /// Return (1w) + performance_return_one_week: String, + /// Return (1y) + performance_return_one_year: String, + /// Return (6m) + performance_return_six_months: String, + /// Return (10y) + performance_return_ten_years: String, + /// Return (3m) + performance_return_three_months: String, + /// Return (3y) + performance_return_three_years: String, + /// Return (2y) + performance_return_two_years: String, + /// Return (ytd) + performance_return_ytd: String, + /// Update time (unix seconds) + update_time: i64, +} + +/// The user's cumulative profit for a held fund. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositionProfits")] +pub(crate) struct FundPositionProfits { + /// Currency + currency: String, + /// Profit series + #[py(array)] + history_value: Vec, + /// Last update time (unix seconds) + last_update_time: i64, + /// Total profit + sum_profit: String, +} + +/// A held-fund net-value point (position view). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundPositionNav")] +pub(crate) struct FundPositionNav { + /// Net value change + change: String, + /// Net value change percent + change_percent: String, + /// Fund counter id + counter_id: String, + /// Fund name + counter_name: String, + /// Last update time (unix seconds) + last_update_time: i64, + /// Net value + value: String, +} + +/// A cash dividend record for a held fund. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundDividend")] +pub(crate) struct FundDividend { + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Currency + currency: String, + /// Date (unix seconds) + date: i64, + /// Dividend method + div_method: String, + /// Fund name + name: String, +} + +/// The user's dividend records for a held fund. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundDividends")] +pub(crate) struct FundDividends { + /// Currency + currency: String, + /// Dividend records + #[py(array)] + div_cash_infos: Vec, + /// Latest dividend date (unix seconds) + lastest_date: i64, + /// Total cash dividend + total_div_cash: String, +} + +/// A fund order (list view). +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrder")] +pub(crate) struct FundOrder { + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Fund name + fund_name: String, + /// Order id + id: i64, + /// Whether it is an auto (DCA) order + is_auto: bool, + /// Net worth + net_worth: String, + /// Product type + product_type: String, + /// State + state: String, + /// State description + state_desc: String, + /// Units + units: String, +} + +/// A keyword block in a fund order detail. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderKeyword")] +pub(crate) struct FundOrderKeyword { + /// Content + content: String, + /// Group + group: String, + /// Key + key: String, + /// Line strategy + line_strategy: String, + /// Title + title: String, +} + +/// A processing stage in a fund order detail. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderStage")] +pub(crate) struct FundOrderStage { + /// Description + desc: String, + /// Key + key: String, + /// Link + link: String, + /// Link text + link_text: String, + /// Progress + progress: String, + /// Stage + stage: String, +} + +/// The full information of a fund order. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderInfo")] +pub(crate) struct FundOrderInfo { + /// Account id + aaid: i64, + /// Account channel + account_channel: String, + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Channel + channel: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Dividend option + dividend_option: String, + /// Equity time (unix seconds) + eq_at: i64, + /// Fee + fee: String, + /// Fund name + fund_name: String, + /// Fund source + fund_source: String, + /// Histories + histories: String, + /// Order id + id: i64, + /// Message + message: String, + /// Net worth + net_worth: String, + /// Price time (unix seconds) + price_at: i64, + /// Processed at (unix seconds) + processed_at: i64, + /// Product type + product_type: String, + /// Whether repurchaseable + repurchaseable: bool, + /// Sale proceeds + sale_proceeds: String, + /// Sales charge + sales_charge: String, + /// Sales price + sales_price: String, + /// Sales unit + sales_unit: String, + /// State + state: String, + /// State description + state_desc: String, + /// Status + status: i32, + /// Extended status + status_ex: i32, + /// T+ description + t_description: String, + /// Time partition + time_partition: String, + /// Total amount + total_amount: String, + /// Transaction at (unix seconds) + transaction_at: i64, + /// Units + units: String, + /// Withdraw at (unix seconds) + withdraw_at: i64, + /// Whether withdrawable + withdrawable: bool, +} + +/// Fund order detail. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderDetail")] +pub(crate) struct FundOrderDetail { + /// Keyword blocks + #[py(array)] + keywords: Vec, + /// The order + #[py(opt)] + order: Option, + /// Processing stages + #[py(array)] + stages: Vec, +} + +/// A fund transaction / cash-flow record. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundTransaction")] +pub(crate) struct FundTransaction { + /// Amount + amount: String, + /// Category + category: String, + /// Created at (unix seconds) + created_at: i64, + /// Currency + currency: String, + /// Description + description: String, + /// Detail created at (unix seconds) + detail_created_at: i64, + /// Detail type + detail_type: String, + /// Done at (unix seconds) + done_at: i64, + /// Quantity description + quantity_description: String, + /// Redirect page + redirect_page: String, + /// Redirect page (v2) + redirect_page_v2: String, + /// Reference number + ref_no: String, + /// Stock quantity + stock_quantity: String, + /// Transaction type + tx_type: String, + /// Type name + type_name: String, +} + +/// The result of validating a fund order. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderValidation")] +pub(crate) struct FundOrderValidation { + /// Auth token to carry into submit + auth_token: String, + /// Risk-assessment eval address + eval_address: String, + /// Fund risk level + fund_risk_level: i32, + /// Message + msg: String, + /// User PI status + user_pi: i32, + /// User risk level + user_risk_level: i32, +} + +/// The result of submitting a fund order. +#[pyclass(skip_from_py_object)] +#[derive(Debug, PyObject, Clone)] +#[py(remote = "longbridge::fund::FundOrderSubmitResponse")] +pub(crate) struct FundOrderSubmitResponse { + /// Action (buy/sell) + action: String, + /// Amount + amount: String, + /// Fund counter id + counter_id: String, + /// Created at (unix seconds) + created_at: i64, + /// Fund name + fund_name: String, + /// Order id + id: i64, + /// Message + msg: String, + /// Status + status: i32, + /// Units + units: String, +} diff --git a/python/src/lib.rs b/python/src/lib.rs index 4aac4874c..bab5cefc9 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -15,6 +15,7 @@ mod content; mod dca; mod decimal; mod error; +mod fund; mod fundamental; mod grid; mod http_client; @@ -48,6 +49,7 @@ fn longbridge(py: Python<'_>, m: Bound) -> PyResult<()> { dca::register_types(&openapi)?; sharelist::register_types(&openapi)?; calendar::register_types(&openapi)?; + fund::register_types(&openapi)?; fundamental::register_types(&openapi)?; grid::register_types(&openapi)?; market::register_types(&openapi)?; diff --git a/python/src/trade/types.rs b/python/src/trade/types.rs index 0cdd0ef39..524db71a3 100644 --- a/python/src/trade/types.rs +++ b/python/src/trade/types.rs @@ -1019,8 +1019,8 @@ pub(crate) struct FundPositionChannel { #[derive(Debug, PyObject, Clone)] #[py(remote = "longbridge::trade::FundPosition")] pub(crate) struct FundPosition { - /// Fund ISIN code - symbol: String, + /// Fund counter id (the ISIN is the last `/`-separated segment) + counter_id: String, /// Current equity current_net_asset_value: PyDecimal, /// Current equity time diff --git a/rust/src/blocking/fund.rs b/rust/src/blocking/fund.rs new file mode 100644 index 000000000..e6db5a3bd --- /dev/null +++ b/rust/src/blocking/fund.rs @@ -0,0 +1,282 @@ +use std::sync::Arc; + +use tokio::sync::mpsc; + +use crate::{ + Config, Result, + blocking::runtime::BlockingRuntime, + fund::{ + FundAnalysis, FundAnalysisDetail, FundAnnualReturn, FundBrief, FundContext, FundDetail, + FundDividends, FundFilters, FundHoldings, FundNavRangeOptions, FundNavValue, FundOrder, + FundOrderDetail, FundOrderSubmitResponse, FundOrderValidation, FundPageOptions, + FundPerformance, FundPerformanceComparison, FundPositionDetail, FundPositionNav, + FundPositionPerformance, FundPositionProfits, FundPositions, FundQuarterlyReturn, + FundStockHolding, FundTransaction, FundTrend, GetFundAnalysisOptions, + GetFundHoldingsOptions, GetFundOrdersOptions, GetFundPositionDividendsOptions, + GetFundPositionOptions, GetFundPositionProfitsOptions, GetFundPositionsOptions, + GetFundStockHoldingsOptions, GetFundTransactionsOptions, GetFundsOptions, HotFund, + SubmitFundOrderOptions, ValidateFundOrderOptions, + }, +}; + +/// Blocking fund (mutual fund) channel context. +pub struct FundContextSync { + rt: BlockingRuntime, +} + +impl FundContextSync { + /// Create a [`FundContextSync`] + pub fn new(config: Arc) -> Result { + let rt = BlockingRuntime::try_new( + move || { + let ctx = FundContext::new(config); + let (tx, rx) = mpsc::unbounded_channel::(); + std::mem::forget(tx); + Ok::<_, crate::Error>((ctx, rx)) + }, + |_: std::convert::Infallible| {}, + )?; + Ok(Self { rt }) + } + + /// Get the hot-selling fund list (blocking) + pub fn hot_funds(&self) -> Result> { + self.rt + .call(move |ctx| async move { ctx.hot_funds().await }) + } + + /// Get the fund list (blocking) + pub fn funds( + &self, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.funds(options).await }) + } + + /// Get the fund list filter options (blocking) + pub fn filters(&self) -> Result { + self.rt.call(move |ctx| async move { ctx.filters().await }) + } + + /// Get fund detail (blocking) + pub fn detail(&self, counter_id: impl Into + Send + 'static) -> Result { + self.rt + .call(move |ctx| async move { ctx.detail(counter_id).await }) + } + + /// Get fund analysis (level 1) (blocking) + pub fn analysis( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.analysis(counter_id, options).await }) + } + + /// Get fund analysis detail (level 2) (blocking) + pub fn analysis_detail( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.analysis_detail(counter_id, options).await }) + } + + /// Get fund trend chart (blocking) + pub fn trend( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.trend(counter_id, options).await }) + } + + /// Get fund annual returns (blocking) + pub fn annual_returns( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.annual_returns(counter_id, options).await }) + } + + /// Get fund quarterly returns (blocking) + pub fn quarterly_returns( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.quarterly_returns(counter_id, options).await }) + } + + /// Get fund performance figures (blocking) + pub fn performance( + &self, + counter_id: impl Into + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.performance(counter_id).await }) + } + + /// Get fund performance comparison (blocking) + pub fn performance_comparison( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.performance_comparison(counter_id, options).await }) + } + + /// Get fund latest net value (blocking) + pub fn nav(&self, counter_id: impl Into + Send + 'static) -> Result> { + self.rt + .call(move |ctx| async move { ctx.nav(counter_id).await }) + } + + /// Get fund historical net value (paged) (blocking) + pub fn nav_history( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.nav_history(counter_id, options).await }) + } + + /// Get fund historical net value by relative time range (blocking) + pub fn nav_range( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.nav_range(counter_id, options).await }) + } + + /// Get a fund's top-10 holdings (blocking) + pub fn holdings( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.holdings(counter_id, options).await }) + } + + /// Get the stocks held by a fund (reverse lookup) (blocking) + pub fn stock_holdings( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.stock_holdings(counter_id, options).await }) + } + + /// Get the user's fund positions overview (blocking) + pub fn positions( + &self, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.positions(options).await }) + } + + /// Get the user's single fund position detail (blocking) + pub fn position( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.position(counter_id, options).await }) + } + + /// Get the performance figures of a held fund (blocking) + pub fn position_performance( + &self, + counter_id: impl Into + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.position_performance(counter_id).await }) + } + + /// Get the cumulative-profit series of a held fund (blocking) + pub fn position_profits( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.position_profits(counter_id, options).await }) + } + + /// Get the net-value history of a held fund (blocking) + pub fn position_nav( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.position_nav(counter_id, options).await }) + } + + /// Get the dividend records of a held fund (blocking) + pub fn position_dividends( + &self, + counter_id: impl Into + Send + 'static, + options: impl Into> + Send + 'static, + ) -> Result { + self.rt + .call(move |ctx| async move { ctx.position_dividends(counter_id, options).await }) + } + + /// Get the user's fund orders (blocking) + pub fn orders( + &self, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.orders(options).await }) + } + + /// Get a fund order detail (blocking) + pub fn order(&self, order_id: i64) -> Result { + self.rt + .call(move |ctx| async move { ctx.order(order_id).await }) + } + + /// Get the user's fund transactions (cash-flow records) (blocking) + pub fn transactions( + &self, + options: impl Into> + Send + 'static, + ) -> Result> { + self.rt + .call(move |ctx| async move { ctx.transactions(options).await }) + } + + /// Validate a fund order before submitting (blocking) + pub fn validate_order(&self, options: ValidateFundOrderOptions) -> Result { + self.rt + .call(move |ctx| async move { ctx.validate_order(options).await }) + } + + /// Submit a fund order (buy / sell) (blocking) + pub fn submit_order(&self, options: SubmitFundOrderOptions) -> Result { + self.rt + .call(move |ctx| async move { ctx.submit_order(options).await }) + } + + /// Cancel (withdraw) a fund order (blocking) + pub fn cancel_order(&self, order_id: i64) -> Result<()> { + self.rt + .call(move |ctx| async move { ctx.cancel_order(order_id).await }) + } +} diff --git a/rust/src/blocking/mod.rs b/rust/src/blocking/mod.rs index 11cc01fdf..9970092f4 100644 --- a/rust/src/blocking/mod.rs +++ b/rust/src/blocking/mod.rs @@ -7,6 +7,7 @@ mod calendar; mod content; mod dca; mod error; +mod fund; mod fundamental; mod grid; mod market; @@ -25,6 +26,7 @@ pub use calendar::CalendarContextSync; pub use content::ContentContextSync; pub use dca::DCAContextSync; pub use error::BlockingError; +pub use fund::FundContextSync; pub use fundamental::FundamentalContextSync; pub use grid::GridContextSync; pub use market::MarketContextSync; diff --git a/rust/src/fund/context.rs b/rust/src/fund/context.rs new file mode 100644 index 000000000..01f4b7c0f --- /dev/null +++ b/rust/src/fund/context.rs @@ -0,0 +1,666 @@ +use std::sync::Arc; + +use longbridge_httpcli::{HttpClient, Json, Method}; +use serde::{Deserialize, Serialize}; +use tracing::{Subscriber, dispatcher, instrument::WithSubscriber}; + +use crate::{ + Config, Result, + fund::{ + FundAnalysis, FundAnalysisDetail, FundAnnualReturn, FundBrief, FundDetail, FundDividends, + FundFilters, FundHoldings, FundNavRangeOptions, FundNavValue, FundOrder, FundOrderDetail, + FundOrderSubmitResponse, FundOrderValidation, FundPageOptions, FundPerformance, + FundPerformanceComparison, FundPositionDetail, FundPositionNav, FundPositionPerformance, + FundPositionProfits, FundPositions, FundQuarterlyReturn, FundStockHolding, FundTransaction, + FundTrend, GetFundAnalysisOptions, GetFundHoldingsOptions, GetFundOrdersOptions, + GetFundPositionDividendsOptions, GetFundPositionOptions, GetFundPositionProfitsOptions, + GetFundPositionsOptions, GetFundStockHoldingsOptions, GetFundTransactionsOptions, + GetFundsOptions, HotFund, SubmitFundOrderOptions, ValidateFundOrderOptions, + }, +}; + +#[derive(Debug, Deserialize)] +struct WList { + #[serde(default = "Vec::new")] + list: Vec, +} + +#[derive(Debug, Deserialize)] +struct WFunds { + #[serde(default)] + funds: Vec, +} + +#[derive(Debug, Deserialize)] +struct WValue { + #[serde(default = "Vec::new")] + value: Vec, +} + +#[derive(Debug, Deserialize)] +struct WHistory { + #[serde(default = "Vec::new")] + history_value: Vec, +} + +#[derive(Debug, Deserialize)] +struct WLists { + #[serde(default = "Vec::new")] + lists: Vec, +} + +#[derive(Debug, Deserialize)] +struct WOrders { + #[serde(default)] + orders: Vec, +} + +/// Query wrapper that prepends the fund `counter_id` before flattening the +/// endpoint-specific options. The fund identifier (`counter_id`, e.g. +/// `UT/FD/HK0000384492`) contains `/`, so it cannot live in the URL path and is +/// passed as the `counter_id` query parameter instead. +#[derive(Serialize)] +struct CounterIdQuery { + counter_id: String, + #[serde(flatten)] + options: T, +} + +/// Empty option set for `counter_id`-only endpoints. +#[derive(Serialize, Default)] +struct NoQuery {} + +/// Query wrapper for the batch endpoints (latest NAV, daily performance, held +/// fund performance) whose backend takes a JSON-array `counter_ids` parameter. +/// The single `counter_id` is wrapped into a one-element JSON array to match +/// the backend contract — sending the scalar `counter_id` makes the backend +/// fail. +#[derive(Serialize)] +struct CounterIdsQuery { + counter_ids: String, +} + +impl CounterIdsQuery { + fn single(counter_id: String) -> Self { + Self { + counter_ids: serde_json::to_string(&[counter_id]).expect("serialize counter_ids array"), + } + } +} + +struct InnerFundContext { + http_cli: HttpClient, + log_subscriber: Arc, +} + +impl Drop for InnerFundContext { + fn drop(&mut self) { + dispatcher::with_default(&self.log_subscriber.clone().into(), || { + tracing::info!("fund context dropped"); + }); + } +} + +/// Fund (mutual fund) channel context. +#[derive(Clone)] +pub struct FundContext(Arc); + +impl FundContext { + /// Create a [`FundContext`] + pub fn new(config: Arc) -> Self { + let log_subscriber = config.create_log_subscriber("fund"); + let ctx = Self(Arc::new(InnerFundContext { + http_cli: config.create_http_client(), + log_subscriber, + })); + dispatcher::with_default(&ctx.0.log_subscriber.clone().into(), || { + tracing::info!("fund context created"); + }); + ctx + } + + /// Returns the log subscriber + #[inline] + pub fn log_subscriber(&self) -> Arc { + self.0.log_subscriber.clone() + } + + // ----- fund catalog / market data (scope: quote) ----- + + /// Get the hot-selling fund list. + pub async fn hot_funds(&self) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/hot-funds") + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .list) + } + + /// Get the fund list. + pub async fn funds( + &self, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::POST, "/v1/fund/funds") + .body(Json(options.into().unwrap_or_default())) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .funds) + } + + /// Get the fund list filter options. + pub async fn filters(&self) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/filters") + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund detail. + pub async fn detail(&self, counter_id: impl Into) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/detail") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: NoQuery {}, + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund analysis (level 1). + pub async fn analysis( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/analysis") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund analysis detail (level 2). + pub async fn analysis_detail( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/analysis/detail") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund trend chart. + pub async fn trend( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/trend") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund annual returns. + pub async fn annual_returns( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/returns/annual") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .list) + } + + /// Get fund quarterly returns. + pub async fn quarterly_returns( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/returns/quarterly") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .list) + } + + /// Get fund performance figures. + pub async fn performance(&self, counter_id: impl Into) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/performance") + .query_params(CounterIdsQuery::single(counter_id.into())) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .value) + } + + /// Get fund performance comparison. + pub async fn performance_comparison( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/performance/comparison") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get fund latest net value. + pub async fn nav(&self, counter_id: impl Into) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/nav") + .query_params(CounterIdsQuery::single(counter_id.into())) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .value) + } + + /// Get fund historical net value (paged). + pub async fn nav_history( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/nav-history") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .history_value) + } + + /// Get fund historical net value by relative time range. + pub async fn nav_range( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/nav-range") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .history_value) + } + + /// Get a fund's top-10 holdings. + pub async fn holdings( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/holdings") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get the stocks held by a fund (reverse lookup). + pub async fn stock_holdings( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/funds/stock-holdings") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .lists) + } + + // ----- user fund positions (scope: portfolio-asset) ----- + + /// Get the user's fund positions overview. + pub async fn positions( + &self, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds") + .query_params(options.into().unwrap_or_default()) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get the user's single fund position detail. + pub async fn position( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds/detail") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get the performance figures of a held fund. + pub async fn position_performance( + &self, + counter_id: impl Into, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds/performance") + .query_params(CounterIdsQuery::single(counter_id.into())) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .value) + } + + /// Get the cumulative-profit series of a held fund. + pub async fn position_profits( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds/profits") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get the net-value history of a held fund. + pub async fn position_nav( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds/nav-history") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .history_value) + } + + /// Get the dividend records of a held fund. + pub async fn position_dividends( + &self, + counter_id: impl Into, + options: impl Into>, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/asset/funds/dividends") + .query_params(CounterIdQuery { + counter_id: counter_id.into(), + options: options.into().unwrap_or_default(), + }) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + // ----- fund orders & trading (scope: order) ----- + + /// Get the user's fund orders (also serves as the trade/execution record). + pub async fn orders( + &self, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/orders") + .query_params(options.into().unwrap_or_default()) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .orders) + } + + /// Get a fund order detail. + pub async fn order(&self, order_id: i64) -> Result { + Ok(self + .0 + .http_cli + .request(Method::GET, format!("/v1/fund/orders/{order_id}")) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Get the user's fund transactions (cash-flow records). + pub async fn transactions( + &self, + options: impl Into>, + ) -> Result> { + Ok(self + .0 + .http_cli + .request(Method::GET, "/v1/fund/transactions") + .query_params(options.into().unwrap_or_default()) + .response::>>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0 + .list) + } + + /// Validate a fund order before submitting. + pub async fn validate_order( + &self, + options: ValidateFundOrderOptions, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::POST, "/v1/fund/orders/validate") + .body(Json(options)) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Submit a fund order (buy / sell). + pub async fn submit_order( + &self, + options: SubmitFundOrderOptions, + ) -> Result { + Ok(self + .0 + .http_cli + .request(Method::POST, "/v1/fund/orders") + .body(Json(options)) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await? + .0) + } + + /// Cancel (withdraw) a fund order. + pub async fn cancel_order(&self, order_id: i64) -> Result<()> { + #[derive(Debug, serde::Serialize)] + struct Req { + ut_id: i64, + } + #[derive(Deserialize)] + struct Resp { + #[serde(default)] + #[allow(dead_code)] + msg: String, + } + self.0 + .http_cli + .request(Method::POST, format!("/v1/fund/orders/{order_id}/cancel")) + .body(Json(Req { ut_id: order_id })) + .response::>() + .send() + .with_subscriber(self.0.log_subscriber.clone()) + .await?; + Ok(()) + } +} diff --git a/rust/src/fund/mod.rs b/rust/src/fund/mod.rs new file mode 100644 index 000000000..58f264ebc --- /dev/null +++ b/rust/src/fund/mod.rs @@ -0,0 +1,8 @@ +//! Fund (mutual fund) channel types and context. +mod context; +mod requests; +pub mod types; + +pub use context::FundContext; +pub use requests::*; +pub use types::*; diff --git a/rust/src/fund/requests.rs b/rust/src/fund/requests.rs new file mode 100644 index 000000000..0e0b87f29 --- /dev/null +++ b/rust/src/fund/requests.rs @@ -0,0 +1,994 @@ +//! Request option builders for the fund endpoints. +//! +//! The fund identifier is exposed as `counter_id` (e.g. `UT/FD/HK0000384492`). +//! Because it contains `/` it cannot live in the URL path, so endpoints that +//! target a single fund take `counter_id` as a separate method argument and +//! send it as the `counter_id` query parameter; these option structs only carry +//! the remaining query-string / request-body fields. + +use serde::Serialize; +use serde_json::Value; + +/// Options for the fund list ([`FundContext::funds`]). +/// +/// [`FundContext::funds`]: crate::fund::FundContext::funds +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundsOptions { + /// Server-defined filter object + #[serde(skip_serializing_if = "Option::is_none")] + filter: Option, + /// Quick-filter ids + #[serde(skip_serializing_if = "Vec::is_empty")] + quick_ids: Vec, + /// Earning-rate time intervals + #[serde(skip_serializing_if = "Vec::is_empty")] + time_interval: Vec, +} + +impl GetFundsOptions { + /// Create a new [`GetFundsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the filter object + #[inline] + #[must_use] + pub fn filter(self, filter: Value) -> Self { + Self { + filter: Some(filter), + ..self + } + } + + /// Set the quick-filter ids + #[inline] + #[must_use] + pub fn quick_ids(self, quick_ids: I) -> Self + where + I: IntoIterator, + { + Self { + quick_ids: quick_ids.into_iter().collect(), + ..self + } + } + + /// Set the earning-rate time intervals + #[inline] + #[must_use] + pub fn time_interval(self, time_interval: I) -> Self + where + I: IntoIterator, + T: Into, + { + Self { + time_interval: time_interval.into_iter().map(Into::into).collect(), + ..self + } + } +} + +/// Options for the fund analysis / trend / comparison endpoints. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundAnalysisOptions { + /// Analysis period + #[serde(skip_serializing_if = "Option::is_none")] + period: Option, +} + +impl GetFundAnalysisOptions { + /// Create a new [`GetFundAnalysisOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the analysis period + #[inline] + #[must_use] + pub fn period(mut self, period: i32) -> Self { + self.period = Some(period); + self + } +} + +/// Paging options (page / size). +#[derive(Debug, Default, Clone, Serialize)] +pub struct FundPageOptions { + /// Page number + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + /// Page size + #[serde(skip_serializing_if = "Option::is_none")] + size: Option, +} + +impl FundPageOptions { + /// Create a new [`FundPageOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the page number + #[inline] + #[must_use] + pub fn page(self, page: i32) -> Self { + Self { + page: Some(page), + ..self + } + } + + /// Set the page size + #[inline] + #[must_use] + pub fn size(self, size: i32) -> Self { + Self { + size: Some(size), + ..self + } + } +} + +/// Net-value range options (relative months / years before now). +#[derive(Debug, Default, Clone, Serialize)] +pub struct FundNavRangeOptions { + /// Number of months before now + #[serde(skip_serializing_if = "Option::is_none")] + month_before: Option, + /// Number of years before now + #[serde(skip_serializing_if = "Option::is_none")] + year_before: Option, +} + +impl FundNavRangeOptions { + /// Create a new [`FundNavRangeOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the number of months before now + #[inline] + #[must_use] + pub fn month_before(self, month_before: i32) -> Self { + Self { + month_before: Some(month_before), + ..self + } + } + + /// Set the number of years before now + #[inline] + #[must_use] + pub fn year_before(self, year_before: i32) -> Self { + Self { + year_before: Some(year_before), + ..self + } + } +} + +/// Options for the fund holdings endpoint. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundHoldingsOptions { + /// Scene + #[serde(skip_serializing_if = "Option::is_none")] + scene: Option, +} + +impl GetFundHoldingsOptions { + /// Create a new [`GetFundHoldingsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the scene + #[inline] + #[must_use] + pub fn scene(mut self, scene: i32) -> Self { + self.scene = Some(scene); + self + } +} + +/// Options for the fund stock-holdings (reverse) endpoint. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundStockHoldingsOptions { + /// Maximum number of stocks to return + #[serde(skip_serializing_if = "Option::is_none")] + limit: Option, +} + +impl GetFundStockHoldingsOptions { + /// Create a new [`GetFundStockHoldingsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the maximum number of stocks to return + #[inline] + #[must_use] + pub fn limit(mut self, limit: i32) -> Self { + self.limit = Some(limit); + self + } +} + +/// Options for the fund positions overview endpoint. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundPositionsOptions { + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, + /// Account id + #[serde(skip_serializing_if = "Option::is_none")] + aaid: Option, +} + +impl GetFundPositionsOptions { + /// Create a new [`GetFundPositionsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } + + /// Set the account id + #[inline] + #[must_use] + pub fn aaid(self, aaid: i64) -> Self { + Self { + aaid: Some(aaid), + ..self + } + } +} + +/// Options for a single fund position detail. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundPositionOptions { + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, + /// Account id + #[serde(skip_serializing_if = "Option::is_none")] + aaid: Option, + /// Range start + #[serde(skip_serializing_if = "Option::is_none")] + start: Option, + /// Range end + #[serde(skip_serializing_if = "Option::is_none")] + end: Option, +} + +impl GetFundPositionOptions { + /// Create a new [`GetFundPositionOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } + + /// Set the account id + #[inline] + #[must_use] + pub fn aaid(self, aaid: i64) -> Self { + Self { + aaid: Some(aaid), + ..self + } + } + + /// Set the range start + #[inline] + #[must_use] + pub fn start(self, start: impl Into) -> Self { + Self { + start: Some(start.into()), + ..self + } + } + + /// Set the range end + #[inline] + #[must_use] + pub fn end(self, end: impl Into) -> Self { + Self { + end: Some(end.into()), + ..self + } + } +} + +/// Options for a single fund position cumulative-profit list. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundPositionProfitsOptions { + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, + /// Account id + #[serde(skip_serializing_if = "Option::is_none")] + aaid: Option, + /// Range start + #[serde(skip_serializing_if = "Option::is_none")] + start: Option, + /// Range end + #[serde(skip_serializing_if = "Option::is_none")] + end: Option, + /// Page number + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + /// Page size + #[serde(skip_serializing_if = "Option::is_none")] + size: Option, +} + +impl GetFundPositionProfitsOptions { + /// Create a new [`GetFundPositionProfitsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } + + /// Set the account id + #[inline] + #[must_use] + pub fn aaid(self, aaid: i64) -> Self { + Self { + aaid: Some(aaid), + ..self + } + } + + /// Set the range start + #[inline] + #[must_use] + pub fn start(self, start: impl Into) -> Self { + Self { + start: Some(start.into()), + ..self + } + } + + /// Set the range end + #[inline] + #[must_use] + pub fn end(self, end: impl Into) -> Self { + Self { + end: Some(end.into()), + ..self + } + } + + /// Set the page number + #[inline] + #[must_use] + pub fn page(self, page: i32) -> Self { + Self { + page: Some(page), + ..self + } + } + + /// Set the page size + #[inline] + #[must_use] + pub fn size(self, size: i32) -> Self { + Self { + size: Some(size), + ..self + } + } +} + +/// Options for a single fund position dividend list. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundPositionDividendsOptions { + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, + /// Account id + #[serde(skip_serializing_if = "Option::is_none")] + aaid: Option, + /// Currency + #[serde(skip_serializing_if = "Option::is_none")] + currency: Option, + /// Range start (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + start: Option, + /// Range end (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + end: Option, + /// Page number + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + /// Page size + #[serde(skip_serializing_if = "Option::is_none")] + size: Option, +} + +impl GetFundPositionDividendsOptions { + /// Create a new [`GetFundPositionDividendsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } + + /// Set the account id + #[inline] + #[must_use] + pub fn aaid(self, aaid: i64) -> Self { + Self { + aaid: Some(aaid), + ..self + } + } + + /// Set the currency + #[inline] + #[must_use] + pub fn currency(self, currency: impl Into) -> Self { + Self { + currency: Some(currency.into()), + ..self + } + } + + /// Set the range start (unix seconds) + #[inline] + #[must_use] + pub fn start(self, start: i64) -> Self { + Self { + start: Some(start), + ..self + } + } + + /// Set the range end (unix seconds) + #[inline] + #[must_use] + pub fn end(self, end: i64) -> Self { + Self { + end: Some(end), + ..self + } + } + + /// Set the page number + #[inline] + #[must_use] + pub fn page(self, page: i32) -> Self { + Self { + page: Some(page), + ..self + } + } + + /// Set the page size + #[inline] + #[must_use] + pub fn size(self, size: i32) -> Self { + Self { + size: Some(size), + ..self + } + } +} + +/// Options for the fund orders list. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundOrdersOptions { + /// Filter by fund counter ids + #[serde(rename = "counter_id", skip_serializing_if = "Vec::is_empty")] + counter_ids: Vec, + /// Filter by actions (comma-separated) + #[serde(skip_serializing_if = "Option::is_none")] + actions: Option, + /// Filter by states (comma-separated) + #[serde(skip_serializing_if = "Option::is_none")] + states: Option, + /// Filter by currency + #[serde(skip_serializing_if = "Option::is_none")] + currency: Option, + /// Range start (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + start: Option, + /// Range end (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + end: Option, + /// Page number + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + /// Page size + #[serde(skip_serializing_if = "Option::is_none")] + size: Option, +} + +impl GetFundOrdersOptions { + /// Create a new [`GetFundOrdersOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Filter by fund counter ids + #[inline] + #[must_use] + pub fn counter_ids(self, counter_ids: I) -> Self + where + I: IntoIterator, + T: Into, + { + Self { + counter_ids: counter_ids.into_iter().map(Into::into).collect(), + ..self + } + } + + /// Filter by actions + #[inline] + #[must_use] + pub fn actions(self, actions: impl Into) -> Self { + Self { + actions: Some(actions.into()), + ..self + } + } + + /// Filter by states + #[inline] + #[must_use] + pub fn states(self, states: impl Into) -> Self { + Self { + states: Some(states.into()), + ..self + } + } + + /// Filter by currency + #[inline] + #[must_use] + pub fn currency(self, currency: impl Into) -> Self { + Self { + currency: Some(currency.into()), + ..self + } + } + + /// Set the range start (unix seconds) + #[inline] + #[must_use] + pub fn start(self, start: i64) -> Self { + Self { + start: Some(start), + ..self + } + } + + /// Set the range end (unix seconds) + #[inline] + #[must_use] + pub fn end(self, end: i64) -> Self { + Self { + end: Some(end), + ..self + } + } + + /// Set the page number + #[inline] + #[must_use] + pub fn page(self, page: i32) -> Self { + Self { + page: Some(page), + ..self + } + } + + /// Set the page size + #[inline] + #[must_use] + pub fn size(self, size: i32) -> Self { + Self { + size: Some(size), + ..self + } + } +} + +/// Options for the fund transactions (cash-flow) list. +#[derive(Debug, Default, Clone, Serialize)] +pub struct GetFundTransactionsOptions { + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, + /// Business type + #[serde(skip_serializing_if = "Option::is_none")] + business_type: Option, + /// Category + #[serde(skip_serializing_if = "Option::is_none")] + category: Option, + /// Currencies (comma-separated) + #[serde(skip_serializing_if = "Option::is_none")] + currencies: Option, + /// Range start (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + start: Option, + /// Range end (unix seconds) + #[serde(skip_serializing_if = "Option::is_none")] + end: Option, + /// Page number + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + /// Page size + #[serde(skip_serializing_if = "Option::is_none")] + size: Option, +} + +impl GetFundTransactionsOptions { + /// Create a new [`GetFundTransactionsOptions`] + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } + + /// Set the business type + #[inline] + #[must_use] + pub fn business_type(self, business_type: impl Into) -> Self { + Self { + business_type: Some(business_type.into()), + ..self + } + } + + /// Set the category + #[inline] + #[must_use] + pub fn category(self, category: impl Into) -> Self { + Self { + category: Some(category.into()), + ..self + } + } + + /// Set the currencies + #[inline] + #[must_use] + pub fn currencies(self, currencies: impl Into) -> Self { + Self { + currencies: Some(currencies.into()), + ..self + } + } + + /// Set the range start (unix seconds) + #[inline] + #[must_use] + pub fn start(self, start: i64) -> Self { + Self { + start: Some(start), + ..self + } + } + + /// Set the range end (unix seconds) + #[inline] + #[must_use] + pub fn end(self, end: i64) -> Self { + Self { + end: Some(end), + ..self + } + } + + /// Set the page number + #[inline] + #[must_use] + pub fn page(self, page: i32) -> Self { + Self { + page: Some(page), + ..self + } + } + + /// Set the page size + #[inline] + #[must_use] + pub fn size(self, size: i32) -> Self { + Self { + size: Some(size), + ..self + } + } +} + +/// Options for validating a fund order. +#[derive(Debug, Clone, Serialize)] +pub struct ValidateFundOrderOptions { + /// Fund counter id + counter_id: String, + /// Action (buy/sell) + action: String, + /// Currency + currency: String, + /// Amount (for amount-based orders) + #[serde(skip_serializing_if = "Option::is_none")] + amount: Option, + /// Units (for unit-based orders) + #[serde(skip_serializing_if = "Option::is_none")] + units: Option, + /// Dividend option + #[serde(skip_serializing_if = "Option::is_none")] + dividend_option: Option, + /// Fund source + #[serde(skip_serializing_if = "Option::is_none")] + fund_source: Option, + /// Account channel + #[serde(skip_serializing_if = "Option::is_none")] + account_channel: Option, +} + +impl ValidateFundOrderOptions { + /// Create a new [`ValidateFundOrderOptions`] + #[inline] + #[must_use] + pub fn new( + counter_id: impl Into, + action: impl Into, + currency: impl Into, + ) -> Self { + Self { + counter_id: counter_id.into(), + action: action.into(), + currency: currency.into(), + amount: None, + units: None, + dividend_option: None, + fund_source: None, + account_channel: None, + } + } + + /// Set the amount + #[inline] + #[must_use] + pub fn amount(self, amount: impl Into) -> Self { + Self { + amount: Some(amount.into()), + ..self + } + } + + /// Set the units + #[inline] + #[must_use] + pub fn units(self, units: impl Into) -> Self { + Self { + units: Some(units.into()), + ..self + } + } + + /// Set the dividend option + #[inline] + #[must_use] + pub fn dividend_option(self, dividend_option: i32) -> Self { + Self { + dividend_option: Some(dividend_option), + ..self + } + } + + /// Set the fund source + #[inline] + #[must_use] + pub fn fund_source(self, fund_source: i32) -> Self { + Self { + fund_source: Some(fund_source), + ..self + } + } + + /// Set the account channel + #[inline] + #[must_use] + pub fn account_channel(self, account_channel: impl Into) -> Self { + Self { + account_channel: Some(account_channel.into()), + ..self + } + } +} + +/// Options for submitting a fund order. +#[derive(Debug, Clone, Serialize)] +pub struct SubmitFundOrderOptions { + /// Fund counter id + counter_id: String, + /// Action (buy/sell) + action: String, + /// Currency + currency: String, + /// Amount (for amount-based orders) + #[serde(skip_serializing_if = "Option::is_none")] + amount: Option, + /// Units (for unit-based orders) + #[serde(skip_serializing_if = "Option::is_none")] + units: Option, + /// Dividend option + #[serde(skip_serializing_if = "Option::is_none")] + dividend_option: Option, + /// Fee + #[serde(skip_serializing_if = "Option::is_none")] + fee: Option, + /// Whether to sell all + #[serde(skip_serializing_if = "Option::is_none")] + is_sell_all: Option, + /// Remark + #[serde(skip_serializing_if = "Option::is_none")] + remark: Option, + /// Trade method + #[serde(skip_serializing_if = "Option::is_none")] + trade_method: Option, +} + +impl SubmitFundOrderOptions { + /// Create a new [`SubmitFundOrderOptions`] + #[inline] + #[must_use] + pub fn new( + counter_id: impl Into, + action: impl Into, + currency: impl Into, + ) -> Self { + Self { + counter_id: counter_id.into(), + action: action.into(), + currency: currency.into(), + amount: None, + units: None, + dividend_option: None, + fee: None, + is_sell_all: None, + remark: None, + trade_method: None, + } + } + + /// Set the amount + #[inline] + #[must_use] + pub fn amount(self, amount: impl Into) -> Self { + Self { + amount: Some(amount.into()), + ..self + } + } + + /// Set the units + #[inline] + #[must_use] + pub fn units(self, units: impl Into) -> Self { + Self { + units: Some(units.into()), + ..self + } + } + + /// Set the dividend option + #[inline] + #[must_use] + pub fn dividend_option(self, dividend_option: i32) -> Self { + Self { + dividend_option: Some(dividend_option), + ..self + } + } + + /// Set the fee + #[inline] + #[must_use] + pub fn fee(self, fee: impl Into) -> Self { + Self { + fee: Some(fee.into()), + ..self + } + } + + /// Set whether to sell all + #[inline] + #[must_use] + pub fn is_sell_all(self, is_sell_all: bool) -> Self { + Self { + is_sell_all: Some(is_sell_all), + ..self + } + } + + /// Set the remark + #[inline] + #[must_use] + pub fn remark(self, remark: impl Into) -> Self { + Self { + remark: Some(remark.into()), + ..self + } + } + + /// Set the trade method + #[inline] + #[must_use] + pub fn trade_method(self, trade_method: i32) -> Self { + Self { + trade_method: Some(trade_method), + ..self + } + } +} diff --git a/rust/src/fund/types.rs b/rust/src/fund/types.rs new file mode 100644 index 000000000..39da061bd --- /dev/null +++ b/rust/src/fund/types.rs @@ -0,0 +1,1023 @@ +//! Fund (mutual fund) types. +//! +//! Response models for the fund channel OpenAPI endpoints. Numeric fields that +//! arrive as strings on the wire are kept as [`String`] to preserve the exact +//! server formatting; unix-second/millisecond timestamps are kept as `i64`. +//! Every struct uses `#[serde(default)]` so missing fields decode to their +//! default instead of failing. + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::serde_utils; + +/// A fund net-asset-value data point (latest / historical). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundNavValue { + /// Net value change + pub change: String, + /// Net value change percent + pub change_percent: String, + /// Formatted change percent + pub change_percent_format: String, + /// Fund counter id + pub counter_id: String, + /// Fund name + pub counter_name: String, + /// Currency + pub currency: String, + /// Formatted date + pub date_format: String, + /// ISIN + pub isin: String, + /// Last update time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub last_update_time: i64, + /// Net value + pub value: String, + /// Formatted net value + pub value_format: String, +} + +/// A recent performance point used by the hot-fund list. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPerformancePoint { + /// Date + pub date: String, + /// Last done value + pub last_done: String, +} + +/// A hot-selling fund entry. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct HotFund { + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: String, + /// Fund counter id + pub counter_id: String, + /// Currency + pub currency: String, + /// Earning rate + pub earning_rate: String, + /// Recent performance points + pub fund_performances: Vec, + /// Fund name + pub name: String, + /// Minimum purchase amount + pub purchase_amount: String, + /// Recommendation text + pub recommendation_text: String, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: String, + /// Time interval of the earning rate + pub time_interval: String, +} + +/// A fund entry in the fund list. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundBrief { + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: String, + /// Fund code + pub code: String, + /// Fund counter id + pub counter_id: String, + /// Currency + pub currency: String, + /// Description + pub description: String, + /// Earning rate + pub earning_rate: String, + /// Whether the user is holding this fund + pub holding: bool, + /// ISIN + pub isin: String, + /// Fund name + pub name: String, + /// Product + pub product: String, + /// Minimum purchase amount + pub purchase_amount: String, + /// Recommendation text + pub recommendation_text: String, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: String, + /// Time interval of the earning rate + pub time_interval: String, + /// Unit value + pub unit_value: String, +} + +/// Fund list filter options. Each list holds server-defined option objects. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundFilters { + /// Asset class options + pub asset_class: Vec, + /// Company options + pub company: Vec, + /// Currency options + pub currency: Vec, + /// Industry category options + pub industry_category_name: Vec, + /// Risk level options + pub risk_level: Vec, +} + +/// A single holding entry inside a fund's asset allocation. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundAssetAllocationItem { + /// Security code + pub code: String, + /// Security counter id + pub counter_id: String, + /// Name + pub name: String, + /// Position ratio + pub position_ratio: String, +} + +/// A fund's asset allocation. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundAssetAllocation { + /// Asset type + pub asset_type: i32, + /// Allocation entries + pub lists: Vec, + /// Report date + pub report_date: String, +} + +/// Fund detail. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundDetail { + /// Additional purchase amount + pub additional_purchase_amount: String, + /// Affirm day + pub affirm_day: i32, + /// Amount affirm day + pub amount_affirm_day: String, + /// Asset allocation + #[serde(default)] + pub asset_allocation: Option, + /// Asset class + pub asset_class: i32, + /// Asset class name + pub asset_class_name: String, + /// Bill purchase rate + pub bill_purchase_rate: String, + /// Channel + pub channel: String, + /// Close period + pub close_period: String, + /// Fund code + pub code: String, + /// Currency + pub currency: String, + /// Cut off time + pub cut_off_time: String, + /// Whether it is a derivative + pub derivatives: bool, + /// Done day + pub done_day: i32, + /// Excess return fee + pub excess_return_fee: String, + /// GST rate + pub gst_rate: String, + /// Introduction + pub introduce: String, + /// Whether it is a cash-plus fund + pub is_cash_plus: bool, + /// Whether it is a complex product + pub is_complex: bool, + /// Whether it is a new cash-plus fund + pub is_new_cash_plus: bool, + /// Whether it is a Yinghebao fund + pub is_yinghebao: bool, + /// ISIN + pub isin: String, + /// Management rate + pub manage_rate: String, + /// Manager + pub manager: String, + /// Minimum holding cash + pub min_hold_cash: String, + /// Minimum holding share + pub min_hold_share: String, + /// Minimum sell share + pub min_sell_share: String, + /// Month raise day + pub month_raise_day: String, + /// Fund name + pub name: String, + /// Net value deadline + pub nav_deadline: String, + /// Whether it is no-load + pub no_load: bool, + /// Open date + pub open_date: String, + /// Open period + pub open_period: String, + /// Product + pub product: String, + /// Product information locals + pub product_information_locals: String, + /// Profile + pub profile: String, + /// Whether purchasable + pub purchasable: i32, + /// Purchase affirm day + pub purchase_affirm_day: String, + /// Minimum purchase amount + pub purchase_amount: String, + /// Purchase rate + pub purchase_rate: String, + /// Rating + pub rating: i32, + /// Whether redeemable + pub redeemable: i32, + /// Redemption advance day + pub redemption_advance_day: String, + /// Redemption amount + pub redemption_amount: String, + /// Redemption close period text + pub redemption_close_period_shows: String, + /// Redemption done day + pub redemption_done_day: String, + /// Redemption open day text + pub redemption_open_day_shows: String, + /// Risk level + pub risk_level: i32, + /// Risk level name + pub risk_level_name: String, + /// Verify status + pub verify_status: i32, + /// Whether it is a virtual currency fund + pub virtual_currency: bool, + /// Year to date yield + pub year_to_date_yield: String, + /// Year to date yield type + pub ytd_yield_type: i32, +} + +/// Fund analysis (level 1). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundAnalysis { + /// Actual period + pub actual_period: i32, + /// Cost level (server-defined structure) + pub cost_level: Value, + /// Return ability (server-defined structure) + pub return_ability: Value, + /// Risk ability (server-defined structure) + pub risk_ability: Value, + /// Updated at + pub updated_at: String, + /// Value for money (server-defined structure) + pub value_for_money: Value, + /// Whether visible + pub visible: bool, +} + +/// Fund analysis detail (level 2). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundAnalysisDetail { + /// Actual period + pub actual_period: i32, + /// Available periods + pub available_periods: Vec, + /// Cost level (server-defined structure) + pub cost_level: Value, + /// Return ability (server-defined structure) + pub return_ability: Value, + /// Risk ability (server-defined structure) + pub risk_ability: Value, + /// Updated at + pub updated_at: String, + /// Value for money (server-defined structure) + pub value_for_money: Value, + /// Whether visible + pub visible: bool, +} + +/// A benchmark contrast series in a fund trend chart. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundTrendContrast { + /// Benchmark name + pub benchmark_name: String, + /// Performance points (server-defined structure) + pub performances: Vec, +} + +/// Fund trend chart. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundTrend { + /// Actual period + pub actual_period: i32, + /// Available periods + pub available_periods: Vec, + /// Category average performances (server-defined structure) + pub category_average_performances: Vec, + /// Benchmark contrast performances + #[serde(default)] + pub contrast_performances: Option, + /// Fund performances (server-defined structure) + pub fund_performances: Vec, +} + +/// A named contrast performance series. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundNamedContrast { + /// Series name + pub name: String, + /// Performance points (server-defined structure) + pub performances: Vec, +} + +/// Fund performance comparison. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPerformanceComparison { + /// Contrast performance series + pub contrast_performances: Vec, + /// Fund performances (server-defined structure) + pub fund_performances: Vec, +} + +/// A fund annual return entry. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundAnnualReturn { + /// Change percent + pub change_percent: String, + /// Year + pub year: i32, +} + +/// A fund quarterly return entry. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundQuarterlyReturn { + /// Change percent + pub change_percent: String, + /// Quarter + pub quarter: i32, + /// Year + pub year: i32, +} + +/// A fund's detailed performance figures. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPerformance { + /// Annualized return (5y) + pub annualized_return_five: String, + /// Annualized return (1y) + pub annualized_return_one: String, + /// Annualized return (10y) + pub annualized_return_ten: String, + /// Annualized return (3y) + pub annualized_return_three: String, + /// Annualized return (2y) + pub annualized_return_two: String, + /// Fund counter id + pub counter_id: String, + /// Fund name + pub fund_name: String, + /// Rank (5y) + pub performance_rank_five_years: i32, + /// Rank (1d) + pub performance_rank_one_day: i32, + /// Rank (1m) + pub performance_rank_one_month: i32, + /// Rank (1w) + pub performance_rank_one_week: i32, + /// Rank (1y) + pub performance_rank_one_year: i32, + /// Rank (6m) + pub performance_rank_six_months: i32, + /// Rank (10y) + pub performance_rank_ten_years: i32, + /// Rank (3m) + pub performance_rank_three_months: i32, + /// Rank (3y) + pub performance_rank_three_years: i32, + /// Rank (2y) + pub performance_rank_two_years: i32, + /// Rank (ytd) + pub performance_rank_ytd: i32, + /// Return (5y) + pub performance_return_five_years: String, + /// Return (1d) + pub performance_return_one_day: String, + /// Return (1m) + pub performance_return_one_month: String, + /// Return (1w) + pub performance_return_one_week: String, + /// Return (1y) + pub performance_return_one_year: String, + /// Return (6m) + pub performance_return_six_months: String, + /// Return (10y) + pub performance_return_ten_years: String, + /// Return (3m) + pub performance_return_three_months: String, + /// Return (3y) + pub performance_return_three_years: String, + /// Return (2y) + pub performance_return_two_years: String, + /// Return (ytd) + pub performance_return_ytd: String, + /// Total peers (5y) + pub performance_total_five_years: i32, + /// Total peers (1d) + pub performance_total_one_day: i32, + /// Total peers (1m) + pub performance_total_one_month: i32, + /// Total peers (1w) + pub performance_total_one_week: i32, + /// Total peers (1y) + pub performance_total_one_year: i32, + /// Total peers (6m) + pub performance_total_six_months: i32, + /// Total peers (10y) + pub performance_total_ten_years: i32, + /// Total peers (3m) + pub performance_total_three_months: i32, + /// Total peers (3y) + pub performance_total_three_years: i32, + /// Total peers (2y) + pub performance_total_two_years: i32, + /// Total peers (ytd) + pub performance_total_ytd: i32, + /// Seven days annualized + pub seven_days_annualized: String, + /// Ten thousand price + pub ten_thousand_price: String, + /// Update time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub update_time: i64, +} + +/// A single fund holding (top-10 holdings). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundHolding { + /// Bond type + pub bond_type: String, + /// Bond type name + pub bond_type_name: String, + /// Country name + pub country_name: String, + /// Holding type + pub holding_type: String, + /// Industry name + pub industry_name: String, + /// Market value + pub market_value: String, + /// Maturity date + pub maturity_date: String, + /// Name + pub name: String, + /// Share change + pub share_change: String, + /// Share change percent + pub share_change_percent: String, + /// Shares + pub shares: String, + /// Weighting + pub weighting: String, +} + +/// A fund's top-10 holdings. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundHoldings { + /// Holding entries + pub holdings: Vec, + /// Report date + pub report_date: String, + /// Total weighting + pub weighting: String, +} + +/// A stock held by the fund (reverse lookup). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundStockHolding { + /// Stock code + pub code: String, + /// Stock counter id + pub counter_id: String, + /// Currency + pub currency: String, + /// Stock name + pub name: String, + /// Position ratio + pub position_ratio: String, + /// Report date + pub report_date: String, +} + +/// A single fund position held by the user. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPosition { + /// Holding amount + pub amount: String, + /// Fund counter id + pub counter_id: String, + /// Currency + pub currency: String, + /// Frozen units + pub freeze_units: String, + /// Holding profit + pub holding_profit: String, + /// Holding units + pub holding_units: String, + /// Fund name + pub name: String, + /// Recent profit + pub recent_profit: String, + /// Recent trading day (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub recent_trading_day: i64, + /// Accumulated recent profit + pub sum_recent_profit: String, +} + +/// The user's fund positions overview. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositions { + /// Account channel + pub account_channel: String, + /// Position entries + pub list: Vec, + /// Pending buy orders amount + pub pending_buy_orders: String, + /// Recent trading day (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub recent_trading_day: i64, + /// Sold pending credit orders amount + pub sold_pending_credit_orders: String, +} + +/// A dated value point. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundDatedValue { + /// Date (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub date: i64, + /// Value + pub value: String, +} + +/// A fund unit-value point (position view). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundUnitValue { + /// Date (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub date: i64, + /// Day increase rate + pub day_increase_rate: String, + /// Total value + pub total_value: String, + /// Unit value + pub unit_value: String, +} + +/// Detail values of a single fund position. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositionDetailValues { + /// Amount + pub amount: String, + /// Currency + pub currency: String, + /// Holding cost + pub holding_cost: String, + /// Holding profit + pub holding_profit: String, + /// Holding profit rate + pub holding_profit_rate: String, + /// Holding units + pub holding_units: String, + /// Holding value + pub holding_value: String, + /// Pending buy value + pub pending_buy_value: String, + /// Pending sell value + pub pending_sell_value: String, + /// Accumulated profit (to date) + pub profit_amount_accum_td: String, + /// Accumulated profit rate (to date) + pub profit_amount_accum_td_rate: String, + /// Recent profit + pub recent_profit: String, + /// Recent trading day (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub recent_tradingday: i64, + /// Recent unit value + pub recent_unit_value: String, + /// Sold pending-confirm units + pub sold_pending_confirm_units: String, +} + +/// Detail of a single fund position. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositionDetail { + /// Detail values + #[serde(default)] + pub detail_values: Option, + /// Accumulated profit series + pub sum_profit: Vec, + /// Unit value series + pub ut_value: Vec, +} + +/// Performance figures for a held fund. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositionPerformance { + /// Annualized return (5y) + pub annualized_return_five: String, + /// Annualized return (1y) + pub annualized_return_one: String, + /// Annualized return (10y) + pub annualized_return_ten: String, + /// Annualized return (3y) + pub annualized_return_three: String, + /// Annualized return (2y) + pub annualized_return_two: String, + /// Fund counter id + pub counter_id: String, + /// Fund name + pub fund_name: String, + /// Return (5y) + pub performance_return_five_years: String, + /// Return (1d) + pub performance_return_one_day: String, + /// Return (1m) + pub performance_return_one_month: String, + /// Return (1w) + pub performance_return_one_week: String, + /// Return (1y) + pub performance_return_one_year: String, + /// Return (6m) + pub performance_return_six_months: String, + /// Return (10y) + pub performance_return_ten_years: String, + /// Return (3m) + pub performance_return_three_months: String, + /// Return (3y) + pub performance_return_three_years: String, + /// Return (2y) + pub performance_return_two_years: String, + /// Return (ytd) + pub performance_return_ytd: String, + /// Update time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub update_time: i64, +} + +/// The user's cumulative profit for a held fund. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositionProfits { + /// Currency + pub currency: String, + /// Profit series + pub history_value: Vec, + /// Last update time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub last_update_time: i64, + /// Total profit + pub sum_profit: String, +} + +/// A held-fund net-value point (position view). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundPositionNav { + /// Net value change + pub change: String, + /// Net value change percent + pub change_percent: String, + /// Fund counter id + pub counter_id: String, + /// Fund name + pub counter_name: String, + /// Last update time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub last_update_time: i64, + /// Net value + pub value: String, +} + +/// A cash dividend record for a held fund. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundDividend { + /// Amount + pub amount: String, + /// Fund counter id + pub counter_id: String, + /// Currency + pub currency: String, + /// Date (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub date: i64, + /// Dividend method + pub div_method: String, + /// Fund name + pub name: String, +} + +/// The user's dividend records for a held fund. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundDividends { + /// Currency + pub currency: String, + /// Dividend records + pub div_cash_infos: Vec, + /// Latest dividend date (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub lastest_date: i64, + /// Total cash dividend + pub total_div_cash: String, +} + +/// A fund order (list view). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrder { + /// Action (buy/sell) + pub action: String, + /// Amount + pub amount: String, + /// Fund counter id + pub counter_id: String, + /// Created at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub created_at: i64, + /// Currency + pub currency: String, + /// Fund name + pub fund_name: String, + /// Order id + #[serde(with = "serde_utils::int64_str")] + pub id: i64, + /// Whether it is an auto (DCA) order + pub is_auto: bool, + /// Net worth + pub net_worth: String, + /// Product type + pub product_type: String, + /// State + pub state: String, + /// State description + pub state_desc: String, + /// Units + pub units: String, +} + +/// A keyword block in a fund order detail. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderKeyword { + /// Content + pub content: String, + /// Group + pub group: String, + /// Key + pub key: String, + /// Line strategy + pub line_strategy: String, + /// Title + pub title: String, +} + +/// A processing stage in a fund order detail. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderStage { + /// Description + pub desc: String, + /// Key + pub key: String, + /// Link + pub link: String, + /// Link text + pub link_text: String, + /// Progress + pub progress: String, + /// Stage + pub stage: String, +} + +/// The full information of a fund order. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderInfo { + /// Account id + #[serde(with = "serde_utils::int64_str")] + pub aaid: i64, + /// Account channel + pub account_channel: String, + /// Action (buy/sell) + pub action: String, + /// Amount + pub amount: String, + /// Channel + pub channel: String, + /// Fund counter id + pub counter_id: String, + /// Created at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub created_at: i64, + /// Currency + pub currency: String, + /// Dividend option + pub dividend_option: String, + /// Equity time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub eq_at: i64, + /// Fee + pub fee: String, + /// Fund name + pub fund_name: String, + /// Fund source + pub fund_source: String, + /// Histories + pub histories: String, + /// Order id + #[serde(with = "serde_utils::int64_str")] + pub id: i64, + /// Message + pub message: String, + /// Net worth + pub net_worth: String, + /// Price time (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub price_at: i64, + /// Processed at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub processed_at: i64, + /// Product type + pub product_type: String, + /// Whether repurchaseable + pub repurchaseable: bool, + /// Sale proceeds + pub sale_proceeds: String, + /// Sales charge + pub sales_charge: String, + /// Sales price + pub sales_price: String, + /// Sales unit + pub sales_unit: String, + /// State + pub state: String, + /// State description + pub state_desc: String, + /// Status + pub status: i32, + /// Extended status + pub status_ex: i32, + /// T+ description + pub t_description: String, + /// Time partition + pub time_partition: String, + /// Total amount + pub total_amount: String, + /// Transaction at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub transaction_at: i64, + /// Units + pub units: String, + /// Withdraw at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub withdraw_at: i64, + /// Whether withdrawable + pub withdrawable: bool, +} + +/// Fund order detail. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderDetail { + /// Keyword blocks + pub keywords: Vec, + /// The order + #[serde(default)] + pub order: Option, + /// Processing stages + pub stages: Vec, +} + +/// A fund transaction / cash-flow record. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundTransaction { + /// Amount + pub amount: String, + /// Category + pub category: String, + /// Created at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub created_at: i64, + /// Currency + pub currency: String, + /// Description + pub description: String, + /// Detail created at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub detail_created_at: i64, + /// Detail type + pub detail_type: String, + /// Done at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub done_at: i64, + /// Quantity description + pub quantity_description: String, + /// Redirect page + pub redirect_page: String, + /// Redirect page (v2) + pub redirect_page_v2: String, + /// Reference number + pub ref_no: String, + /// Stock quantity + pub stock_quantity: String, + /// Transaction type + pub tx_type: String, + /// Type name + pub type_name: String, +} + +/// The result of validating a fund order. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderValidation { + /// Auth token to carry into submit + pub auth_token: String, + /// Risk-assessment eval address + pub eval_address: String, + /// Fund risk level + pub fund_risk_level: i32, + /// Message + pub msg: String, + /// User PI status + pub user_pi: i32, + /// User risk level + pub user_risk_level: i32, +} + +/// The result of submitting a fund order. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct FundOrderSubmitResponse { + /// Action (buy/sell) + pub action: String, + /// Amount + pub amount: String, + /// Fund counter id + pub counter_id: String, + /// Created at (unix seconds) + #[serde(with = "serde_utils::int64_str")] + pub created_at: i64, + /// Fund name + pub fund_name: String, + /// Order id + #[serde(with = "serde_utils::int64_str")] + pub id: i64, + /// Message + pub msg: String, + /// Status + pub status: i32, + /// Units + pub units: String, +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c5078705e..4f99bc0de 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -26,6 +26,7 @@ pub mod asset; pub mod calendar; pub mod content; pub mod dca; +pub mod fund; pub mod fundamental; pub mod grid; pub mod market; @@ -44,6 +45,7 @@ pub use config::{Config, Language, PushCandlestickMode}; pub use content::ContentContext; pub use dca::DCAContext; pub use error::{Error, Result, SimpleError, SimpleErrorKind}; +pub use fund::FundContext; pub use fundamental::FundamentalContext; // ── US-market type re-exports ───────────────────────────────────────────────── pub use fundamental::types::{ diff --git a/rust/src/serde_utils.rs b/rust/src/serde_utils.rs index bdcaa9290..0af2d7ca4 100644 --- a/rust/src/serde_utils.rs +++ b/rust/src/serde_utils.rs @@ -436,8 +436,19 @@ pub(crate) mod int64_str { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Ok(value.parse::().unwrap_or_default()) + // The backend may send an int64 as either a JSON string or a JSON + // number; accept both so a numeric value never fails the whole + // response. + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrInt { + String(String), + Int(i64), + } + match StringOrInt::deserialize(deserializer)? { + StringOrInt::Int(n) => Ok(n), + StringOrInt::String(s) => Ok(s.parse::().unwrap_or_default()), + } } } @@ -614,3 +625,45 @@ pub(crate) mod value_as_opt_string { } } } + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + #[derive(Deserialize)] + struct Wrap { + #[serde(with = "super::int64_str")] + value: i64, + } + + #[test] + fn int64_str_accepts_string_and_number() { + // The backend may send an int64 as a quoted string or a bare number; + // both must deserialize to the same value. + assert_eq!( + serde_json::from_str::(r#"{"value":"123"}"#) + .unwrap() + .value, + 123 + ); + assert_eq!( + serde_json::from_str::(r#"{"value":123}"#) + .unwrap() + .value, + 123 + ); + // Negative values pass through; an unparseable string falls back to 0. + assert_eq!( + serde_json::from_str::(r#"{"value":-9}"#) + .unwrap() + .value, + -9 + ); + assert_eq!( + serde_json::from_str::(r#"{"value":""}"#) + .unwrap() + .value, + 0 + ); + } +} diff --git a/rust/src/trade/types.rs b/rust/src/trade/types.rs index fbd7b0ce5..72ee15398 100644 --- a/rust/src/trade/types.rs +++ b/rust/src/trade/types.rs @@ -897,8 +897,9 @@ pub struct FundPositionChannel { /// Fund position #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FundPosition { - /// Fund ISIN code - pub symbol: String, + /// Fund counter id (the ISIN is the last `/`-separated segment) + #[serde(alias = "symbol")] + pub counter_id: String, /// Current equity #[serde(with = "serde_utils::decimal_empty_is_0")] pub current_net_asset_value: Decimal, @@ -1677,7 +1678,7 @@ mod tests { "list": [{ "account_channel": "lb", "fund_info": [{ - "symbol": "HK0000447943", + "counter_id": "UT/FD/HK0000447943", "symbol_name": "高腾亚洲收益基金", "currency": "USD", "holding_units": "5.000", @@ -1697,13 +1698,19 @@ mod tests { assert_eq!(channel.positions.len(), 1); let position = &channel.positions[0]; - assert_eq!(position.symbol, "HK0000447943"); + assert_eq!(position.counter_id, "UT/FD/HK0000447943"); assert_eq!(position.symbol_name, "高腾亚洲收益基金"); assert_eq!(position.currency, "USD"); assert_eq!(position.current_net_asset_value, decimal!(0i32)); assert_eq!(position.cost_net_asset_value, decimal!(0i32)); assert_eq!(position.holding_units, decimal!(5i32)); assert_eq!(position.net_asset_value_day, datetime!(2022-4-14 0:00 +8)); + + // Transition compatibility: the legacy `symbol` key (which carried a + // bare ISIN) still deserializes into `counter_id` via the serde alias. + let legacy = r#"{"list":[{"account_channel":"lb","fund_info":[{"symbol":"HK0000447943","symbol_name":"x","currency":"USD","holding_units":"0","current_net_asset_value":"0","cost_net_asset_value":"0","net_asset_value_day":"1649865600"}]}]}"#; + let resp: FundPositionsResponse = serde_json::from_str(legacy).unwrap(); + assert_eq!(resp.channels[0].positions[0].counter_id, "HK0000447943"); } #[test]