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 option_used_quota: Option<i32>,
22 option_remain_quota: Option<i32>,
23 sub_list: Vec<SubInfoItemOut>,
24}
25
26#[derive(Serialize)]
27struct UsedQuotaOut {
28 used_sub_quota: i32,
29 used_k_line_quota: i32,
30}
31
32pub async fn query_subscription(client: &Arc<FutuClient>, is_req_all_conn: bool) -> Result<String> {
34 let req = futu_proto::qot_get_sub_info::Request {
35 c2s: futu_proto::qot_get_sub_info::C2s {
36 is_req_all_conn: Some(is_req_all_conn),
37 header: None,
38 },
39 };
40 let body = req.encode_to_vec();
41 let frame = client
42 .request(futu_core::proto_id::QOT_GET_SUB_INFO, body)
43 .await?;
44 let resp = futu_proto::qot_get_sub_info::Response::decode(frame.body.as_ref())
45 .map_err(|e| anyhow!("decode sub_info: {e}"))?;
46 if resp.ret_type != 0 {
47 bail!("sub_info ret_type={} msg={:?}", resp.ret_type, resp.ret_msg);
48 }
49 let s = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
50 let mut sub_list = Vec::new();
53 for conn in &s.conn_sub_info_list {
54 for si in &conn.sub_info_list {
55 sub_list.push(SubInfoItemOut {
56 sub_type: si.sub_type,
57 symbols: si
58 .security_list
59 .iter()
60 .map(|sec| format!("{}.{}", sec.market, sec.code))
61 .collect(),
62 });
63 }
64 }
65 let out = SubInfoOut {
66 total_used_quota: s.total_used_quota,
67 remain_quota: s.remain_quota,
68 option_used_quota: s.option_used_quota,
69 option_remain_quota: s.option_remain_quota,
70 sub_list,
71 };
72 Ok(serde_json::to_string_pretty(&out)?)
73}
74
75pub async fn get_used_quota(client: &Arc<FutuClient>) -> Result<String> {
77 let req = futu_proto::used_quota::Request {
78 c2s: futu_proto::used_quota::C2s {},
79 };
80 let body = req.encode_to_vec();
81 let frame = client
82 .request(futu_core::proto_id::GET_USED_QUOTA, body)
83 .await?;
84 let resp = futu_proto::used_quota::Response::decode(frame.body.as_ref())
85 .map_err(|e| anyhow!("decode used_quota: {e}"))?;
86 if resp.ret_type != 0 {
87 bail!(
88 "used_quota ret_type={} msg={:?}",
89 resp.ret_type,
90 resp.ret_msg
91 );
92 }
93 let s = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
94 let out = UsedQuotaOut {
95 used_sub_quota: s
96 .used_sub_quota
97 .ok_or_else(|| anyhow!("missing used_sub_quota"))?,
98 used_k_line_quota: s
99 .used_k_line_quota
100 .ok_or_else(|| anyhow!("missing used_k_line_quota"))?,
101 };
102 Ok(serde_json::to_string_pretty(&out)?)
103}