futu_backend/trade_query/orders/
order_info.rs1use bytes::Bytes;
2use futu_cache::trd_cache::{CachedOrder, OrderRelationSourceToken, TrdCache};
3use futu_command_spec::{TradeQueryEnvironment, TradeQueryOperation, trade::trade_query_route};
4use futu_core::error::{FutuError, Result};
5use futu_domain_trade_history::detail_request::{
6 CurrentTradeDetailRequestPlan, plan_current_trade_detail_request_like_cpp,
7};
8use prost::Message;
9
10use crate::command_runtime::execute_trade_read;
11use crate::conn::BackendConn;
12use crate::proto_internal::{order_sys_interface, sim_order_sys_interface};
13
14use super::builders::build_order_info_req_base;
15use super::cipher::current_trade_query_real_cipher_or_error_like_cpp;
16use super::helpers::derive_acc_query_context;
17use super::projection::{
18 order_proto_to_cached_with_jp_acc_type_like_cpp, sim_order_proto_to_cached_like_cpp,
19};
20use super::status_helpers::{
21 backend_order_info_status_by_account_like_cpp, backend_order_info_status_like_cpp,
22};
23use super::types::QueryErrorMode;
24
25pub async fn query_order_info_strict_for_push_refresh(
33 backend: &BackendConn,
34 acc_id: u64,
35 trd_cache: &TrdCache,
36 order_ids: &[String],
37 security_type: Option<u32>,
38 exchange: Option<&str>,
39) -> Result<Vec<CachedOrder>> {
40 query_order_info_strict_for_push_refresh_with_source(
41 backend,
42 acc_id,
43 trd_cache,
44 order_ids,
45 security_type,
46 exchange,
47 OrderRelationSourceToken::unknown(backend.connection_generation()),
48 )
49 .await
50}
51
52pub async fn query_order_info_strict_for_push_refresh_with_source(
53 backend: &BackendConn,
54 acc_id: u64,
55 trd_cache: &TrdCache,
56 order_ids: &[String],
57 security_type: Option<u32>,
58 exchange: Option<&str>,
59 source: OrderRelationSourceToken,
60) -> Result<Vec<CachedOrder>> {
61 let filtered_ids = match plan_current_trade_detail_request_like_cpp(order_ids) {
62 CurrentTradeDetailRequestPlan::SkipEmpty => return Ok(vec![]),
63 CurrentTradeDetailRequestPlan::Query { filtered_ids } => filtered_ids,
64 };
65
66 let (trd_env, _account_trd_market, _enabled_markets) =
67 derive_acc_query_context(trd_cache, acc_id);
68 let environment = if trd_env == 1 {
69 TradeQueryEnvironment::Real
70 } else {
71 TradeQueryEnvironment::Sim
72 };
73 let route =
74 trade_query_route(TradeQueryOperation::OrderInfo, environment).ok_or_else(|| {
75 FutuError::Codec(format!(
76 "QueryOrderInfo has no registered {environment:?} backend route"
77 ))
78 })?;
79 let cmd_id = route.cmd_id;
80 let real_cipher = if trd_env == 1 {
81 current_trade_query_real_cipher_or_error_like_cpp(
82 acc_id,
83 trd_cache,
84 QueryErrorMode::StrictPushRefresh,
85 "QueryOrderInfo",
86 )?
87 } else {
88 Vec::new()
89 };
90 let trade_cipher = (trd_env == 1).then_some(real_cipher.as_slice());
91 let req = build_order_info_req_base(
92 acc_id,
93 security_type,
94 exchange,
95 &filtered_ids,
96 real_cipher.clone(),
97 );
98 let resp = execute_trade_read(
99 backend,
100 TradeQueryOperation::OrderInfo,
101 environment,
102 trade_cipher,
103 Bytes::from(req.encode_to_vec()),
104 )
105 .await?;
106 let (orders, received): (Vec<CachedOrder>, usize) = if trd_env == 1 {
107 let parsed: order_sys_interface::OrderDetailRsp = Message::decode(resp.body.as_ref())?;
108 if let Err(status_err) = backend_order_info_status_like_cpp(
109 parsed.result,
110 parsed.msg_header.as_ref(),
111 parsed.err_msg.as_deref(),
112 acc_id,
113 filtered_ids.len(),
114 parsed.orders.len(),
115 ) {
116 return Err(FutuError::ServerError {
117 ret_type: status_err.result,
118 msg: format!("{} (acc_id={acc_id})", status_err.message),
119 });
120 }
121 let received = parsed.orders.len();
122 let orders = parsed
123 .orders
124 .iter()
125 .filter_map(|order| {
126 let jp_acc_type = order
127 .sub_account_id
128 .and_then(|id| trd_cache.jp_acc_type_for_sub_account(acc_id, id));
129 order_proto_to_cached_with_jp_acc_type_like_cpp(trd_env, order, jp_acc_type)
130 })
131 .collect();
132 (orders, received)
133 } else {
134 let parsed: sim_order_sys_interface::OrderDetailRsp = Message::decode(resp.body.as_ref())?;
135 if let Err(status_err) = backend_order_info_status_by_account_like_cpp(
136 parsed.result,
137 parsed
138 .msg_header
139 .as_ref()
140 .and_then(|header| header.account_id),
141 parsed.err_msg.as_deref(),
142 acc_id,
143 filtered_ids.len(),
144 parsed.orders.len(),
145 ) {
146 return Err(FutuError::ServerError {
147 ret_type: status_err.result,
148 msg: format!("{} (acc_id={acc_id})", status_err.message),
149 });
150 }
151 let received = parsed.orders.len();
152 let orders = parsed
153 .orders
154 .iter()
155 .filter_map(|order| sim_order_proto_to_cached_like_cpp(trd_env, order))
156 .collect();
157 (orders, received)
158 };
159 if orders.len() != received {
160 trd_cache.mark_order_relation_snapshot_incomplete_from_source(acc_id, source);
161 }
162 let mut applied_orders = Vec::with_capacity(orders.len());
163 for order in orders {
164 if trd_cache.upsert_order_from_source(acc_id, order.clone(), source) {
165 applied_orders.push(order);
166 }
167 }
168 tracing::debug!(
169 cmd_id,
170 requested = filtered_ids.len(),
171 received,
172 applied = applied_orders.len(),
173 "order details queried"
174 );
175 Ok(applied_orders)
176}