futu_mcp/handlers/core/
sub_query.rs1use std::sync::Arc;
5
6use anyhow::{Result, anyhow, bail};
7use futu_net::client::FutuClient;
8use prost::Message;
9use serde::Serialize;
10
11#[derive(Serialize)]
12struct SubInfoItemOut {
13 sub_type: i32,
14 symbols: Vec<String>,
15}
16
17#[derive(Serialize)]
18struct SubInfoOut {
19 total_used_quota: i32,
20 remain_quota: i32,
21 sub_list: Vec<SubInfoItemOut>,
22}
23
24#[derive(Serialize)]
25struct UsedQuotaOut {
26 used_sub_quota: i32,
27 used_k_line_quota: i32,
28}
29
30pub async fn query_subscription(client: &Arc<FutuClient>, is_req_all_conn: bool) -> Result<String> {
32 let req = futu_proto::qot_get_sub_info::Request {
33 c2s: futu_proto::qot_get_sub_info::C2s {
34 is_req_all_conn: Some(is_req_all_conn),
35 header: None,
36 },
37 };
38 let body = req.encode_to_vec();
39 let frame = client
40 .request(futu_core::proto_id::QOT_GET_SUB_INFO, body)
41 .await?;
42 let resp = futu_proto::qot_get_sub_info::Response::decode(frame.body.as_ref())
43 .map_err(|e| anyhow!("decode sub_info: {e}"))?;
44 if resp.ret_type != 0 {
45 bail!("sub_info ret_type={} msg={:?}", resp.ret_type, resp.ret_msg);
46 }
47 let s = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
48 let mut sub_list = Vec::new();
51 for conn in &s.conn_sub_info_list {
52 for si in &conn.sub_info_list {
53 sub_list.push(SubInfoItemOut {
54 sub_type: si.sub_type,
55 symbols: si
56 .security_list
57 .iter()
58 .map(|sec| format!("{}.{}", sec.market, sec.code))
59 .collect(),
60 });
61 }
62 }
63 let out = SubInfoOut {
64 total_used_quota: s.total_used_quota,
65 remain_quota: s.remain_quota,
66 sub_list,
67 };
68 Ok(serde_json::to_string_pretty(&out)?)
69}
70
71pub async fn get_used_quota(client: &Arc<FutuClient>) -> Result<String> {
73 let req = futu_proto::used_quota::Request {
74 c2s: futu_proto::used_quota::C2s {},
75 };
76 let body = req.encode_to_vec();
77 let frame = client
78 .request(futu_core::proto_id::GET_USED_QUOTA, body)
79 .await?;
80 let resp = futu_proto::used_quota::Response::decode(frame.body.as_ref())
81 .map_err(|e| anyhow!("decode used_quota: {e}"))?;
82 if resp.ret_type != 0 {
83 bail!(
84 "used_quota ret_type={} msg={:?}",
85 resp.ret_type,
86 resp.ret_msg
87 );
88 }
89 let s = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
90 let out = UsedQuotaOut {
91 used_sub_quota: s
92 .used_sub_quota
93 .ok_or_else(|| anyhow!("missing used_sub_quota"))?,
94 used_k_line_quota: s
95 .used_k_line_quota
96 .ok_or_else(|| anyhow!("missing used_k_line_quota"))?,
97 };
98 Ok(serde_json::to_string_pretty(&out)?)
99}