futu_trd/
misc.rs

1// 交易杂项: 最大可买卖、订阅推送、确认订单、历史订单/成交
2
3use std::sync::atomic::{AtomicU32, Ordering};
4
5use futu_core::error::{FutuError, Result};
6use futu_core::proto_id;
7use futu_net::client::FutuClient;
8
9use crate::query::{Order, OrderFill};
10use crate::types::TrdHeader;
11
12// ===== 最大可买卖数量 =====
13
14/// 最大可买卖数量查询参数
15#[derive(Debug, Clone)]
16pub struct MaxTrdQtysParams {
17    pub header: TrdHeader,
18    pub order_type: i32,
19    pub code: String,
20    pub price: f64,
21    pub order_id: Option<u64>,
22}
23
24/// 获取最大可买卖数量(原始 proto 响应)
25pub async fn get_max_trd_qtys(
26    client: &FutuClient,
27    params: &MaxTrdQtysParams,
28) -> Result<futu_proto::trd_get_max_trd_qtys::S2c> {
29    let req = futu_proto::trd_get_max_trd_qtys::Request {
30        c2s: futu_proto::trd_get_max_trd_qtys::C2s {
31            header: params.header.to_proto(),
32            order_type: params.order_type,
33            code: params.code.clone(),
34            price: params.price,
35            order_id: params.order_id,
36            adjust_price: None,
37            adjust_side_and_limit: None,
38            sec_market: None,
39            order_id_ex: None,
40            session: None,
41            position_id: None,
42        },
43    };
44
45    let body = prost::Message::encode_to_vec(&req);
46    let resp_frame = client.request(proto_id::TRD_GET_MAX_TRD_QTYS, body).await?;
47    let resp: futu_proto::trd_get_max_trd_qtys::Response =
48        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
49
50    if resp.ret_type != 0 {
51        return Err(FutuError::ServerError {
52            ret_type: resp.ret_type,
53            msg: resp.ret_msg.unwrap_or_default(),
54        });
55    }
56
57    resp.s2c
58        .ok_or(FutuError::Codec("missing s2c in GetMaxTrdQtys".into()))
59}
60
61// ===== 订阅账户推送 =====
62
63/// 订阅交易账户的推送(订单/成交更新)
64pub async fn sub_acc_push(client: &FutuClient, acc_ids: &[u64]) -> Result<()> {
65    let req = futu_proto::trd_sub_acc_push::Request {
66        c2s: futu_proto::trd_sub_acc_push::C2s {
67            acc_id_list: acc_ids.to_vec(),
68        },
69    };
70
71    let body = prost::Message::encode_to_vec(&req);
72    let resp_frame = client.request(proto_id::TRD_SUB_ACC_PUSH, body).await?;
73    let resp: futu_proto::trd_sub_acc_push::Response =
74        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
75
76    if resp.ret_type != 0 {
77        return Err(FutuError::ServerError {
78            ret_type: resp.ret_type,
79            msg: resp.ret_msg.unwrap_or_default(),
80        });
81    }
82
83    Ok(())
84}
85
86// ===== 确认订单 =====
87
88static RECONFIRM_SERIAL: AtomicU32 = AtomicU32::new(20_000_000);
89
90/// 再次确认订单
91pub async fn reconfirm_order(
92    client: &FutuClient,
93    header: &TrdHeader,
94    order_id: u64,
95    reason: i32,
96) -> Result<u64> {
97    let serial = RECONFIRM_SERIAL.fetch_add(1, Ordering::Relaxed);
98    let req = futu_proto::trd_reconfirm_order::Request {
99        c2s: futu_proto::trd_reconfirm_order::C2s {
100            packet_id: futu_proto::common::PacketId {
101                conn_id: 0,
102                serial_no: serial,
103            },
104            header: header.to_proto(),
105            order_id,
106            reconfirm_reason: reason,
107        },
108    };
109
110    let body = prost::Message::encode_to_vec(&req);
111    let resp_frame = client.request(proto_id::TRD_RECONFIRM_ORDER, body).await?;
112    let resp: futu_proto::trd_reconfirm_order::Response =
113        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
114
115    if resp.ret_type != 0 {
116        return Err(FutuError::ServerError {
117            ret_type: resp.ret_type,
118            msg: resp.ret_msg.unwrap_or_default(),
119        });
120    }
121
122    let s2c = resp
123        .s2c
124        .ok_or(FutuError::Codec("missing s2c in ReconfirmOrder".into()))?;
125
126    Ok(s2c.order_id)
127}
128
129// ===== 历史订单 =====
130
131/// 历史订单过滤条件
132#[derive(Debug, Clone)]
133pub struct HistoryFilterConditions {
134    pub code_list: Vec<String>,
135    pub id_list: Vec<u64>,
136    pub begin_time: Option<String>,
137    pub end_time: Option<String>,
138}
139
140impl HistoryFilterConditions {
141    pub fn to_proto(&self) -> futu_proto::trd_common::TrdFilterConditions {
142        futu_proto::trd_common::TrdFilterConditions {
143            code_list: self.code_list.clone(),
144            id_list: self.id_list.clone(),
145            begin_time: self.begin_time.clone(),
146            end_time: self.end_time.clone(),
147            order_id_ex_list: vec![],
148            filter_market: None,
149        }
150    }
151}
152
153/// 查询历史订单列表
154pub async fn get_history_order_list(
155    client: &FutuClient,
156    header: &TrdHeader,
157    filter: &HistoryFilterConditions,
158) -> Result<Vec<Order>> {
159    let req = futu_proto::trd_get_history_order_list::Request {
160        c2s: futu_proto::trd_get_history_order_list::C2s {
161            header: header.to_proto(),
162            filter_conditions: filter.to_proto(),
163            filter_status_list: vec![],
164        },
165    };
166
167    let body = prost::Message::encode_to_vec(&req);
168    let resp_frame = client
169        .request(proto_id::TRD_GET_HISTORY_ORDER_LIST, body)
170        .await?;
171    let resp: futu_proto::trd_get_history_order_list::Response =
172        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
173
174    if resp.ret_type != 0 {
175        return Err(FutuError::ServerError {
176            ret_type: resp.ret_type,
177            msg: resp.ret_msg.unwrap_or_default(),
178        });
179    }
180
181    let s2c = resp.s2c.ok_or(FutuError::Codec(
182        "missing s2c in GetHistoryOrderList".into(),
183    ))?;
184
185    Ok(s2c.order_list.iter().map(Order::from_proto).collect())
186}
187
188/// 查询历史成交列表
189pub async fn get_history_order_fill_list(
190    client: &FutuClient,
191    header: &TrdHeader,
192    filter: &HistoryFilterConditions,
193) -> Result<Vec<OrderFill>> {
194    let req = futu_proto::trd_get_history_order_fill_list::Request {
195        c2s: futu_proto::trd_get_history_order_fill_list::C2s {
196            header: header.to_proto(),
197            filter_conditions: filter.to_proto(),
198        },
199    };
200
201    let body = prost::Message::encode_to_vec(&req);
202    let resp_frame = client
203        .request(proto_id::TRD_GET_HISTORY_ORDER_FILL_LIST, body)
204        .await?;
205    let resp: futu_proto::trd_get_history_order_fill_list::Response =
206        prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
207
208    if resp.ret_type != 0 {
209        return Err(FutuError::ServerError {
210            ret_type: resp.ret_type,
211            msg: resp.ret_msg.unwrap_or_default(),
212        });
213    }
214
215    let s2c = resp.s2c.ok_or(FutuError::Codec(
216        "missing s2c in GetHistoryOrderFillList".into(),
217    ))?;
218
219    Ok(s2c
220        .order_fill_list
221        .iter()
222        .map(OrderFill::from_proto)
223        .collect())
224}