Skip to main content

futu_backend/trade_query/orders/
status_helpers.rs

1//! trade_query/orders/status_helpers — backend response_status_like_cpp + backend_order_status/type/market_to_ftapi + init_trade_data
2//! (v1.4.110 CC Batch J: 拆自 orders.rs L781-888 + 1073-1213)
3
4use std::sync::Arc;
5
6use futu_cache::{qot_right::QotRightCache, trd_cache::TrdCache};
7pub use futu_domain_trade_history::projection::orders::is_valid_real_trd_market_like_cpp;
8#[cfg(test)]
9pub use futu_domain_trade_history::projection::scalars::backend_order_type_to_ftapi_like_cpp as backend_order_type_to_ftapi;
10pub use futu_domain_trade_history::projection::scalars::{
11    backend_order_status_to_ftapi_like_cpp as backend_order_status_to_ftapi,
12    backend_real_market_to_trd_market_like_cpp,
13};
14use futu_domain_trade_history::status::{
15    BackendTradeMsgHeaderFacts, BackendTradeResponseStatusInput,
16    backend_order_fill_info_status_like_cpp as domain_backend_order_fill_info_status_like_cpp,
17    backend_order_fill_list_status_like_cpp as domain_backend_order_fill_list_status_like_cpp,
18    backend_order_info_status_by_account_like_cpp as domain_backend_order_info_status_by_account_like_cpp,
19    backend_order_info_status_like_cpp as domain_backend_order_info_status_like_cpp,
20    backend_order_list_status_like_cpp as domain_backend_order_list_status_like_cpp,
21};
22
23use super::super::*;
24use crate::conn::BackendConn;
25use crate::proto_internal::odr_sys_cmn;
26
27use super::types::BackendResponseStatusError;
28
29pub(super) const fn backend_response_status_log_class(is_backend_error: bool) -> &'static str {
30    if is_backend_error {
31        "backend_rejected"
32    } else {
33        "invalid_response"
34    }
35}
36
37/// Match C++ order-list response gating for backend refresh helpers.
38///
39/// Ref:
40/// - `FutuOpenD/Src/NNProtoCenter/Trade/Order/NNProto_Trd_OrderReal.cpp:245-288`
41/// - `FutuOpenD/Src/NNProtoCenter/Trade/Order/NNProto_Trd_OrderSimulate.cpp:239-282`
42#[cfg(test)]
43pub fn backend_order_list_status_like_cpp(
44    result: Option<i32>,
45    msg_header: Option<&odr_sys_cmn::MsgHeader>,
46    err_msg: Option<&str>,
47    acc_id: u64,
48) -> std::result::Result<(), BackendResponseStatusError> {
49    backend_order_list_status_by_account_like_cpp(
50        result,
51        msg_header.and_then(|header| header.account_id),
52        err_msg,
53        acc_id,
54    )
55}
56
57pub(super) fn backend_order_list_status_by_account_like_cpp(
58    result: Option<i32>,
59    backend_acc_id: Option<u64>,
60    err_msg: Option<&str>,
61    acc_id: u64,
62) -> std::result::Result<(), BackendResponseStatusError> {
63    domain_backend_order_list_status_like_cpp(BackendTradeResponseStatusInput {
64        message_name: "OrderListRsp",
65        result,
66        msg_header: backend_acc_id.map(|account_id| BackendTradeMsgHeaderFacts {
67            account_id: Some(account_id),
68        }),
69        err_msg,
70        acc_id,
71    })
72}
73
74/// Match C++ single-order-info response gating.
75///
76/// Ref:
77/// - `FutuOpenD/Src/NNProtoCenter/Trade/Order/NNProto_Trd_OrderReal.cpp:368-418`
78/// - `FutuOpenD/Src/NNProtoCenter/Trade/Order/NNProto_Trd_OrderSimulate.cpp:362-412`
79pub fn backend_order_info_status_like_cpp(
80    result: Option<i32>,
81    msg_header: Option<&odr_sys_cmn::MsgHeader>,
82    err_msg: Option<&str>,
83    acc_id: u64,
84    expected_count: usize,
85    actual_count: usize,
86) -> std::result::Result<(), BackendResponseStatusError> {
87    domain_backend_order_info_status_like_cpp(
88        BackendTradeResponseStatusInput {
89            message_name: "OrderDetailRsp",
90            result,
91            msg_header: msg_header.map(backend_trade_msg_header_facts),
92            err_msg,
93            acc_id,
94        },
95        expected_count,
96        actual_count,
97    )
98}
99
100pub(super) fn backend_order_info_status_by_account_like_cpp(
101    result: Option<i32>,
102    backend_acc_id: Option<u64>,
103    err_msg: Option<&str>,
104    acc_id: u64,
105    expected_count: usize,
106    actual_count: usize,
107) -> std::result::Result<(), BackendResponseStatusError> {
108    domain_backend_order_info_status_by_account_like_cpp(
109        result,
110        backend_acc_id,
111        err_msg,
112        acc_id,
113        expected_count,
114        actual_count,
115    )
116}
117
118/// Match C++ current-deal response gating for backend refresh helpers.
119///
120/// Ref:
121/// - `FutuOpenD/Src/NNProtoCenter/Trade/Deal/NNProto_Trd_DealReal.cpp:259-305`
122/// - `FutuOpenD/Src/NNProtoCenter/Trade/_NNProto_Trd_Comm.h:20-30`
123pub fn backend_order_fill_list_status_like_cpp(
124    result: Option<i32>,
125    msg_header: Option<&odr_sys_cmn::MsgHeader>,
126    err_msg: Option<&str>,
127    acc_id: u64,
128) -> std::result::Result<(), BackendResponseStatusError> {
129    domain_backend_order_fill_list_status_like_cpp(BackendTradeResponseStatusInput {
130        message_name: "OrderFillListRsp",
131        result,
132        msg_header: msg_header.map(backend_trade_msg_header_facts),
133        err_msg,
134        acc_id,
135    })
136}
137
138/// Match C++ single-deal-info response gating.
139///
140/// Ref: `FutuOpenD/Src/NNProtoCenter/Trade/Deal/NNProto_Trd_DealReal.cpp:382-431`.
141pub fn backend_order_fill_info_status_like_cpp(
142    result: Option<i32>,
143    msg_header: Option<&odr_sys_cmn::MsgHeader>,
144    err_msg: Option<&str>,
145    acc_id: u64,
146    expected_count: usize,
147    actual_count: usize,
148) -> std::result::Result<(), BackendResponseStatusError> {
149    domain_backend_order_fill_info_status_like_cpp(
150        BackendTradeResponseStatusInput {
151            message_name: "OrderFillInfoRsp",
152            result,
153            msg_header: msg_header.map(backend_trade_msg_header_facts),
154            err_msg,
155            acc_id,
156        },
157        expected_count,
158        actual_count,
159    )
160}
161
162fn backend_trade_msg_header_facts(header: &odr_sys_cmn::MsgHeader) -> BackendTradeMsgHeaderFacts {
163    BackendTradeMsgHeaderFacts {
164        account_id: header.account_id,
165    }
166}
167
168/// 为所有真实账户查询资金和持仓 (CMD 3020)
169pub async fn init_trade_data(
170    backend: &Arc<BackendConn>,
171    trd_cache: &Arc<TrdCache>,
172    qot_right_cache: &Arc<QotRightCache>,
173) {
174    let accounts = trd_cache.get_accounts();
175    let real_accounts: Vec<u64> = accounts
176        .iter()
177        .filter(|a| a.trd_env == 1) // 只查真实账户
178        .map(|a| a.acc_id)
179        .collect();
180
181    tracing::info!(
182        count = real_accounts.len(),
183        "querying trade data for real accounts"
184    );
185
186    for &acc_id in &real_accounts {
187        // v1.4.106 codex 1556 F1+F3 + v1.4.107 fanout:
188        // bootstrap query 不知 user-requested currency / asset_category, 全传
189        // None 走 backend helper 的 C++ category expansion (JP margin=Foreign,
190        // JP derivative=Domestic+Foreign, other=None).
191        let quote_level = super::super::account_info::Cmd3020QuoteLevel::from_has_us_utp(
192            qot_right_cache.get().has_us_utp,
193        );
194        if let Err(e) =
195            query_account_info(backend, acc_id, trd_cache, None, None, quote_level).await
196        {
197            tracing::warn!(
198                error = ?futu_core::log_redact::RedactedFutuError::new(&e),
199                "init trade data account info query failed"
200            );
201        }
202    }
203
204    tracing::info!("trade data initialization complete");
205}