1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::types::{QotMarket, Security};
6
7#[derive(Debug, Clone)]
9pub struct SecurityStaticInfo {
10 pub security: Security,
11 pub id: i64,
12 pub lot_size: i32,
13 pub sec_type: i32,
14 pub name: String,
15 pub list_time: String,
16 pub delisting: bool,
17 pub exch_type: i32,
24}
25
26impl SecurityStaticInfo {
27 pub fn from_proto(info: &futu_proto::qot_common::SecurityStaticInfo) -> Self {
28 let basic = &info.basic;
29 Self {
30 security: Security::from_proto(&basic.security),
31 id: basic.id,
32 lot_size: basic.lot_size,
33 sec_type: basic.sec_type,
34 name: basic.name.clone(),
35 list_time: basic.list_time.clone(),
36 delisting: basic.delisting.unwrap_or(false),
37 exch_type: basic.exch_type.unwrap_or(0),
39 }
40 }
41
42 pub fn exchange_code(&self) -> Option<&'static str> {
50 futu_core::exch_type::exch_type_to_string(self.exch_type)
51 }
52}
53
54pub async fn get_static_info(
58 client: &FutuClient,
59 securities: &[Security],
60) -> Result<Vec<SecurityStaticInfo>> {
61 let req = futu_proto::qot_get_static_info::Request {
62 c2s: futu_proto::qot_get_static_info::C2s {
63 market: None,
64 sec_type: None,
65 security_list: securities.iter().map(|s| s.to_proto()).collect(),
66 header: None,
67 },
68 };
69
70 let body = prost::Message::encode_to_vec(&req);
71 let resp_frame = client.request(proto_id::QOT_GET_STATIC_INFO, body).await?;
72
73 let resp: futu_proto::qot_get_static_info::Response =
74 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
75
76 if resp.ret_type != 0 {
77 return Err(crate::server_err(resp.ret_type, resp.ret_msg));
78 }
79
80 let s2c = resp
81 .s2c
82 .ok_or(FutuError::Codec("missing s2c in GetStaticInfo".into()))?;
83
84 Ok(s2c
85 .static_info_list
86 .iter()
87 .map(SecurityStaticInfo::from_proto)
88 .collect())
89}
90
91pub async fn get_static_info_by_market(
93 client: &FutuClient,
94 market: QotMarket,
95 sec_type: i32,
96) -> Result<Vec<SecurityStaticInfo>> {
97 let req = futu_proto::qot_get_static_info::Request {
98 c2s: futu_proto::qot_get_static_info::C2s {
99 market: Some(market as i32),
100 sec_type: Some(sec_type),
101 security_list: vec![],
102 header: None,
103 },
104 };
105
106 let body = prost::Message::encode_to_vec(&req);
107 let resp_frame = client.request(proto_id::QOT_GET_STATIC_INFO, body).await?;
108
109 let resp: futu_proto::qot_get_static_info::Response =
110 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
111
112 if resp.ret_type != 0 {
113 return Err(crate::server_err(resp.ret_type, resp.ret_msg));
114 }
115
116 let s2c = resp
117 .s2c
118 .ok_or(FutuError::Codec("missing s2c in GetStaticInfo".into()))?;
119
120 Ok(s2c
121 .static_info_list
122 .iter()
123 .map(SecurityStaticInfo::from_proto)
124 .collect())
125}
126
127#[cfg(test)]
128mod tests;