1use serde::{Deserialize, Serialize};
7
8pub const SYS_QUERY_GET_QUOTE_RIGHTS_PROFILE: &str = "sys.get_quote_rights_profile";
9pub const TEST_CMD_GET_QUOTE_RIGHTS_PROFILE: &str = "get_quote_rights_profile";
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct QuoteRightsProfile {
13 pub nick_name: Option<String>,
14 pub user_id: Option<i64>,
15 pub user_attribution: Option<i32>,
16 pub user_attribution_region: Option<String>,
17 pub ret_msg: Option<String>,
18 pub quota: QuoteRightsQuota,
19 pub items: Vec<QuoteRightItem>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct QuoteRightsQuota {
24 pub subscribe_total: Option<i32>,
25 pub history_kl_total: Option<i32>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct QuoteRightItem {
30 pub market: String,
31 pub category: String,
32 pub raw: Option<i32>,
33 pub label: String,
34}
35
36#[derive(Debug, Clone, Default, PartialEq, Eq)]
37pub struct QuoteRightsSnapshot {
38 pub nick_name: Option<String>,
39 pub user_id: Option<i64>,
40 pub user_attribution: Option<i32>,
41 pub ret_msg: Option<String>,
42 pub sub_quota: Option<i32>,
43 pub history_kl_quota: Option<i32>,
44 pub hk_qot_right: Option<i32>,
45 pub hk_option_qot_right: Option<i32>,
46 pub hk_future_qot_right: Option<i32>,
47 pub us_qot_right: Option<i32>,
48 pub us_index_qot_right: Option<i32>,
49 pub us_option_qot_right: Option<i32>,
50 pub us_cme_future_qot_right: Option<i32>,
51 pub us_cbot_future_qot_right: Option<i32>,
52 pub us_nymex_future_qot_right: Option<i32>,
53 pub us_comex_future_qot_right: Option<i32>,
54 pub us_cboe_future_qot_right: Option<i32>,
55 pub us_otc_qot_right: Option<i32>,
56 pub sh_qot_right: Option<i32>,
57 pub sz_qot_right: Option<i32>,
58 pub sg_future_qot_right: Option<i32>,
59 pub sg_stock_qot_right: Option<i32>,
60 pub my_stock_qot_right: Option<i32>,
61 pub jp_future_qot_right: Option<i32>,
62 pub jp_stock_qot_right: Option<i32>,
63 pub cc_qot_right: Option<i32>,
64}
65
66pub fn profile_from_get_user_info(
67 resp: &futu_proto::get_user_info::Response,
68) -> Result<QuoteRightsProfile, String> {
69 if resp.ret_type != 0 {
70 return Err(format!(
71 "get_user_info ret_type={} msg={}",
72 resp.ret_type,
73 resp.ret_msg.as_deref().unwrap_or("")
74 ));
75 }
76 let s = resp.s2c.as_ref().ok_or_else(|| {
77 format!(
78 "get_user_info ret_type=0 but missing s2c msg={}",
79 resp.ret_msg.as_deref().unwrap_or("")
80 )
81 })?;
82
83 Ok(profile_from_snapshot(QuoteRightsSnapshot {
84 nick_name: s.nick_name.clone(),
85 user_id: s.user_id,
86 user_attribution: s.user_attribution,
87 ret_msg: resp.ret_msg.clone(),
88 sub_quota: s.sub_quota,
89 history_kl_quota: s.history_kl_quota,
90 hk_qot_right: s.hk_qot_right,
91 hk_option_qot_right: s.hk_option_qot_right,
92 hk_future_qot_right: s.hk_future_qot_right,
93 us_qot_right: s.us_qot_right,
94 us_index_qot_right: s.us_index_qot_right,
95 us_option_qot_right: s.us_option_qot_right,
96 us_cme_future_qot_right: s.us_cme_future_qot_right,
97 us_cbot_future_qot_right: s.us_cbot_future_qot_right,
98 us_nymex_future_qot_right: s.us_nymex_future_qot_right,
99 us_comex_future_qot_right: s.us_comex_future_qot_right,
100 us_cboe_future_qot_right: s.us_cboe_future_qot_right,
101 us_otc_qot_right: s.us_otc_qot_right,
102 sh_qot_right: s.sh_qot_right,
103 sz_qot_right: s.sz_qot_right,
104 sg_future_qot_right: s.sg_future_qot_right,
105 sg_stock_qot_right: s.sg_stock_qot_right,
106 my_stock_qot_right: s.my_stock_qot_right,
107 jp_future_qot_right: s.jp_future_qot_right,
108 jp_stock_qot_right: s.jp_stock_qot_right,
109 cc_qot_right: s.cc_qot_right,
110 }))
111}
112
113pub fn profile_from_snapshot(s: QuoteRightsSnapshot) -> QuoteRightsProfile {
114 let mut items = Vec::with_capacity(21);
115 push_item(
116 &mut items,
117 "香港市场",
118 "股票",
119 s.hk_qot_right,
120 label_standard,
121 );
122 push_item(
123 &mut items,
124 "香港市场",
125 "期权",
126 s.hk_option_qot_right,
127 label_standard,
128 );
129 push_item(
130 &mut items,
131 "香港市场",
132 "期货",
133 s.hk_future_qot_right,
134 label_standard,
135 );
136 push_item(
137 &mut items,
138 "美国市场",
139 "股票",
140 s.us_qot_right,
141 label_us_stock,
142 );
143 push_item(
144 &mut items,
145 "美国市场",
146 "指数",
147 s.us_index_qot_right,
148 label_standard,
149 );
150 push_item(
151 &mut items,
152 "美国市场",
153 "期权",
154 s.us_option_qot_right,
155 label_standard,
156 );
157 push_item(
158 &mut items,
159 "美国市场",
160 "CME",
161 s.us_cme_future_qot_right,
162 label_standard,
163 );
164 push_item(
165 &mut items,
166 "美国市场",
167 "CBOT",
168 s.us_cbot_future_qot_right,
169 label_standard,
170 );
171 push_item(
172 &mut items,
173 "美国市场",
174 "NYMEX",
175 s.us_nymex_future_qot_right,
176 label_standard,
177 );
178 push_item(
179 &mut items,
180 "美国市场",
181 "COMEX",
182 s.us_comex_future_qot_right,
183 label_standard,
184 );
185 push_item(
186 &mut items,
187 "美国市场",
188 "CBOE",
189 s.us_cboe_future_qot_right,
190 label_standard,
191 );
192 push_item(
193 &mut items,
194 "美国市场",
195 "OTC",
196 s.us_otc_qot_right,
197 label_standard,
198 );
199 push_item(
200 &mut items,
201 "A股市场",
202 "上证",
203 s.sh_qot_right,
204 label_standard,
205 );
206 push_item(
207 &mut items,
208 "A股市场",
209 "深证",
210 s.sz_qot_right,
211 label_standard,
212 );
213 push_item(
214 &mut items,
215 "新加坡市场",
216 "股票",
217 s.sg_stock_qot_right,
218 label_standard,
219 );
220 push_item(
221 &mut items,
222 "新加坡市场",
223 "期货",
224 s.sg_future_qot_right,
225 label_standard,
226 );
227 push_item(
228 &mut items,
229 "马来西亚市场",
230 "股票",
231 s.my_stock_qot_right,
232 label_standard,
233 );
234 push_item(
235 &mut items,
236 "日本市场",
237 "股票",
238 s.jp_stock_qot_right,
239 label_standard,
240 );
241 push_item(
242 &mut items,
243 "日本市场",
244 "期货",
245 s.jp_future_qot_right,
246 label_standard,
247 );
248 push_item(
249 &mut items,
250 "加密货币市场",
251 "加密货币",
252 s.cc_qot_right,
253 label_standard,
254 );
255
256 QuoteRightsProfile {
257 nick_name: s.nick_name,
258 user_id: s.user_id,
259 user_attribution: s.user_attribution,
260 user_attribution_region: user_attribution_region(s.user_attribution)
261 .map(ToString::to_string),
262 ret_msg: s.ret_msg,
263 quota: QuoteRightsQuota {
264 subscribe_total: s.sub_quota,
265 history_kl_total: s.history_kl_quota,
266 },
267 items,
268 }
269}
270
271fn push_item(
272 items: &mut Vec<QuoteRightItem>,
273 market: &str,
274 category: &str,
275 raw: Option<i32>,
276 label_fn: fn(Option<i32>) -> String,
277) {
278 items.push(QuoteRightItem {
279 market: market.to_string(),
280 category: category.to_string(),
281 raw,
282 label: label_fn(raw),
283 });
284}
285
286fn label_standard(raw: Option<i32>) -> String {
287 match raw {
288 None | Some(0) => "未知".to_string(),
289 Some(1) => "BMP".to_string(),
290 Some(2) => "LV1".to_string(),
291 Some(3) => "LV2".to_string(),
292 Some(4) => "SF".to_string(),
293 Some(5) => "无权限".to_string(),
294 Some(6) => "LV3".to_string(),
295 Some(v) => format!("未知({v})"),
296 }
297}
298
299fn label_us_stock(raw: Option<i32>) -> String {
300 match raw {
301 Some(3) => "LV3".to_string(),
302 other => label_standard(other),
303 }
304}
305
306fn user_attribution_region(raw: Option<i32>) -> Option<&'static str> {
307 match raw {
308 Some(1) => Some("CN"),
309 Some(2) => Some("US"),
310 Some(3) => Some("SG"),
311 Some(4) => Some("AU"),
312 Some(5) => Some("JP"),
313 Some(6) => Some("HK"),
314 _ => None,
315 }
316}