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