1use 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#[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
24pub 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
61pub 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
86static RECONFIRM_SERIAL: AtomicU32 = AtomicU32::new(20_000_000);
89
90pub 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#[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
153pub 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
188pub 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}