1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4use std::time::Duration;
5
6use crate::types::TrdHeader;
7
8#[derive(Debug, Clone)]
10pub struct Order {
11 pub order_id: u64,
12 pub order_id_ex: String,
13 pub trd_side: i32,
14 pub order_type: i32,
15 pub order_status: i32,
16 pub code: String,
17 pub name: String,
18 pub qty: f64,
19 pub price: f64,
20 pub create_time: String,
21 pub update_time: String,
22 pub fill_qty: f64,
23 pub fill_avg_price: f64,
24 pub last_err_msg: String,
25}
26
27impl Order {
28 pub fn from_proto(o: &futu_proto::trd_common::Order) -> Self {
29 Self {
30 order_id: o.order_id,
31 order_id_ex: o.order_id_ex.clone(),
32 trd_side: o.trd_side,
33 order_type: o.order_type,
34 order_status: o.order_status,
35 code: o.code.clone(),
36 name: o.name.clone(),
37 qty: o.qty,
38 price: o.price.unwrap_or(0.0),
39 create_time: o.create_time.clone(),
40 update_time: o.update_time.clone(),
41 fill_qty: o.fill_qty.unwrap_or(0.0),
42 fill_avg_price: o.fill_avg_price.unwrap_or(0.0),
43 last_err_msg: o.last_err_msg.clone().unwrap_or_default(),
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct OrderFill {
51 pub fill_id: u64,
52 pub fill_id_ex: String,
53 pub order_id: u64,
54 pub trd_side: i32,
55 pub code: String,
56 pub name: String,
57 pub qty: f64,
58 pub price: f64,
59 pub create_time: String,
60}
61
62impl OrderFill {
63 pub fn from_proto(f: &futu_proto::trd_common::OrderFill) -> Result<Self> {
64 let order_id = f.order_id.ok_or_else(|| {
65 FutuError::Codec("missing orderID in OrderFill".into())
67 })?;
68
69 Ok(Self {
70 fill_id: f.fill_id,
71 fill_id_ex: f.fill_id_ex.clone(),
72 order_id,
73 trd_side: f.trd_side,
74 code: f.code.clone(),
75 name: f.name.clone(),
76 qty: f.qty,
77 price: f.price,
78 create_time: f.create_time.clone(),
79 })
80 }
81}
82
83pub async fn get_order_list(client: &FutuClient, header: &TrdHeader) -> Result<Vec<Order>> {
85 get_order_list_with_options(client, header, None, None).await
86}
87
88pub async fn get_order_list_with_refresh_cache(
93 client: &FutuClient,
94 header: &TrdHeader,
95 timeout: Duration,
96) -> Result<Vec<Order>> {
97 get_order_list_with_options(client, header, Some(true), Some(timeout)).await
98}
99
100fn build_get_order_list_request(
101 header: &TrdHeader,
102 refresh_cache: Option<bool>,
103) -> futu_proto::trd_get_order_list::Request {
104 futu_proto::trd_get_order_list::Request {
105 c2s: futu_proto::trd_get_order_list::C2s {
106 header: header.to_proto(),
107 filter_conditions: None,
108 filter_status_list: vec![],
109 refresh_cache,
110 },
111 }
112}
113
114async fn get_order_list_with_options(
115 client: &FutuClient,
116 header: &TrdHeader,
117 refresh_cache: Option<bool>,
118 timeout: Option<Duration>,
119) -> Result<Vec<Order>> {
120 let req = build_get_order_list_request(header, refresh_cache);
121
122 let body = prost::Message::encode_to_vec(&req);
123 let resp_frame = match timeout {
124 Some(timeout) => {
125 client
126 .request_with_timeout(proto_id::TRD_GET_ORDER_LIST, body, timeout)
127 .await?
128 }
129 None => client.request(proto_id::TRD_GET_ORDER_LIST, body).await?,
130 };
131
132 let resp: futu_proto::trd_get_order_list::Response =
133 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
134
135 if resp.ret_type != 0 {
136 return Err(crate::server_err(
137 resp.ret_type,
138 resp.ret_msg,
139 resp.err_code,
140 ));
141 }
142
143 let s2c = resp
144 .s2c
145 .ok_or(FutuError::Codec("missing s2c in GetOrderList".into()))?;
146
147 Ok(s2c.order_list.iter().map(Order::from_proto).collect())
148}
149
150pub async fn get_order_fill_list(
152 client: &FutuClient,
153 header: &TrdHeader,
154) -> Result<Vec<OrderFill>> {
155 let req = futu_proto::trd_get_order_fill_list::Request {
156 c2s: futu_proto::trd_get_order_fill_list::C2s {
157 header: header.to_proto(),
158 filter_conditions: None,
159 refresh_cache: None,
160 },
161 };
162
163 let body = prost::Message::encode_to_vec(&req);
164 let resp_frame = client
165 .request(proto_id::TRD_GET_ORDER_FILL_LIST, body)
166 .await?;
167
168 let resp: futu_proto::trd_get_order_fill_list::Response =
169 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
170
171 if resp.ret_type != 0 {
172 return Err(crate::server_err(
173 resp.ret_type,
174 resp.ret_msg,
175 resp.err_code,
176 ));
177 }
178
179 let s2c = resp
180 .s2c
181 .ok_or(FutuError::Codec("missing s2c in GetOrderFillList".into()))?;
182
183 s2c.order_fill_list
184 .iter()
185 .map(OrderFill::from_proto)
186 .collect()
187}
188
189#[cfg(test)]
190mod tests;