1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::types::TrdHeader;
6
7#[derive(Debug, Clone)]
9pub struct Order {
10 pub order_id: u64,
11 pub order_id_ex: String,
12 pub trd_side: i32,
13 pub order_type: i32,
14 pub order_status: i32,
15 pub code: String,
16 pub name: String,
17 pub qty: f64,
18 pub price: f64,
19 pub create_time: String,
20 pub update_time: String,
21 pub fill_qty: f64,
22 pub fill_avg_price: f64,
23 pub last_err_msg: String,
24}
25
26impl Order {
27 pub fn from_proto(o: &futu_proto::trd_common::Order) -> Self {
28 Self {
29 order_id: o.order_id,
30 order_id_ex: o.order_id_ex.clone(),
31 trd_side: o.trd_side,
32 order_type: o.order_type,
33 order_status: o.order_status,
34 code: o.code.clone(),
35 name: o.name.clone(),
36 qty: o.qty,
37 price: o.price.unwrap_or(0.0),
38 create_time: o.create_time.clone(),
39 update_time: o.update_time.clone(),
40 fill_qty: o.fill_qty.unwrap_or(0.0),
41 fill_avg_price: o.fill_avg_price.unwrap_or(0.0),
42 last_err_msg: o.last_err_msg.clone().unwrap_or_default(),
43 }
44 }
45}
46
47#[derive(Debug, Clone)]
49pub struct OrderFill {
50 pub fill_id: u64,
51 pub fill_id_ex: String,
52 pub order_id: u64,
53 pub trd_side: i32,
54 pub code: String,
55 pub name: String,
56 pub qty: f64,
57 pub price: f64,
58 pub create_time: String,
59}
60
61impl OrderFill {
62 pub fn from_proto(f: &futu_proto::trd_common::OrderFill) -> Result<Self> {
63 let order_id = f.order_id.ok_or_else(|| {
64 FutuError::Codec(
65 "missing orderID in OrderFill; C++ _APIServer_Trd_Comm.cpp:2145-2151 \
66 sets orderID before sending FTAPI OrderFill"
67 .into(),
68 )
69 })?;
70
71 Ok(Self {
72 fill_id: f.fill_id,
73 fill_id_ex: f.fill_id_ex.clone(),
74 order_id,
75 trd_side: f.trd_side,
76 code: f.code.clone(),
77 name: f.name.clone(),
78 qty: f.qty,
79 price: f.price,
80 create_time: f.create_time.clone(),
81 })
82 }
83}
84
85pub async fn get_order_list(client: &FutuClient, header: &TrdHeader) -> Result<Vec<Order>> {
87 let req = futu_proto::trd_get_order_list::Request {
88 c2s: futu_proto::trd_get_order_list::C2s {
89 header: header.to_proto(),
90 filter_conditions: None,
91 filter_status_list: vec![],
92 refresh_cache: None,
93 },
94 };
95
96 let body = prost::Message::encode_to_vec(&req);
97 let resp_frame = client.request(proto_id::TRD_GET_ORDER_LIST, body).await?;
98
99 let resp: futu_proto::trd_get_order_list::Response =
100 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
101
102 if resp.ret_type != 0 {
103 return Err(crate::server_err(
104 resp.ret_type,
105 resp.ret_msg,
106 resp.err_code,
107 ));
108 }
109
110 let s2c = resp
111 .s2c
112 .ok_or(FutuError::Codec("missing s2c in GetOrderList".into()))?;
113
114 Ok(s2c.order_list.iter().map(Order::from_proto).collect())
115}
116
117pub async fn get_order_fill_list(
119 client: &FutuClient,
120 header: &TrdHeader,
121) -> Result<Vec<OrderFill>> {
122 let req = futu_proto::trd_get_order_fill_list::Request {
123 c2s: futu_proto::trd_get_order_fill_list::C2s {
124 header: header.to_proto(),
125 filter_conditions: None,
126 refresh_cache: None,
127 },
128 };
129
130 let body = prost::Message::encode_to_vec(&req);
131 let resp_frame = client
132 .request(proto_id::TRD_GET_ORDER_FILL_LIST, body)
133 .await?;
134
135 let resp: futu_proto::trd_get_order_fill_list::Response =
136 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
137
138 if resp.ret_type != 0 {
139 return Err(crate::server_err(
140 resp.ret_type,
141 resp.ret_msg,
142 resp.err_code,
143 ));
144 }
145
146 let s2c = resp
147 .s2c
148 .ok_or(FutuError::Codec("missing s2c in GetOrderFillList".into()))?;
149
150 s2c.order_fill_list
151 .iter()
152 .map(OrderFill::from_proto)
153 .collect()
154}
155
156#[cfg(test)]
157mod tests;