Skip to main content

futu_backend/quote_sub/
sub_bits.rs

1use futu_domain_qot_subscription::{
2    BitInfoPayloadPlan, BitInfoPlan, Lv2OrderEncoding, Lv2OrderItemPlan, SubBitOptions,
3};
4use prost::Message;
5
6use crate::proto_internal::ft_cmd_stock_quote_sub_data;
7
8pub use encode_subscribe_bit_infos_with_options as sub_type_to_bit_infos_with_options;
9pub use futu_domain_qot_subscription::plan_subscribe_bits as sub_type_to_bits;
10pub use futu_domain_qot_subscription::plan_subscribe_bits_with_options as sub_type_to_bits_with_options;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct SubscribeBitInfo {
14    pub bit: u32,
15    pub prob: Option<i64>,
16    pub prob2: Option<String>,
17    pub prob2_v2: Option<Vec<u8>>,
18}
19
20pub fn encode_subscribe_bit_infos_with_options(
21    sub_type: i32,
22    opts: SubBitOptions,
23) -> Vec<SubscribeBitInfo> {
24    futu_domain_qot_subscription::plan_subscribe_bit_infos_with_options(sub_type, opts)
25        .into_iter()
26        .map(encode_subscribe_bit_plan)
27        .collect()
28}
29
30fn encode_subscribe_bit_plan(plan: BitInfoPlan) -> SubscribeBitInfo {
31    let (prob2, prob2_v2) = match plan.payload {
32        BitInfoPayloadPlan::None => (None, None),
33        BitInfoPayloadPlan::Lv2Order {
34            encoding: Lv2OrderEncoding::LegacyProb2,
35            items,
36        } => (Some(encode_legacy_items(&items)), None),
37        BitInfoPayloadPlan::Lv2Order {
38            encoding: Lv2OrderEncoding::BytesV2,
39            items,
40        } => (None, Some(encode_v2_items(&items))),
41    };
42    SubscribeBitInfo {
43        bit: plan.bit,
44        prob: plan.prob,
45        prob2,
46        prob2_v2,
47    }
48}
49
50fn encode_v2_items(items: &[Lv2OrderItemPlan]) -> Vec<u8> {
51    ft_cmd_stock_quote_sub_data::Uslv2OrderSubProb {
52        sub_items: items
53            .iter()
54            .map(|item| ft_cmd_stock_quote_sub_data::Uslv2OrderSubItem {
55                us_lv2_order_type: Some(item.lv2_type),
56                us_lv2_order_level: Some(item.level),
57            })
58            .collect(),
59    }
60    .encode_to_vec()
61}
62
63fn encode_legacy_items(items: &[Lv2OrderItemPlan]) -> String {
64    // Ref: C++ `MktQotSubInstance.cpp:151,164` selects legacy `prob2` for the
65    // ARCA/NASDAQ TV compatibility groups at level 60. Their protobuf field
66    // tags and values are ASCII bytes, so mapping through `char::from` is
67    // byte-identical without a production panic boundary. If a future group
68    // needs values above ASCII, it must use prob2_v2 like the existing
69    // overnight/dynamic profiles.
70    encode_v2_items(items).into_iter().map(char::from).collect()
71}