Skip to main content

futu_backend/trade_query/
crypto_account.rs

1use futu_core::error::{FutuError, Result};
2use futu_domain_trade_account::crypto_account_currency_label_like_cpp;
3
4use super::*;
5
6use crate::crypto_trade::{
7    CryptoAccountContext, lookup_crypto_account_context, lookup_crypto_read_account_context,
8};
9
10mod cache;
11mod command;
12mod status;
13use cache::cache_cmd20631_crypto_account_info;
14use command::request_cmd20631_crypto_account_info;
15use status::ensure_cmd20631_crypto_account_info_status;
16
17const FTAPI_CURRENCY_HKD: i32 = 1;
18
19/// Query crypto account funds through CMD20631.
20///
21/// C++ 10.5.6508: `NNProto_Trd_AccCrypto.cpp:197-215` sends
22/// `asset_pl::AccountInfoReq` with an `odr_sys_cmn::MsgHeader` and
23/// `union_currency`. `APIServer_Trd_GetFunds.cpp` then maps crypto market value
24/// to `Funds.cryptoMv` and keeps ordinary `marketVal` at zero.
25pub async fn query_crypto_account_info(
26    backend: &BackendConn,
27    acc_id: u64,
28    trd_cache: &TrdCache,
29    requested_currency: Option<i32>,
30) -> Result<()> {
31    ensure_crypto_currency_supported(requested_currency)?;
32    let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
33    query_crypto_account_info_with_context(backend, acc_id, trd_cache, requested_currency, &ctx)
34        .await
35}
36
37/// Startup-only Crypto asset read. Unlike public/write paths, C++ startup can
38/// query before trade unlock and serializes a present but empty asset cipher.
39pub async fn query_crypto_account_info_for_startup(
40    backend: &BackendConn,
41    acc_id: u64,
42    trd_cache: &TrdCache,
43    currency: i32,
44) -> Result<()> {
45    ensure_crypto_currency_supported(Some(currency))?;
46    let ctx = lookup_crypto_read_account_context(trd_cache, acc_id)?;
47    query_crypto_account_info_with_context(backend, acc_id, trd_cache, Some(currency), &ctx).await
48}
49
50/// Query crypto positions through CMD20631.
51///
52/// C++ 10.7 `Trd_GetPositionList.C2S.currency` is required for crypto
53/// accounts. Forward it as CMD20631 `union_currency` instead of reusing the
54/// non-crypto first-valid fallback.
55pub async fn query_crypto_position_account_info(
56    backend: &BackendConn,
57    acc_id: u64,
58    trd_cache: &TrdCache,
59    requested_currency: Option<i32>,
60) -> Result<()> {
61    ensure_crypto_currency_supported(requested_currency)?;
62    let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
63    query_crypto_account_info_with_context(backend, acc_id, trd_cache, requested_currency, &ctx)
64        .await
65}
66
67async fn query_crypto_account_info_with_context(
68    backend: &BackendConn,
69    acc_id: u64,
70    trd_cache: &TrdCache,
71    requested_currency: Option<i32>,
72    ctx: &CryptoAccountContext,
73) -> Result<()> {
74    let effective_currency = requested_currency.unwrap_or(FTAPI_CURRENCY_HKD);
75    let Some(currency_label) = crypto_account_currency_label_like_cpp(effective_currency) else {
76        return Err(FutuError::ServerError {
77            ret_type: -1,
78            msg: format!("Crypto account info: unsupported currency id {effective_currency}"),
79        });
80    };
81    let req = asset_pl::AccountInfoReq {
82        msg_header: Some(ctx.build_asset_msg_header("account_info")),
83        union_currency: Some(currency_label.to_string()),
84        without_zero_quantity_pstn: None,
85    };
86    let parsed = request_cmd20631_crypto_account_info(backend, acc_id, req).await?;
87    ensure_cmd20631_crypto_account_info_status(&parsed, acc_id)?;
88
89    cache_cmd20631_crypto_account_info(
90        trd_cache,
91        acc_id,
92        requested_currency,
93        currency_label,
94        &parsed,
95    );
96    Ok(())
97}
98
99fn ensure_crypto_currency_supported(requested_currency: Option<i32>) -> Result<()> {
100    let effective_currency = requested_currency.unwrap_or(FTAPI_CURRENCY_HKD);
101    if crypto_account_currency_label_like_cpp(effective_currency).is_some() {
102        return Ok(());
103    }
104    Err(FutuError::ServerError {
105        ret_type: -1,
106        msg: format!("Crypto account info: unsupported currency id {effective_currency}"),
107    })
108}
109
110#[cfg(test)]
111mod tests;