Skip to main content

futu_qot/
sub.rs

1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::subscription_plan::{SubscribeOptionsPlan, SubscribePlanError};
6use crate::types::{Security, SubType};
7
8/// QOT subscribe options exposed by FTAPI `Qot_Sub.C2S`.
9#[derive(Debug, Clone, Copy, Default)]
10pub struct SubscribeOptions {
11    pub is_reg_push: bool,
12    pub is_first_push: bool,
13    pub extended_time: Option<bool>,
14    pub session: Option<i32>,
15    pub orderbook_detail: Option<bool>,
16}
17
18/// 订阅行情
19///
20/// 订阅指定股票的指定类型行情数据。
21/// 订阅后可通过推送接收实时数据更新。
22pub async fn subscribe(
23    client: &FutuClient,
24    securities: &[Security],
25    sub_types: &[SubType],
26    is_reg_push: bool,
27    is_first_push: bool,
28) -> Result<()> {
29    subscribe_with_options(
30        client,
31        securities,
32        sub_types,
33        SubscribeOptions {
34            is_reg_push,
35            is_first_push,
36            ..SubscribeOptions::default()
37        },
38    )
39    .await
40}
41
42pub async fn subscribe_with_options(
43    client: &FutuClient,
44    securities: &[Security],
45    sub_types: &[SubType],
46    options: SubscribeOptions,
47) -> Result<()> {
48    validate_subscribe_options(options)?;
49    let req = futu_proto::qot_sub::Request {
50        c2s: futu_proto::qot_sub::C2s {
51            security_list: securities.iter().map(|s| s.to_proto()).collect(),
52            sub_type_list: sub_types.iter().map(|t| *t as i32).collect(),
53            is_sub_or_un_sub: true,
54            is_reg_or_un_reg_push: Some(options.is_reg_push),
55            reg_push_rehab_type_list: vec![],
56            is_first_push: Some(options.is_first_push),
57            is_unsub_all: None,
58            is_sub_order_book_detail: options.orderbook_detail,
59            extended_time: options.extended_time,
60            session: options.session,
61            header: None,
62        },
63    };
64
65    let body = prost::Message::encode_to_vec(&req);
66    let resp_frame = client.request(proto_id::QOT_SUB, body).await?;
67
68    let resp: futu_proto::qot_sub::Response =
69        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
70
71    check_ret_type(resp.ret_type, resp.ret_msg)
72}
73
74fn validate_subscribe_options(options: SubscribeOptions) -> Result<()> {
75    match SubscribeOptionsPlan::from_raw(
76        options.session,
77        options.extended_time,
78        options.orderbook_detail,
79    ) {
80        Ok(_) => Ok(()),
81        Err(SubscribePlanError::OvernightSessionUnsupported) => Err(FutuError::Codec(
82            "Qot_Sub: session=OVERNIGHT (4) is not supported; use session=2 (ETH) or 3 (ALL)"
83                .to_string(),
84        )),
85    }
86}
87
88/// 退订行情
89pub async fn unsubscribe(
90    client: &FutuClient,
91    securities: &[Security],
92    sub_types: &[SubType],
93) -> Result<()> {
94    let req = futu_proto::qot_sub::Request {
95        c2s: futu_proto::qot_sub::C2s {
96            security_list: securities.iter().map(|s| s.to_proto()).collect(),
97            sub_type_list: sub_types.iter().map(|t| *t as i32).collect(),
98            is_sub_or_un_sub: false,
99            is_reg_or_un_reg_push: Some(true),
100            reg_push_rehab_type_list: vec![],
101            is_first_push: None,
102            is_unsub_all: None,
103            is_sub_order_book_detail: None,
104            extended_time: None,
105            session: None,
106            header: None,
107        },
108    };
109
110    let body = prost::Message::encode_to_vec(&req);
111    let resp_frame = client.request(proto_id::QOT_SUB, body).await?;
112
113    let resp: futu_proto::qot_sub::Response =
114        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
115
116    check_ret_type(resp.ret_type, resp.ret_msg)
117}
118
119fn check_ret_type(ret_type: i32, ret_msg: Option<String>) -> Result<()> {
120    if ret_type != 0 {
121        return Err(crate::server_err(ret_type, ret_msg));
122    }
123    Ok(())
124}