futu_backend/trade_query/crypto_orders/
queries_fills.rs1use futu_core::error::{FutuError, Result};
5use futu_domain_trade_history::pagination::{
6 CryptoOeFillPageDecision, CryptoReadCursorPageDecision,
7 crypto_oe_fill_pagination_exceeded_like_cpp, crypto_read_cursor_pagination_exceeded_like_cpp,
8 decide_crypto_oe_fill_page_like_cpp, decide_crypto_read_cursor_page_like_cpp,
9};
10use futu_domain_trade_history::{
11 plan_crypto_current_fill_list_request_like_cpp, plan_crypto_history_fill_list_request_like_cpp,
12 plan_crypto_order_related_fill_request_like_cpp,
13};
14
15use super::super::*;
16
17use crate::crypto_trade::{
18 CryptoAccountContext, lookup_crypto_account_context, lookup_crypto_read_account_context,
19};
20use crate::trade_cmd::{CryptoTradeOperation, crypto_trade_command};
21
22use super::projections::*;
23use super::types::*;
24
25pub async fn query_crypto_order_fills(
26 backend: &BackendConn,
27 acc_id: u64,
28 trd_cache: &TrdCache,
29) -> Result<Vec<OrderFillInfo>> {
30 let ctx = lookup_crypto_read_account_context(trd_cache, acc_id)?;
33 query_crypto_order_fills_with_context(backend, acc_id, &ctx).await
34}
35
36pub async fn query_crypto_order_fills_for_startup(
39 backend: &BackendConn,
40 acc_id: u64,
41 trd_cache: &TrdCache,
42) -> Result<Vec<OrderFillInfo>> {
43 let ctx = lookup_crypto_read_account_context(trd_cache, acc_id)?;
44 query_crypto_order_fills_with_context(backend, acc_id, &ctx).await
45}
46
47async fn query_crypto_order_fills_with_context(
48 backend: &BackendConn,
49 acc_id: u64,
50 _ctx: &CryptoAccountContext,
51) -> Result<Vec<OrderFillInfo>> {
52 use prost::Message;
53
54 let spec = crypto_trade_command(CryptoTradeOperation::Deals);
55 let mut all_fills = Vec::new();
56 let mut page_token: Option<String> = None;
57
58 for _ in 0..MAX_PAGES {
59 let plan = plan_crypto_current_fill_list_request_like_cpp(page_token.as_deref());
60 let req = inbound_oe::FillListReq {
61 page_size: Some(plan.page_size),
62 page_token: plan.page_token,
63 long_account_id: Some(acc_id),
64 symbol: plan.symbol,
65 list_type: Some(plan.list_type),
66 };
67 let resp = crate::command_runtime::execute_crypto_trade_command(
68 backend,
69 CryptoTradeOperation::Deals,
70 None,
71 bytes::Bytes::from(req.encode_to_vec()),
72 )
73 .await
74 .inspect_err(|e| {
75 tracing::warn!(
76 cmd_id = spec.cmd,
77 error = ?futu_core::log_redact::RedactedFutuError::new(e),
78 "crypto order fill query failed"
79 );
80 })?;
81
82 let parsed: inbound_oe::FillListRsp = Message::decode(resp.body.as_ref()).map_err(|e| {
83 tracing::warn!(
84 cmd_id = spec.cmd,
85 body_len = resp.body.len(),
86 error_class = "protobuf_decode",
87 "crypto order fill query decode failed"
88 );
89 FutuError::Proto(e)
90 })?;
91
92 all_fills.extend(
93 parsed
94 .base_fill_list
95 .iter()
96 .filter_map(project_crypto_base_fill),
97 );
98 match decide_crypto_oe_fill_page_like_cpp(parsed.page_token.as_deref()) {
99 CryptoOeFillPageDecision::Complete => {
100 tracing::debug!(count = all_fills.len(), "crypto order fills queried");
101 return Ok(all_fills);
102 }
103 CryptoOeFillPageDecision::Continue { next_page_token } => {
104 page_token = Some(next_page_token);
105 }
106 }
107 }
108
109 Err(FutuError::Codec(
110 crypto_oe_fill_pagination_exceeded_like_cpp("query_crypto_order_fills", MAX_PAGES),
111 ))
112}
113
114pub async fn query_crypto_history_order_fills(
120 backend: &BackendConn,
121 acc_id: u64,
122 trd_cache: &TrdCache,
123 start_micros: u64,
124 end_micros: u64,
125) -> Result<Vec<OrderFillInfo>> {
126 use prost::Message;
127
128 let _ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
129 let spec = crypto_trade_command(CryptoTradeOperation::HistoryDeals);
130 let mut all_fills = Vec::new();
131 let mut page_token: Option<String> = None;
132
133 for _ in 0..MAX_PAGES {
134 let plan = plan_crypto_history_fill_list_request_like_cpp(
135 start_micros,
136 end_micros,
137 page_token.as_deref(),
138 );
139 let req = inbound_oe::GetFillListByAccountAndTimeRangeRequest {
140 page_size: Some(plan.page_size),
141 page_token: plan.page_token,
142 long_account_id: Some(acc_id),
143 start_time: Some(plan.start_time_micros),
144 end_time: Some(plan.end_time_micros),
145 symbol: plan.symbol,
146 };
147 let resp = crate::command_runtime::execute_crypto_trade_command(
148 backend,
149 CryptoTradeOperation::HistoryDeals,
150 None,
151 bytes::Bytes::from(req.encode_to_vec()),
152 )
153 .await
154 .inspect_err(|e| {
155 tracing::warn!(
156 cmd_id = spec.cmd,
157 error = ?futu_core::log_redact::RedactedFutuError::new(e),
158 "crypto history fill query failed"
159 );
160 })?;
161
162 let parsed: inbound_oe::GetFillListByAccountAndTimeRangeResponse =
163 Message::decode(resp.body.as_ref()).map_err(|e| {
164 tracing::warn!(
165 cmd_id = spec.cmd,
166 body_len = resp.body.len(),
167 error_class = "protobuf_decode",
168 "crypto history fill query decode failed"
169 );
170 FutuError::Proto(e)
171 })?;
172
173 all_fills.extend(
174 parsed
175 .base_fill_list
176 .iter()
177 .filter_map(project_crypto_base_fill),
178 );
179 match decide_crypto_oe_fill_page_like_cpp(parsed.page_token.as_deref()) {
180 CryptoOeFillPageDecision::Complete => {
181 tracing::debug!(count = all_fills.len(), "crypto history fills queried");
182 return Ok(all_fills);
183 }
184 CryptoOeFillPageDecision::Continue { next_page_token } => {
185 page_token = Some(next_page_token);
186 }
187 }
188 }
189
190 Err(FutuError::Codec(
191 crypto_oe_fill_pagination_exceeded_like_cpp("query_crypto_history_order_fills", MAX_PAGES),
192 ))
193}
194
195pub async fn query_crypto_order_related_fills(
201 backend: &BackendConn,
202 acc_id: u64,
203 trd_cache: &TrdCache,
204 order_id_ex: &str,
205) -> Result<Vec<OrderFillInfo>> {
206 use prost::Message;
207
208 let initial_plan = plan_crypto_order_related_fill_request_like_cpp(order_id_ex, None)
209 .map_err(FutuError::Codec)?;
210 let order_id_ex = initial_plan.order_id;
211
212 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
213 let spec = crypto_trade_command(CryptoTradeOperation::OrderFillDetail);
214 let mut all_fills = Vec::new();
215 let mut page_flag: Option<String> = None;
216
217 for _ in 0..MAX_PAGES {
218 let plan =
219 plan_crypto_order_related_fill_request_like_cpp(&order_id_ex, page_flag.as_deref())
220 .map_err(FutuError::Codec)?;
221 let req = inbound_read::OrderFillDetailReq {
222 msg_header: Some(ctx.build_crypto_msg_header("order_fill_detail")),
223 page_size: Some(plan.page_size),
224 page_flag: plan.page_flag,
225 order_id: Some(plan.order_id),
226 };
227 let resp = crate::command_runtime::execute_crypto_trade_command(
228 backend,
229 CryptoTradeOperation::OrderFillDetail,
230 None,
231 bytes::Bytes::from(req.encode_to_vec()),
232 )
233 .await
234 .inspect_err(|e| {
235 tracing::warn!(
236 cmd_id = spec.cmd,
237 order_id = %order_id_ex,
238 error = ?futu_core::log_redact::RedactedFutuError::new(e),
239 "crypto order related fill query failed"
240 );
241 })?;
242
243 let parsed: inbound_read::OrderFillDetailRsp = Message::decode(resp.body.as_ref())
244 .map_err(|e| {
245 tracing::warn!(
246 cmd_id = spec.cmd,
247 order_id = %order_id_ex,
248 body_len = resp.body.len(),
249 error_class = "protobuf_decode",
250 "crypto order related fill query decode failed"
251 );
252 FutuError::Proto(e)
253 })?;
254
255 all_fills.extend(
256 parsed
257 .order_fills
258 .iter()
259 .filter_map(project_crypto_read_fill),
260 );
261 match decide_crypto_read_cursor_page_like_cpp(
262 "query_crypto_order_related_fills",
263 parsed.completed,
264 parsed.page_flag.as_deref(),
265 all_fills.len(),
266 ) {
267 CryptoReadCursorPageDecision::Complete => {
268 tracing::debug!(
269 order_id = %order_id_ex,
270 count = all_fills.len(),
271 "crypto order related fills queried"
272 );
273 return Ok(all_fills);
274 }
275 CryptoReadCursorPageDecision::Continue { next_page_flag } => {
276 page_flag = Some(next_page_flag);
277 }
278 CryptoReadCursorPageDecision::PartialMissingPageFlag { error_message, .. } => {
279 return Err(FutuError::Codec(error_message));
280 }
281 }
282 }
283
284 Err(FutuError::Codec(
285 crypto_read_cursor_pagination_exceeded_like_cpp(
286 "query_crypto_order_related_fills",
287 MAX_PAGES,
288 ),
289 ))
290}