1use prost::Message;
2
3use crate::proto_internal::ft_cmd_stock_quote_sub_data;
4
5use super::{sbit, sub_type};
6
7pub fn sub_type_to_bits(sub_type: i32) -> Vec<(u32, i64)> {
10 match sub_type {
11 sub_type::BASIC => vec![
12 (sbit::PRICE, 0),
13 (sbit::STOCK_STATE, 0),
14 (sbit::STOCK_TYPE_SPECIFIC, 0),
15 (sbit::DEAL_STATISTICS, 0),
16 ],
17 sub_type::ORDER_BOOK => vec![(sbit::ORDER_BOOK, 0)],
18 sub_type::TICKER => vec![(sbit::TICK, 1)], sub_type::RT => vec![(sbit::STOCK_STATE, 0), (sbit::TIME_SHARING, 0)],
20 sub_type::BROKER => vec![(sbit::HK_BROKER_QUEUE, 0)],
21 sub_type::KL_1MIN => vec![(sbit::KLINE_1MIN, 1)], sub_type::KL_3MIN => vec![(sbit::KLINE_3MIN, 1)],
23 sub_type::KL_5MIN => vec![(sbit::KLINE_5MIN, 1)],
24 sub_type::KL_10MIN => vec![(sbit::KLINE_10MIN, 1)],
25 sub_type::KL_15MIN => vec![(sbit::KLINE_15MIN, 1)],
26 sub_type::KL_30MIN => vec![(sbit::KLINE_30MIN, 1)],
27 sub_type::KL_60MIN => vec![(sbit::KLINE_60MIN, 1)],
28 sub_type::KL_120MIN => vec![(sbit::KLINE_120MIN, 1)],
29 sub_type::KL_180MIN => vec![(sbit::KLINE_180MIN, 1)],
30 sub_type::KL_240MIN => vec![(sbit::KLINE_240MIN, 1)],
31 sub_type::KL_DAY => vec![(sbit::KLINE_DAY, 1)],
32 sub_type::KL_WEEK => vec![(sbit::KLINE_WEEK, 1)],
33 sub_type::KL_MONTH => vec![(sbit::KLINE_MONTH, 1)],
34 sub_type::KL_QUARTER => vec![(sbit::KLINE_QUARTER, 1)],
35 sub_type::KL_YEAR => vec![(sbit::KLINE_YEAR, 1)],
36 _ => vec![],
37 }
38}
39
40#[derive(Debug, Clone, Default)]
45pub struct SubBitOptions {
46 pub session: i32,
49 pub orderbook_detail: bool,
51 pub orderbook_full_depth: bool,
55 pub broker_detail: bool,
57 pub us_pre_after_detail: bool,
60 pub extended_time: bool,
63 pub merged_lv2_order_types: u32,
66 pub merged_lv2_order_types_v2: u32,
69 pub merged_lv2_order_types_v2_level40: u32,
72 pub merged_lv2_order_subs: Vec<futu_cache::qot_right::Lv2OrderSubDescriptor>,
77 pub backend_market: i32,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct SubscribeBitInfo {
88 pub bit: u32,
89 pub prob: i64,
90 pub prob2: Option<String>,
91 pub prob2_v2: Option<Vec<u8>>,
92}
93
94const ORDER_BOOK_40_PROB: i64 = 0;
96const ORDER_BOOK_ALL_PROB: i64 = 1;
97const ORDER_BOOK_ALL_WITH_DETAIL_PROB: i64 = 2;
98const ORDER_BOOK_SIMPLE_LV2_PROB: i64 = 8 | 16;
99const ORDER_BOOK_SGX_STOCK_PROB: i64 = 1;
100const ORDER_BOOK_SGX_STOCK_ODD_LOT_PROB: i64 = 2;
101const ORDER_BOOK_MYX_STOCK_PROB: i64 = 1;
102const ORDER_BOOK_MYX_STOCK_ODD_LOT_PROB: i64 = 16;
103
104fn sg_my_order_book_prob(backend_market: i32) -> Option<i64> {
105 match backend_market {
106 15 => Some(ORDER_BOOK_SGX_STOCK_PROB | ORDER_BOOK_SGX_STOCK_ODD_LOT_PROB),
111 27 => Some(ORDER_BOOK_MYX_STOCK_PROB | ORDER_BOOK_MYX_STOCK_ODD_LOT_PROB),
112 _ => None,
113 }
114}
115
116fn encode_us_lv2_order_sub_prob(lv2_type: u32, level: u32) -> Vec<u8> {
117 ft_cmd_stock_quote_sub_data::Uslv2OrderSubProb {
118 sub_items: vec![ft_cmd_stock_quote_sub_data::Uslv2OrderSubItem {
119 us_lv2_order_type: Some(lv2_type),
120 us_lv2_order_level: Some(level),
121 }],
122 }
123 .encode_to_vec()
124}
125
126fn for_each_type_mask_bit(mut mask: u32, mut f: impl FnMut(u32)) {
127 while mask != 0 {
128 let bit = mask & (!mask + 1);
129 f(bit);
130 mask &= !bit;
131 }
132}
133
134fn for_each_us_lv2_prob2_type(mask: u32, mut f: impl FnMut(u32)) {
135 for known in [
139 futu_cache::qot_right::US_LV2_ORDER_NASDAQ_TV,
140 futu_cache::qot_right::US_LV2_ORDER_ARCA,
141 ] {
142 if mask & known != 0 {
143 f(known);
144 }
145 }
146 let remaining = mask
147 & !(futu_cache::qot_right::US_LV2_ORDER_NASDAQ_TV
148 | futu_cache::qot_right::US_LV2_ORDER_ARCA);
149 for_each_type_mask_bit(remaining, f);
150}
151
152fn push_lv2_order_sub_info(
153 infos: &mut Vec<SubscribeBitInfo>,
154 sub: futu_cache::qot_right::Lv2OrderSubDescriptor,
155) {
156 let bytes = encode_us_lv2_order_sub_prob(sub.lv2_type, sub.level);
157 if sub.prob2_v2 {
158 infos.push(SubscribeBitInfo {
159 bit: sbit::MEGER_LV2_ORDER,
160 prob: 0,
161 prob2: None,
162 prob2_v2: Some(bytes),
163 });
164 } else {
165 let (prob2, prob2_v2) = encode_legacy_or_bytes_prob2(bytes);
166 infos.push(SubscribeBitInfo {
167 bit: sbit::MEGER_LV2_ORDER,
168 prob: 0,
169 prob2,
170 prob2_v2,
171 });
172 }
173}
174
175pub fn sub_type_to_bits_with_options(sub_type: i32, opts: SubBitOptions) -> Vec<(u32, i64)> {
180 sub_type_to_bit_infos_with_options(sub_type, opts)
181 .into_iter()
182 .map(|info| (info.bit, info.prob))
183 .collect()
184}
185
186pub fn sub_type_to_bit_infos_with_options(
187 sub_type: i32,
188 opts: SubBitOptions,
189) -> Vec<SubscribeBitInfo> {
190 let ticker_session_prob = match opts.session {
198 2 => 1 | 2 | 4,
199 3 => 1 | 2 | 4 | 8,
200 _ => 1,
201 };
202 let kline_session_prob = match opts.session {
206 2 => 2,
207 3 => 3,
208 _ => 1,
209 };
210 match sub_type {
211 sub_type::BASIC => {
212 let mut infos = vec![
213 SubscribeBitInfo::new(sbit::PRICE, 0),
214 SubscribeBitInfo::new(sbit::STOCK_STATE, 0),
215 SubscribeBitInfo::new(sbit::STOCK_TYPE_SPECIFIC, 0),
216 SubscribeBitInfo::new(sbit::DEAL_STATISTICS, 0),
217 ];
218 if opts.us_pre_after_detail {
219 infos.push(SubscribeBitInfo::new(sbit::US_PREMARKET_AFTERHOURS, 0));
222 }
223 infos
224 }
225 sub_type::ORDER_BOOK => {
226 let sg_my_prob = sg_my_order_book_prob(opts.backend_market);
227 let bit = if sg_my_prob.is_some() {
228 sbit::ORDER_BOOK
229 } else if opts.orderbook_detail {
230 sbit::ORDER_BOOK_DETAIL
231 } else {
232 sbit::ORDER_BOOK
233 };
234 let mut infos = Vec::new();
235 for sub in &opts.merged_lv2_order_subs {
236 push_lv2_order_sub_info(&mut infos, *sub);
237 }
238 for_each_type_mask_bit(opts.merged_lv2_order_types_v2_level40, |lv2_type| {
239 infos.push(SubscribeBitInfo {
240 bit: sbit::MEGER_LV2_ORDER,
241 prob: 0,
242 prob2: None,
243 prob2_v2: Some(encode_us_lv2_order_sub_prob(lv2_type, 40)),
244 });
245 });
246 for_each_type_mask_bit(opts.merged_lv2_order_types_v2, |lv2_type| {
247 infos.push(SubscribeBitInfo {
248 bit: sbit::MEGER_LV2_ORDER,
249 prob: 0,
250 prob2: None,
251 prob2_v2: Some(encode_us_lv2_order_sub_prob(lv2_type, 60)),
252 });
253 });
254 for_each_us_lv2_prob2_type(opts.merged_lv2_order_types, |lv2_type| {
255 let bytes = encode_us_lv2_order_sub_prob(lv2_type, 60);
256 let (prob2, prob2_v2) = encode_legacy_or_bytes_prob2(bytes);
257 infos.push(SubscribeBitInfo {
258 bit: sbit::MEGER_LV2_ORDER,
259 prob: 0,
260 prob2,
261 prob2_v2,
262 });
263 });
264 let prob = if let Some(prob) = sg_my_prob {
265 prob
266 } else if opts.orderbook_full_depth {
267 if opts.orderbook_detail {
268 ORDER_BOOK_ALL_WITH_DETAIL_PROB
269 } else {
270 ORDER_BOOK_ALL_PROB
271 }
272 } else if opts.merged_lv2_order_types != 0
273 || opts.merged_lv2_order_types_v2 != 0
274 || opts.merged_lv2_order_types_v2_level40 != 0
275 || !opts.merged_lv2_order_subs.is_empty()
276 {
277 ORDER_BOOK_SIMPLE_LV2_PROB
278 } else {
279 ORDER_BOOK_40_PROB
280 };
281 infos.push(SubscribeBitInfo::new(bit, prob));
282 infos
283 }
284 sub_type::ORDER_BOOK_ODD => {
285 let prob = sg_my_order_book_prob(opts.backend_market).unwrap_or(0);
286 vec![SubscribeBitInfo::new(sbit::ORDER_BOOK, prob)]
287 }
288 sub_type::TICKER => vec![SubscribeBitInfo::new(sbit::TICK, ticker_session_prob)],
289 sub_type::RT => vec![
290 SubscribeBitInfo::new(sbit::STOCK_STATE, 0),
291 SubscribeBitInfo::new(sbit::TIME_SHARING, 0),
292 ],
293 sub_type::BROKER => {
294 let bit = if opts.broker_detail {
295 sbit::HK_BROKER_DETAIL
296 } else {
297 sbit::HK_BROKER_QUEUE
298 };
299 vec![SubscribeBitInfo::new(bit, 0)]
300 }
301 sub_type::KL_1MIN => vec![SubscribeBitInfo::new(sbit::KLINE_1MIN, kline_session_prob)],
302 sub_type::KL_3MIN => vec![SubscribeBitInfo::new(sbit::KLINE_3MIN, kline_session_prob)],
303 sub_type::KL_5MIN => vec![SubscribeBitInfo::new(sbit::KLINE_5MIN, kline_session_prob)],
304 sub_type::KL_10MIN => vec![SubscribeBitInfo::new(sbit::KLINE_10MIN, kline_session_prob)],
305 sub_type::KL_15MIN => vec![SubscribeBitInfo::new(sbit::KLINE_15MIN, kline_session_prob)],
306 sub_type::KL_30MIN => vec![SubscribeBitInfo::new(sbit::KLINE_30MIN, kline_session_prob)],
307 sub_type::KL_60MIN => vec![SubscribeBitInfo::new(sbit::KLINE_60MIN, kline_session_prob)],
308 sub_type::KL_120MIN => {
309 vec![SubscribeBitInfo::new(
310 sbit::KLINE_120MIN,
311 kline_session_prob,
312 )]
313 }
314 sub_type::KL_180MIN => {
315 vec![SubscribeBitInfo::new(
316 sbit::KLINE_180MIN,
317 kline_session_prob,
318 )]
319 }
320 sub_type::KL_240MIN => {
321 vec![SubscribeBitInfo::new(
322 sbit::KLINE_240MIN,
323 kline_session_prob,
324 )]
325 }
326 sub_type::KL_DAY => vec![SubscribeBitInfo::new(sbit::KLINE_DAY, kline_session_prob)],
327 sub_type::KL_WEEK => vec![SubscribeBitInfo::new(sbit::KLINE_WEEK, kline_session_prob)],
328 sub_type::KL_MONTH => vec![SubscribeBitInfo::new(sbit::KLINE_MONTH, kline_session_prob)],
329 sub_type::KL_QUARTER => vec![SubscribeBitInfo::new(
330 sbit::KLINE_QUARTER,
331 kline_session_prob,
332 )],
333 sub_type::KL_YEAR => vec![SubscribeBitInfo::new(sbit::KLINE_YEAR, kline_session_prob)],
334 _ => vec![],
335 }
336}
337
338impl SubscribeBitInfo {
339 pub(crate) fn new(bit: u32, prob: i64) -> Self {
340 Self {
341 bit,
342 prob,
343 prob2: None,
344 prob2_v2: None,
345 }
346 }
347}
348
349fn encode_legacy_or_bytes_prob2(bytes: Vec<u8>) -> (Option<String>, Option<Vec<u8>>) {
350 match String::from_utf8(bytes) {
351 Ok(prob2) => (Some(prob2), None),
352 Err(err) => (None, Some(err.into_bytes())),
353 }
354}