Skip to main content

futu_qot/
history_kl.rs

1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::types::{KLType, KLine, RehabType, Security};
6
7/// 历史 K 线查询结果
8#[derive(Debug, Clone)]
9pub struct HistoryKLResult {
10    pub security: Security,
11    pub kl_list: Vec<KLine>,
12    /// 下一页 K 线时间(分页时使用)
13    pub next_kl_time: Option<String>,
14}
15
16/// 获取历史 K 线数据
17///
18/// 按时间范围查询历史 K 线。
19/// - `begin_time`: 开始时间,格式 "yyyy-MM-dd"
20/// - `end_time`: 结束时间,格式 "yyyy-MM-dd"
21/// - `max_num`: 最多返回根数,None 表示不限制
22pub async fn get_history_kl(
23    client: &FutuClient,
24    security: &Security,
25    rehab_type: RehabType,
26    kl_type: KLType,
27    begin_time: &str,
28    end_time: &str,
29    max_num: Option<i32>,
30) -> Result<HistoryKLResult> {
31    let req = futu_proto::qot_request_history_kl::Request {
32        c2s: futu_proto::qot_request_history_kl::C2s {
33            rehab_type: rehab_type as i32,
34            kl_type: kl_type as i32,
35            security: security.to_proto(),
36            begin_time: begin_time.to_string(),
37            end_time: end_time.to_string(),
38            max_ack_kl_num: max_num,
39            need_kl_fields_flag: None,
40            next_req_key: None,
41            extended_time: None,
42            session: None,
43            header: None,
44        },
45    };
46
47    let body = prost::Message::encode_to_vec(&req);
48    let resp_frame = client
49        .request(proto_id::QOT_REQUEST_HISTORY_KL, body)
50        .await?;
51
52    let resp: futu_proto::qot_request_history_kl::Response =
53        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
54
55    if resp.ret_type != 0 {
56        return Err(FutuError::ServerError {
57            ret_type: resp.ret_type,
58            msg: resp.ret_msg.unwrap_or_default(),
59        });
60    }
61
62    let s2c = resp
63        .s2c
64        .ok_or(FutuError::Codec("missing s2c in GetHistoryKL".into()))?;
65
66    Ok(HistoryKLResult {
67        security: Security::from_proto(&s2c.security),
68        kl_list: s2c.kl_list.iter().map(KLine::from_proto).collect(),
69        next_kl_time: None, // field removed in new proto; use next_req_key for pagination
70    })
71}
72
73/// 分页请求历史 K 线参数
74#[derive(Debug, Clone)]
75pub struct RequestHistoryKLParams<'a> {
76    pub security: &'a Security,
77    pub rehab_type: RehabType,
78    pub kl_type: KLType,
79    pub begin_time: &'a str,
80    pub end_time: &'a str,
81    pub max_num: Option<i32>,
82    pub next_req_key: Option<&'a [u8]>,
83}
84
85/// 分页请求历史 K 线结果
86#[derive(Debug, Clone)]
87pub struct RequestHistoryKLResult {
88    pub security: Security,
89    pub kl_list: Vec<KLine>,
90    /// 下次请求的分页 key,None 表示数据已全部返回
91    pub next_req_key: Option<Vec<u8>>,
92}
93
94/// 分页请求历史 K 线(支持大数据量分页拉取)
95pub async fn request_history_kl(
96    client: &FutuClient,
97    params: &RequestHistoryKLParams<'_>,
98) -> Result<RequestHistoryKLResult> {
99    let req = futu_proto::qot_request_history_kl::Request {
100        c2s: futu_proto::qot_request_history_kl::C2s {
101            rehab_type: params.rehab_type as i32,
102            kl_type: params.kl_type as i32,
103            security: params.security.to_proto(),
104            begin_time: params.begin_time.to_string(),
105            end_time: params.end_time.to_string(),
106            max_ack_kl_num: params.max_num,
107            need_kl_fields_flag: None,
108            next_req_key: params.next_req_key.map(|k| k.to_vec()),
109            extended_time: None,
110            session: None,
111            header: None,
112        },
113    };
114
115    let body = prost::Message::encode_to_vec(&req);
116    let resp_frame = client
117        .request(proto_id::QOT_REQUEST_HISTORY_KL, body)
118        .await?;
119
120    let resp: futu_proto::qot_request_history_kl::Response =
121        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
122
123    if resp.ret_type != 0 {
124        return Err(FutuError::ServerError {
125            ret_type: resp.ret_type,
126            msg: resp.ret_msg.unwrap_or_default(),
127        });
128    }
129
130    let s2c = resp
131        .s2c
132        .ok_or(FutuError::Codec("missing s2c in RequestHistoryKL".into()))?;
133
134    Ok(RequestHistoryKLResult {
135        security: Security::from_proto(&s2c.security),
136        kl_list: s2c.kl_list.iter().map(KLine::from_proto).collect(),
137        next_req_key: s2c.next_req_key,
138    })
139}