1use std::sync::Arc;
5
6use futu_cache::trd_cache::TrdCache;
7
8use super::super::*;
9use crate::conn::BackendConn;
10use crate::proto_internal::odr_sys_cmn;
11
12use super::types::BackendResponseStatusError;
13
14pub fn backend_trade_response_status_like_cpp(
15 message_name: &str,
16 result: Option<i32>,
17 msg_header: Option<&odr_sys_cmn::MsgHeader>,
18 err_msg: Option<&str>,
19 acc_id: u64,
20) -> std::result::Result<(), BackendResponseStatusError> {
21 let msg_header = msg_header.ok_or_else(|| BackendResponseStatusError {
22 result: -1,
23 message: format!("{message_name} missing msg_header"),
24 is_backend_error: false,
25 })?;
26 backend_trade_response_status_by_account_like_cpp(
27 message_name,
28 result,
29 msg_header.account_id,
30 err_msg,
31 acc_id,
32 )
33}
34
35pub(super) fn backend_trade_response_status_by_account_like_cpp(
36 message_name: &str,
37 result: Option<i32>,
38 backend_acc_id: Option<u64>,
39 err_msg: Option<&str>,
40 acc_id: u64,
41) -> std::result::Result<(), BackendResponseStatusError> {
42 let result = result.ok_or_else(|| BackendResponseStatusError {
43 result: -1,
44 message: format!("{message_name} missing result"),
45 is_backend_error: false,
46 })?;
47 let backend_acc_id = backend_acc_id.ok_or_else(|| BackendResponseStatusError {
48 result: -1,
49 message: format!("{message_name} msg_header missing account_id"),
50 is_backend_error: false,
51 })?;
52 if backend_acc_id != acc_id {
53 return Err(BackendResponseStatusError {
54 result: -1,
55 message: format!(
56 "{message_name} msg_header.account_id mismatch, got {backend_acc_id}, expected {acc_id}"
57 ),
58 is_backend_error: false,
59 });
60 }
61 if result != 0 {
62 return Err(BackendResponseStatusError {
63 result,
64 message: err_msg
65 .unwrap_or("trade query backend rejected")
66 .to_string(),
67 is_backend_error: true,
68 });
69 }
70 Ok(())
71}
72
73pub fn backend_order_list_status_like_cpp(
79 result: Option<i32>,
80 msg_header: Option<&odr_sys_cmn::MsgHeader>,
81 err_msg: Option<&str>,
82 acc_id: u64,
83) -> std::result::Result<(), BackendResponseStatusError> {
84 backend_trade_response_status_like_cpp("OrderListRsp", result, msg_header, err_msg, acc_id)
85}
86
87pub fn backend_order_info_status_like_cpp(
93 result: Option<i32>,
94 msg_header: Option<&odr_sys_cmn::MsgHeader>,
95 err_msg: Option<&str>,
96 acc_id: u64,
97 expected_count: usize,
98 actual_count: usize,
99) -> std::result::Result<(), BackendResponseStatusError> {
100 backend_trade_response_status_like_cpp("OrderDetailRsp", result, msg_header, err_msg, acc_id)?;
101 if actual_count == 0 || actual_count != expected_count {
102 return Err(BackendResponseStatusError {
103 result: -1,
104 message: format!(
105 "OrderDetailRsp orders count mismatch, got {actual_count}, expected {expected_count}"
106 ),
107 is_backend_error: false,
108 });
109 }
110 Ok(())
111}
112
113pub(super) fn backend_order_info_status_by_account_like_cpp(
114 result: Option<i32>,
115 backend_acc_id: Option<u64>,
116 err_msg: Option<&str>,
117 acc_id: u64,
118 expected_count: usize,
119 actual_count: usize,
120) -> std::result::Result<(), BackendResponseStatusError> {
121 backend_trade_response_status_by_account_like_cpp(
122 "OrderDetailRsp",
123 result,
124 backend_acc_id,
125 err_msg,
126 acc_id,
127 )?;
128 if actual_count == 0 || actual_count != expected_count {
129 return Err(BackendResponseStatusError {
130 result: -1,
131 message: format!(
132 "OrderDetailRsp orders count mismatch, got {actual_count}, expected {expected_count}"
133 ),
134 is_backend_error: false,
135 });
136 }
137 Ok(())
138}
139
140pub fn backend_order_fill_list_status_like_cpp(
146 result: Option<i32>,
147 msg_header: Option<&odr_sys_cmn::MsgHeader>,
148 err_msg: Option<&str>,
149 acc_id: u64,
150) -> std::result::Result<(), BackendResponseStatusError> {
151 backend_trade_response_status_like_cpp("OrderFillListRsp", result, msg_header, err_msg, acc_id)
152}
153
154pub fn backend_order_fill_info_status_like_cpp(
158 result: Option<i32>,
159 msg_header: Option<&odr_sys_cmn::MsgHeader>,
160 err_msg: Option<&str>,
161 acc_id: u64,
162 expected_count: usize,
163 actual_count: usize,
164) -> std::result::Result<(), BackendResponseStatusError> {
165 backend_trade_response_status_like_cpp(
166 "OrderFillInfoRsp",
167 result,
168 msg_header,
169 err_msg,
170 acc_id,
171 )?;
172 if actual_count == 0 || actual_count != expected_count {
173 return Err(BackendResponseStatusError {
174 result: -1,
175 message: format!(
176 "OrderFillInfoRsp order_fills count mismatch, got {actual_count}, expected {expected_count}"
177 ),
178 is_backend_error: false,
179 });
180 }
181 Ok(())
182}
183
184pub fn backend_order_status_to_ftapi(status: u32) -> i32 {
190 match status {
191 1 => 2, 2 => 5, 3 => 10, 4 => 11, 5 => 15, 6 => 21, 7 => 14, 101 => 22, 102 => 1, 103 => 24, 104 => 23, _ => -1, }
204}
205
206pub fn backend_order_type_to_ftapi(
219 order_type: u32,
220 trd_market: Option<i32>,
221 security_type: Option<i32>,
222) -> i32 {
223 const TRD_MARKET_HK: i32 = 1;
224 const SECURITY_TYPE_COMMON: i32 = 1;
225 let is_hk_common =
226 trd_market == Some(TRD_MARKET_HK) && security_type == Some(SECURITY_TYPE_COMMON);
227
228 match order_type {
229 1 if is_hk_common => 5, 1 => 1, 2 => 1, 3 => 2, 4 => 6, 5 => 7, 6 => 9, 7 => 8, 8 => 10, 9 => 11, 10 => 12, 11 => 13, 12 => 14, 13 => 15, 21 => 16, 22 => 17, 23 => 18, 24 => 19, _ => -1,
248 }
249}
250
251pub fn map_backend_market_to_trd_market(market: u32) -> i32 {
253 match market {
254 1 => 1, 2 => 2, 3 => 3, 10 => 5, 15 => 6, v => v as i32,
260 }
261}
262
263pub fn backend_real_market_to_trd_market_like_cpp(market: u32) -> i32 {
264 match market {
265 7 => 200, 11 => 111, 12 => 112, 13 => 113, 14 => 114, 15 => 15, 23 => 123, 24 => 124, v => v as i32,
274 }
275}
276
277pub fn is_valid_real_trd_market_like_cpp(trd_market: i32) -> bool {
278 matches!(trd_market, 1 | 2 | 4 | 5 | 6 | 8 | 15 | 111 | 112 | 200)
279}
280
281pub fn backend_order_unpacked_trd_market_like_cpp(
282 trd_env: i32,
283 o: &odr_sys_cmn::Order,
284) -> Option<Option<i32>> {
285 o.order_id.as_ref()?;
289 o.side?;
290 let trd_market = if trd_env == 1 {
291 let trd_market = backend_real_market_to_trd_market_like_cpp(o.market?);
292 if !is_valid_real_trd_market_like_cpp(trd_market) {
293 return None;
294 }
295 Some(trd_market)
296 } else {
297 o.market.map(map_backend_market_to_trd_market)
298 };
299 o.symbol.as_ref()?;
300 o.create_time?;
301 o.status?;
302 Some(trd_market)
303}
304
305pub async fn init_trade_data(backend: &Arc<BackendConn>, trd_cache: &Arc<TrdCache>) {
307 let accounts = trd_cache.get_accounts();
308 let real_accounts: Vec<u64> = accounts
309 .iter()
310 .filter(|a| a.trd_env == 1) .map(|a| a.acc_id)
312 .collect();
313
314 tracing::info!(
315 count = real_accounts.len(),
316 "querying trade data for real accounts"
317 );
318
319 for &acc_id in &real_accounts {
320 if let Err(e) = query_account_info(backend, acc_id, trd_cache, None, None).await {
325 tracing::warn!(
326 acc_id,
327 error = %e,
328 "init trade data account info query failed"
329 );
330 }
331 }
332
333 tracing::info!("trade data initialization complete");
334}