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 .map_err(|e| {
75 tracing::warn!(
76 cmd_id = spec.cmd,
77 error = %e,
78 "crypto order fill query failed"
79 );
80 e
81 })?;
82
83 let parsed: inbound_oe::FillListRsp = Message::decode(resp.body.as_ref()).map_err(|e| {
84 tracing::warn!(
85 cmd_id = spec.cmd,
86 body_len = resp.body.len(),
87 error = %e,
88 "crypto order fill query decode failed"
89 );
90 FutuError::Proto(e)
91 })?;
92
93 all_fills.extend(
94 parsed
95 .base_fill_list
96 .iter()
97 .filter_map(project_crypto_base_fill),
98 );
99 match decide_crypto_oe_fill_page_like_cpp(parsed.page_token.as_deref()) {
100 CryptoOeFillPageDecision::Complete => {
101 tracing::debug!(count = all_fills.len(), "crypto order fills queried");
102 return Ok(all_fills);
103 }
104 CryptoOeFillPageDecision::Continue { next_page_token } => {
105 page_token = Some(next_page_token);
106 }
107 }
108 }
109
110 Err(FutuError::Codec(
111 crypto_oe_fill_pagination_exceeded_like_cpp("query_crypto_order_fills", MAX_PAGES),
112 ))
113}
114
115pub async fn query_crypto_history_order_fills(
121 backend: &BackendConn,
122 acc_id: u64,
123 trd_cache: &TrdCache,
124 start_micros: u64,
125 end_micros: u64,
126) -> Result<Vec<OrderFillInfo>> {
127 use prost::Message;
128
129 let _ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
130 let spec = crypto_trade_command(CryptoTradeOperation::HistoryDeals);
131 let mut all_fills = Vec::new();
132 let mut page_token: Option<String> = None;
133
134 for _ in 0..MAX_PAGES {
135 let plan = plan_crypto_history_fill_list_request_like_cpp(
136 start_micros,
137 end_micros,
138 page_token.as_deref(),
139 );
140 let req = inbound_oe::GetFillListByAccountAndTimeRangeRequest {
141 page_size: Some(plan.page_size),
142 page_token: plan.page_token,
143 long_account_id: Some(acc_id),
144 start_time: Some(plan.start_time_micros),
145 end_time: Some(plan.end_time_micros),
146 symbol: plan.symbol,
147 };
148 let resp = crate::command_runtime::execute_crypto_trade_command(
149 backend,
150 CryptoTradeOperation::HistoryDeals,
151 None,
152 bytes::Bytes::from(req.encode_to_vec()),
153 )
154 .await
155 .map_err(|e| {
156 tracing::warn!(
157 cmd_id = spec.cmd,
158 error = %e,
159 "crypto history fill query failed"
160 );
161 e
162 })?;
163
164 let parsed: inbound_oe::GetFillListByAccountAndTimeRangeResponse =
165 Message::decode(resp.body.as_ref()).map_err(|e| {
166 tracing::warn!(
167 cmd_id = spec.cmd,
168 body_len = resp.body.len(),
169 error = %e,
170 "crypto history fill query decode failed"
171 );
172 FutuError::Proto(e)
173 })?;
174
175 all_fills.extend(
176 parsed
177 .base_fill_list
178 .iter()
179 .filter_map(project_crypto_base_fill),
180 );
181 match decide_crypto_oe_fill_page_like_cpp(parsed.page_token.as_deref()) {
182 CryptoOeFillPageDecision::Complete => {
183 tracing::debug!(count = all_fills.len(), "crypto history fills queried");
184 return Ok(all_fills);
185 }
186 CryptoOeFillPageDecision::Continue { next_page_token } => {
187 page_token = Some(next_page_token);
188 }
189 }
190 }
191
192 Err(FutuError::Codec(
193 crypto_oe_fill_pagination_exceeded_like_cpp("query_crypto_history_order_fills", MAX_PAGES),
194 ))
195}
196
197pub async fn query_crypto_order_related_fills(
203 backend: &BackendConn,
204 acc_id: u64,
205 trd_cache: &TrdCache,
206 order_id_ex: &str,
207) -> Result<Vec<OrderFillInfo>> {
208 use prost::Message;
209
210 let initial_plan = plan_crypto_order_related_fill_request_like_cpp(order_id_ex, None)
211 .map_err(FutuError::Codec)?;
212 let order_id_ex = initial_plan.order_id;
213
214 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
215 let spec = crypto_trade_command(CryptoTradeOperation::OrderFillDetail);
216 let mut all_fills = Vec::new();
217 let mut page_flag: Option<String> = None;
218
219 for _ in 0..MAX_PAGES {
220 let plan =
221 plan_crypto_order_related_fill_request_like_cpp(&order_id_ex, page_flag.as_deref())
222 .map_err(FutuError::Codec)?;
223 let req = inbound_read::OrderFillDetailReq {
224 msg_header: Some(ctx.build_crypto_msg_header("order_fill_detail")),
225 page_size: Some(plan.page_size),
226 page_flag: plan.page_flag,
227 order_id: Some(plan.order_id),
228 };
229 let resp = crate::command_runtime::execute_crypto_trade_command(
230 backend,
231 CryptoTradeOperation::OrderFillDetail,
232 None,
233 bytes::Bytes::from(req.encode_to_vec()),
234 )
235 .await
236 .map_err(|e| {
237 tracing::warn!(
238 cmd_id = spec.cmd,
239 order_id = %order_id_ex,
240 error = %e,
241 "crypto order related fill query failed"
242 );
243 e
244 })?;
245
246 let parsed: inbound_read::OrderFillDetailRsp = Message::decode(resp.body.as_ref())
247 .map_err(|e| {
248 tracing::warn!(
249 cmd_id = spec.cmd,
250 order_id = %order_id_ex,
251 body_len = resp.body.len(),
252 error = %e,
253 "crypto order related fill query decode failed"
254 );
255 FutuError::Proto(e)
256 })?;
257
258 all_fills.extend(
259 parsed
260 .order_fills
261 .iter()
262 .filter_map(project_crypto_read_fill),
263 );
264 match decide_crypto_read_cursor_page_like_cpp(
265 "query_crypto_order_related_fills",
266 parsed.completed,
267 parsed.page_flag.as_deref(),
268 all_fills.len(),
269 ) {
270 CryptoReadCursorPageDecision::Complete => {
271 tracing::debug!(
272 order_id = %order_id_ex,
273 count = all_fills.len(),
274 "crypto order related fills queried"
275 );
276 return Ok(all_fills);
277 }
278 CryptoReadCursorPageDecision::Continue { next_page_flag } => {
279 page_flag = Some(next_page_flag);
280 }
281 CryptoReadCursorPageDecision::PartialMissingPageFlag { error_message, .. } => {
282 return Err(FutuError::Codec(error_message));
283 }
284 }
285 }
286
287 Err(FutuError::Codec(
288 crypto_read_cursor_pagination_exceeded_like_cpp(
289 "query_crypto_order_related_fills",
290 MAX_PAGES,
291 ),
292 ))
293}