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(FutuError::ServerError {
78 ret_type: resp.ret_type,
79 msg: resp.ret_msg.unwrap_or_default(),
80 });
81 }
82
83 let s2c = resp
84 .s2c
85 .ok_or(FutuError::Codec("missing s2c in GetStaticInfo".into()))?;
86
87 Ok(s2c
88 .static_info_list
89 .iter()
90 .map(SecurityStaticInfo::from_proto)
91 .collect())
92}
93
94pub async fn get_static_info_by_market(
96 client: &FutuClient,
97 market: QotMarket,
98 sec_type: i32,
99) -> Result<Vec<SecurityStaticInfo>> {
100 let req = futu_proto::qot_get_static_info::Request {
101 c2s: futu_proto::qot_get_static_info::C2s {
102 market: Some(market as i32),
103 sec_type: Some(sec_type),
104 security_list: vec![],
105 header: None,
106 },
107 };
108
109 let body = prost::Message::encode_to_vec(&req);
110 let resp_frame = client.request(proto_id::QOT_GET_STATIC_INFO, body).await?;
111
112 let resp: futu_proto::qot_get_static_info::Response =
113 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
114
115 if resp.ret_type != 0 {
116 return Err(FutuError::ServerError {
117 ret_type: resp.ret_type,
118 msg: resp.ret_msg.unwrap_or_default(),
119 });
120 }
121
122 let s2c = resp
123 .s2c
124 .ok_or(FutuError::Codec("missing s2c in GetStaticInfo".into()))?;
125
126 Ok(s2c
127 .static_info_list
128 .iter()
129 .map(SecurityStaticInfo::from_proto)
130 .collect())
131}
132
133#[cfg(test)]
134mod tests;