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(crate::server_err(resp.ret_type, resp.ret_msg));
57    }
58
59    let s2c = resp
60        .s2c
61        .ok_or(FutuError::Codec("missing s2c in GetHistoryKL".into()))?;
62
63    Ok(HistoryKLResult {
64        security: Security::from_proto(&s2c.security),
65        kl_list: s2c.kl_list.iter().map(KLine::from_proto).collect(),
66        next_kl_time: None, // field removed in new proto; use next_req_key for pagination
67    })
68}
69
70/// 分页请求历史 K 线参数
71#[derive(Debug, Clone)]
72pub struct RequestHistoryKLParams<'a> {
73    pub security: &'a Security,
74    pub rehab_type: RehabType,
75    pub kl_type: KLType,
76    pub begin_time: &'a str,
77    pub end_time: &'a str,
78    pub max_num: Option<i32>,
79    pub next_req_key: Option<&'a [u8]>,
80}
81
82/// 分页请求历史 K 线结果
83#[derive(Debug, Clone)]
84pub struct RequestHistoryKLResult {
85    pub security: Security,
86    pub kl_list: Vec<KLine>,
87    /// 下次请求的分页 key,None 表示数据已全部返回
88    pub next_req_key: Option<Vec<u8>>,
89}
90
91/// 分页请求历史 K 线(支持大数据量分页拉取)
92pub async fn request_history_kl(
93    client: &FutuClient,
94    params: &RequestHistoryKLParams<'_>,
95) -> Result<RequestHistoryKLResult> {
96    let req = futu_proto::qot_request_history_kl::Request {
97        c2s: futu_proto::qot_request_history_kl::C2s {
98            rehab_type: params.rehab_type as i32,
99            kl_type: params.kl_type as i32,
100            security: params.security.to_proto(),
101            begin_time: params.begin_time.to_string(),
102            end_time: params.end_time.to_string(),
103            max_ack_kl_num: params.max_num,
104            need_kl_fields_flag: None,
105            next_req_key: params.next_req_key.map(|k| k.to_vec()),
106            extended_time: None,
107            session: None,
108            header: None,
109        },
110    };
111
112    let body = prost::Message::encode_to_vec(&req);
113    let resp_frame = client
114        .request(proto_id::QOT_REQUEST_HISTORY_KL, body)
115        .await?;
116
117    let resp: futu_proto::qot_request_history_kl::Response =
118        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
119
120    if resp.ret_type != 0 {
121        return Err(crate::server_err(resp.ret_type, resp.ret_msg));
122    }
123
124    let s2c = resp
125        .s2c
126        .ok_or(FutuError::Codec("missing s2c in RequestHistoryKL".into()))?;
127
128    Ok(RequestHistoryKLResult {
129        security: Security::from_proto(&s2c.security),
130        kl_list: s2c.kl_list.iter().map(KLine::from_proto).collect(),
131        next_req_key: s2c.next_req_key,
132    })
133}