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