futu_mcp/handlers/trade/
account_flag.rs1use std::sync::Arc;
2
3use anyhow::{Result, bail};
4use futu_backend::proto_internal::account_flag;
5use futu_net::client::FutuClient;
6use prost::Message as _;
7use serde::Serialize;
8
9use super::parse_trd_env_int;
10
11#[derive(Serialize)]
20struct AccountFlagOut {
21 item_present: bool,
22 flag_value_present: bool,
23 uid: u64,
24 flag_id: i32,
25 flag_value: i32,
27 updated_time: u64,
29}
30
31fn account_flag_out_from_proto(
32 inner_rsp: account_flag::GetFlagRsp,
33 requested_flag_id: u32,
34) -> AccountFlagOut {
35 let item_present = inner_rsp.item.is_some();
36 let item = inner_rsp.item.unwrap_or_default();
37 let flag_value_present = item.flag_value.is_some();
38 AccountFlagOut {
39 item_present,
40 flag_value_present,
41 uid: item.uid.unwrap_or(0),
42 flag_id: item.flag_id.unwrap_or(requested_flag_id as i32),
43 flag_value: item.flag_value.unwrap_or(0),
44 updated_time: item.updated_time.unwrap_or(0),
45 }
46}
47
48pub async fn get_account_flag(
63 client: &Arc<FutuClient>,
64 env: &str,
65 acc_id: u64,
66 flag_id: u32,
67) -> Result<String> {
68 if acc_id == 0 {
69 bail!("acc_id 必填 (call futu_list_accounts to discover)");
70 }
71 if flag_id == 0 {
72 bail!(
73 "flag_id 必填 (常用值: 5=US 期权确认, 22=衍生品风批, 10=基金 KYC, 16=PDT, \
74 23=美股 OTC. 详见 proto 头部完整列表)"
75 );
76 }
77 let trd_env_int: i32 = parse_trd_env_int(env)?;
79
80 let req = account_flag::DaemonGetAccountFlagReq {
81 c2s: account_flag::daemon_get_account_flag_req::C2s {
82 header: account_flag::DaemonGetAccountFlagHeader {
83 acc_id,
84 trd_env: Some(trd_env_int),
85 flag_id,
86 },
87 },
88 };
89
90 let body = req.encode_to_vec();
91 let frame = client
92 .request(futu_core::proto_id::TRD_GET_ACCOUNT_FLAG, body)
93 .await?;
94 let resp =
95 <account_flag::DaemonGetAccountFlagRsp as prost::Message>::decode(frame.body.as_ref())
96 .map_err(|e| anyhow::anyhow!("decode DaemonGetAccountFlagRsp: {e}"))?;
97 if resp.ret_type != 0 {
98 bail!(
99 "GetAccountFlag ret_type={} msg={:?}",
100 resp.ret_type,
101 resp.ret_msg
102 );
103 }
104
105 let inner_rsp = resp
106 .s2c
107 .and_then(|s| s.inner)
108 .ok_or_else(|| anyhow::anyhow!("empty s2c.inner in GetAccountFlagRsp"))?;
109
110 let out = account_flag_out_from_proto(inner_rsp, flag_id);
111
112 Ok(serde_json::to_string_pretty(&out)?)
113}
114
115#[cfg(test)]
116mod tests;