futu_backend/trade_query/crypto_orders/
queries_orders.rs1use futu_cache::trd_cache::CachedOrder;
5use futu_core::error::{FutuError, Result};
6
7use super::super::*;
8
9use crate::crypto_trade::lookup_crypto_account_context;
10use crate::trade_cmd::{CryptoTradeOperation, crypto_trade_command};
11
12use super::projections::*;
13use super::types::*;
14
15pub async fn query_crypto_orders(
16 backend: &BackendConn,
17 acc_id: u64,
18 trd_cache: &TrdCache,
19) -> Result<Vec<CachedOrder>> {
20 use prost::Message;
21
22 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
23 let spec = crypto_trade_command(CryptoTradeOperation::Orders);
24 let mut all_orders = Vec::new();
25 let mut page_flag: Option<String> = None;
26 let mut completed = false;
27
28 for _ in 0..MAX_PAGES {
29 let req = inbound_read::OrderListReq {
30 msg_header: Some(ctx.build_crypto_msg_header("order_list")),
31 page_size: Some(CRYPTO_ORDER_PAGE_SIZE),
32 page_flag: page_flag.clone(),
33 list_type: Some(inbound_read::CryptoOrderListType::COrderListTypeRecentUpdate as u32),
34 };
35 let resp = backend
36 .request(spec.cmd, req.encode_to_vec())
37 .await
38 .map_err(|e| {
39 tracing::warn!(acc_id, cmd_id = spec.cmd, error = %e, "crypto order query failed");
40 e
41 })?;
42
43 let parsed: inbound_read::OrderListRsp =
44 Message::decode(resp.body.as_ref()).map_err(|e| {
45 tracing::warn!(
46 acc_id,
47 cmd_id = spec.cmd,
48 body_len = resp.body.len(),
49 error = %e,
50 "crypto order query decode failed"
51 );
52 FutuError::Proto(e)
53 })?;
54
55 all_orders.extend(parsed.orders.iter().filter_map(project_crypto_order));
56 if parsed.completed.unwrap_or(false) {
57 completed = true;
58 break;
59 }
60 match parsed.page_flag {
61 Some(ref flag) if !flag.is_empty() => page_flag = Some(flag.clone()),
62 _ => {
63 return Err(FutuError::Codec(
64 "query_crypto_orders: partial response without page_flag".to_string(),
65 ));
66 }
67 }
68 }
69
70 if !completed {
71 return Err(FutuError::Codec(format!(
72 "query_crypto_orders: pagination exceeded {MAX_PAGES} pages"
73 )));
74 }
75
76 trd_cache.merge_preserving_stubs(acc_id, all_orders.clone());
77 tracing::debug!(acc_id, count = all_orders.len(), "crypto orders queried");
78 Ok(all_orders)
79}
80
81pub async fn query_crypto_order_info(
88 backend: &BackendConn,
89 acc_id: u64,
90 trd_cache: &TrdCache,
91 order_ids: &[String],
92) -> Result<Vec<CachedOrder>> {
93 use prost::Message;
94
95 let requested: Vec<String> = order_ids
96 .iter()
97 .map(|id| id.trim())
98 .filter(|id| !id.is_empty())
99 .map(str::to_owned)
100 .collect();
101 if requested.is_empty() {
102 return Err(FutuError::Codec(
103 "query_crypto_order_info: empty order_ids".to_string(),
104 ));
105 }
106
107 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
108 let spec = crypto_trade_command(CryptoTradeOperation::OrderInfo);
109 let req = inbound_read::OrderDetailReq {
110 msg_header: Some(ctx.build_crypto_msg_header("order_detail")),
111 order_ids: requested.clone(),
112 };
113 let resp = backend
114 .request(spec.cmd, req.encode_to_vec())
115 .await
116 .map_err(|e| {
117 tracing::warn!(
118 acc_id,
119 cmd_id = spec.cmd,
120 order_ids = ?requested,
121 error = %e,
122 "crypto order detail query failed"
123 );
124 e
125 })?;
126
127 let parsed: inbound_read::OrderDetailRsp =
128 Message::decode(resp.body.as_ref()).map_err(|e| {
129 tracing::warn!(
130 acc_id,
131 cmd_id = spec.cmd,
132 body_len = resp.body.len(),
133 error = %e,
134 "crypto order detail query decode failed"
135 );
136 FutuError::Proto(e)
137 })?;
138 let orders: Vec<CachedOrder> = parsed
139 .orders
140 .iter()
141 .filter_map(project_crypto_order)
142 .collect();
143 if orders.len() != requested.len() {
144 tracing::warn!(
145 acc_id,
146 requested = requested.len(),
147 returned = orders.len(),
148 order_ids = ?requested,
149 "crypto order detail response count differs from requested ids"
150 );
151 }
152
153 trd_cache.merge_preserving_stubs(acc_id, orders.clone());
154 tracing::debug!(acc_id, count = orders.len(), "crypto order details queried");
155 Ok(orders)
156}
157
158pub async fn query_crypto_history_orders(
165 backend: &BackendConn,
166 acc_id: u64,
167 trd_cache: &TrdCache,
168 start_micros: u64,
169 end_micros: u64,
170) -> Result<Vec<CachedOrder>> {
171 use prost::Message;
172
173 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
174 let spec = crypto_trade_command(CryptoTradeOperation::HistoryOrders);
175 let mut all_orders = Vec::new();
176 let mut page_flag: Option<String> = None;
177 let mut completed = false;
178
179 for _ in 0..MAX_PAGES {
180 let req = inbound_read::HistoryOrderListReq {
181 msg_header: Some(ctx.build_crypto_msg_header("history_order_list")),
182 page_size: Some(CRYPTO_HISTORY_ORDER_PAGE_SIZE),
183 page_flag: page_flag.clone(),
184 start_time: Some(start_micros as i64),
185 end_time: Some(end_micros as i64),
186 symbol: None,
187 order_status: Vec::new(),
188 currency: Vec::new(),
189 side: Vec::new(),
190 query_word: None,
191 ord_type: Vec::new(),
192 };
193 let resp = backend
194 .request(spec.cmd, req.encode_to_vec())
195 .await
196 .map_err(|e| {
197 tracing::warn!(
198 acc_id,
199 cmd_id = spec.cmd,
200 error = %e,
201 "crypto history order query failed"
202 );
203 e
204 })?;
205
206 let parsed: inbound_read::HistoryOrderListRsp = Message::decode(resp.body.as_ref())
207 .map_err(|e| {
208 tracing::warn!(
209 acc_id,
210 cmd_id = spec.cmd,
211 body_len = resp.body.len(),
212 error = %e,
213 "crypto history order query decode failed"
214 );
215 FutuError::Proto(e)
216 })?;
217
218 all_orders.extend(parsed.orders.iter().filter_map(project_crypto_order));
219 if parsed.completed.unwrap_or(false) {
220 completed = true;
221 break;
222 }
223 match parsed.page_flag {
224 Some(ref flag) if !flag.is_empty() => page_flag = Some(flag.clone()),
225 _ => {
226 return Err(FutuError::Codec(
227 "query_crypto_history_orders: partial response without page_flag".to_string(),
228 ));
229 }
230 }
231 }
232
233 if !completed {
234 return Err(FutuError::Codec(format!(
235 "query_crypto_history_orders: pagination exceeded {MAX_PAGES} pages"
236 )));
237 }
238
239 tracing::debug!(
240 acc_id,
241 count = all_orders.len(),
242 "crypto history orders queried"
243 );
244 Ok(all_orders)
245}