futu_backend/trade_query/crypto_orders/
queries_misc.rs1use futu_cache::static_data::CryptoTradeConfig;
5use futu_core::error::{FutuError, Result};
6
7use super::super::common::pf;
8use super::super::*;
9
10use crate::crypto_trade::{CryptoAccountContext, lookup_crypto_account_context};
11use crate::proto_internal::cash_change_detail_cmn;
12use crate::trade_cmd::{CryptoTradeOperation, crypto_trade_command};
13
14use super::projections::*;
15use super::types::*;
16
17pub async fn query_crypto_order_fees(
18 backend: &BackendConn,
19 acc_id: u64,
20 trd_cache: &TrdCache,
21 order_ids: &[String],
22) -> Result<Vec<CryptoOrderFeeInfo>> {
23 use prost::Message;
24
25 let _ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
26 let spec = crypto_trade_command(CryptoTradeOperation::BatchOrderFee);
27 let req = inbound_oe::BatchQueryOrderFeeReq {
28 order_id_list: order_ids.to_vec(),
29 long_account_id: Some(acc_id),
30 };
31 let resp = backend
32 .request(spec.cmd, req.encode_to_vec())
33 .await
34 .map_err(|e| {
35 tracing::warn!(
36 acc_id,
37 cmd_id = spec.cmd,
38 error = %e,
39 "crypto order fee query failed"
40 );
41 e
42 })?;
43
44 let parsed: inbound_oe::BatchQueryOrderFeeRsp =
45 Message::decode(resp.body.as_ref()).map_err(|e| {
46 tracing::warn!(
47 acc_id,
48 cmd_id = spec.cmd,
49 body_len = resp.body.len(),
50 error = %e,
51 "crypto order fee query decode failed"
52 );
53 FutuError::Proto(e)
54 })?;
55
56 let fees = parsed
57 .fee_group_list
58 .iter()
59 .filter_map(project_crypto_order_fee)
60 .collect();
61 Ok(fees)
62}
63
64pub async fn query_crypto_cash_logs(
70 backend: &BackendConn,
71 acc_id: u64,
72 trd_cache: &TrdCache,
73 begin_time: u64,
74 end_time: u64,
75) -> Result<Vec<CryptoCashLogInfo>> {
76 use prost::Message;
77
78 let ctx = lookup_crypto_account_context(trd_cache, acc_id)?;
79 let spec = crypto_trade_command(CryptoTradeOperation::CashLog);
80 let mut all_logs = Vec::new();
81 let mut log_id: Option<String> = None;
82
83 for _ in 0..MAX_PAGES {
84 let req = cash_change_detail_cmn::GetCashLogReq {
85 market: Some(cash_change_detail_cmn::Market::Crypto as u32),
86 account_id: Some(ctx.require_intra_acc_id("crypto_cash_log")?),
87 broker_id: Some(ctx.require_broker_id("crypto_cash_log")?),
88 long_account_id: Some(acc_id),
89 begin_time: Some(begin_time),
90 end_time: Some(end_time),
91 log_id: log_id.clone(),
92 ..Default::default()
93 };
94 let resp = backend
95 .request(spec.cmd, req.encode_to_vec())
96 .await
97 .map_err(|e| {
98 tracing::warn!(
99 acc_id,
100 cmd_id = spec.cmd,
101 error = %e,
102 "crypto cash log query failed"
103 );
104 e
105 })?;
106
107 let parsed: cash_change_detail_cmn::GetCashLogRsp = Message::decode(resp.body.as_ref())
108 .map_err(|e| {
109 tracing::warn!(
110 acc_id,
111 cmd_id = spec.cmd,
112 body_len = resp.body.len(),
113 error = %e,
114 "crypto cash log decode failed"
115 );
116 FutuError::Proto(e)
117 })?;
118
119 let result = parsed.result.unwrap_or(0);
120 if result != 0 {
121 return Err(FutuError::ServerError {
122 ret_type: result,
123 msg: parsed
124 .err_msg
125 .unwrap_or_else(|| "query_crypto_cash_logs: backend error".to_string()),
126 });
127 }
128
129 all_logs.extend(
130 parsed
131 .monthly_log_list
132 .iter()
133 .flat_map(|month| month.cash_log_list.iter())
134 .filter_map(project_crypto_cash_log),
135 );
136
137 if parsed.has_more.unwrap_or(false) {
138 match parsed.next_log_id {
139 Some(ref next) if !next.is_empty() => {
140 log_id = Some(next.clone());
141 continue;
142 }
143 _ => {
144 return Err(FutuError::Codec(
145 "query_crypto_cash_logs: has_more without next_log_id".to_string(),
146 ));
147 }
148 }
149 }
150
151 tracing::debug!(acc_id, count = all_logs.len(), "crypto cash logs queried");
152 return Ok(all_logs);
153 }
154
155 Err(FutuError::Codec(format!(
156 "query_crypto_cash_logs: pagination exceeded {MAX_PAGES} pages"
157 )))
158}
159
160pub async fn query_crypto_trade_configs(
166 backend: &BackendConn,
167 broker_id: u32,
168) -> Result<Vec<CryptoTradeConfig>> {
169 use prost::Message;
170
171 let spec = crypto_trade_command(CryptoTradeOperation::FetchTradeConfig);
172 let req = inbound_oe::FetchTradeConfigRequest {
173 currency_pair_list: Vec::new(),
174 data_from: None,
175 data_max_count: None,
176 symbol_list: Vec::new(),
177 status: None,
178 };
179 let resp = backend
180 .request(spec.cmd, req.encode_to_vec())
181 .await
182 .map_err(|e| {
183 tracing::warn!(broker_id, cmd_id = spec.cmd, error = %e, "crypto trade config query failed");
184 e
185 })?;
186
187 let parsed: inbound_oe::FetchTradeConfigResponse = Message::decode(resp.body.as_ref())
188 .map_err(|e| {
189 tracing::warn!(
190 broker_id,
191 cmd_id = spec.cmd,
192 body_len = resp.body.len(),
193 error = %e,
194 "crypto trade config decode failed"
195 );
196 FutuError::Proto(e)
197 })?;
198 crypto_trade_config_response_status(&parsed)?;
199
200 Ok(parsed
201 .full_trade_config_list
202 .iter()
203 .filter_map(project_crypto_trade_config)
204 .collect())
205}
206
207#[allow(clippy::too_many_arguments)]
215pub async fn query_crypto_max_buy_sell_qty(
216 backend: &BackendConn,
217 ctx: &CryptoAccountContext,
218 account_market: u32,
219 order_type: u32,
220 coin: &str,
221 currency: &str,
222 price: Option<String>,
223 order_id: Option<String>,
224) -> Result<CryptoMaxBuySellQtyInfo> {
225 use prost::Message;
226
227 let spec = crypto_trade_command(CryptoTradeOperation::MaxBuySellQty);
228 let req = crypto_risk::GetMaxBuySellReq {
229 account: Some(crypto_risk_comm::Account {
230 cid: Some(ctx.require_customer_id("max_buy_sell_qty")?),
231 acct_id: Some(ctx.require_intra_acc_id("max_buy_sell_qty")?),
232 market: Some(account_market),
233 broker_id: Some(ctx.require_broker_id("max_buy_sell_qty")?),
234 long_acct_id: Some(ctx.acc_id),
235 }),
236 order_type: Some(order_type),
237 currency: Some(currency.to_string()),
238 coin: Some(coin.to_string()),
239 price,
240 order_id,
241 };
242 let resp = backend
243 .request(spec.cmd, req.encode_to_vec())
244 .await
245 .map_err(|e| {
246 tracing::warn!(
247 acc_id = ctx.acc_id,
248 cmd_id = spec.cmd,
249 error = %e,
250 "crypto max buy/sell qty query failed"
251 );
252 e
253 })?;
254 let parsed: crypto_risk::GetMaxBuySellRsp =
255 Message::decode(resp.body.as_ref()).map_err(|e| {
256 tracing::warn!(
257 acc_id = ctx.acc_id,
258 cmd_id = spec.cmd,
259 body_len = resp.body.len(),
260 error = %e,
261 "crypto max buy/sell qty decode failed"
262 );
263 FutuError::Proto(e)
264 })?;
265
266 Ok(CryptoMaxBuySellQtyInfo {
267 max_cash_buy_qty: pf(&parsed.max_cash_buy_qty),
268 max_sell_qty: pf(&parsed.max_sell_qty),
269 })
270}