asterdex_sdk/futures/endpoints/
advanced_trading.rs1use crate::futures::models::account::{
4 AdlQuantileResponse, CommissionRateResponse, MultiAssetsModeResponse,
5 PositionMarginHistoryRecord, StatusMsgResponse,
6};
7use crate::futures::models::trading::{
8 AllOrdersParams, CountdownParams, CountdownResponse, EmptyResponse, GetOrderParams,
9 IncomeParams, IncomeRecord, OrderResponse, PositionMarginParams, PositionMarginResponse,
10 UserTrade, UserTradesParams,
11};
12use crate::rest::client::RestClient;
13use crate::rest::error::AsterDexError;
14use crate::rest::response::ApiResponse;
15
16impl RestClient {
17 pub async fn get_order(
19 &self,
20 params: GetOrderParams,
21 ) -> Result<ApiResponse<OrderResponse>, AsterDexError> {
22 let order_id_str = params.order_id.map(|id| id.to_string());
23 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(3);
24 pairs.push(("symbol", ¶ms.symbol));
25 if let Some(ref s) = order_id_str {
26 pairs.push(("orderId", s));
27 }
28 if let Some(ref cid) = params.orig_client_order_id {
29 pairs.push(("origClientOrderId", cid));
30 }
31 self.signed_get("/fapi/v3/order", &pairs).await
32 }
33
34 pub async fn get_open_order(
36 &self,
37 params: GetOrderParams,
38 ) -> Result<ApiResponse<OrderResponse>, AsterDexError> {
39 let order_id_str = params.order_id.map(|id| id.to_string());
40 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(3);
41 pairs.push(("symbol", ¶ms.symbol));
42 if let Some(ref s) = order_id_str {
43 pairs.push(("orderId", s));
44 }
45 if let Some(ref cid) = params.orig_client_order_id {
46 pairs.push(("origClientOrderId", cid));
47 }
48 self.signed_get("/fapi/v3/openOrder", &pairs).await
49 }
50
51 pub async fn get_all_orders(
53 &self,
54 params: AllOrdersParams,
55 ) -> Result<ApiResponse<Vec<OrderResponse>>, AsterDexError> {
56 let order_id_str = params.order_id.map(|id| id.to_string());
57 let start_time_str = params.start_time.map(|t| t.to_string());
58 let end_time_str = params.end_time.map(|t| t.to_string());
59 let limit_str = params.limit.map(|l| l.to_string());
60
61 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
62 pairs.push(("symbol", ¶ms.symbol));
63 if let Some(ref s) = order_id_str {
64 pairs.push(("orderId", s));
65 }
66 if let Some(ref s) = start_time_str {
67 pairs.push(("startTime", s));
68 }
69 if let Some(ref s) = end_time_str {
70 pairs.push(("endTime", s));
71 }
72 if let Some(ref s) = limit_str {
73 pairs.push(("limit", s));
74 }
75 self.signed_get("/fapi/v3/allOrders", &pairs).await
76 }
77
78 pub async fn get_user_trades(
80 &self,
81 params: UserTradesParams,
82 ) -> Result<ApiResponse<Vec<UserTrade>>, AsterDexError> {
83 let order_id_str = params.order_id.map(|id| id.to_string());
84 let start_time_str = params.start_time.map(|t| t.to_string());
85 let end_time_str = params.end_time.map(|t| t.to_string());
86 let from_id_str = params.from_id.map(|id| id.to_string());
87 let limit_str = params.limit.map(|l| l.to_string());
88
89 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(6);
90 pairs.push(("symbol", ¶ms.symbol));
91 if let Some(ref s) = order_id_str {
92 pairs.push(("orderId", s));
93 }
94 if let Some(ref s) = start_time_str {
95 pairs.push(("startTime", s));
96 }
97 if let Some(ref s) = end_time_str {
98 pairs.push(("endTime", s));
99 }
100 if let Some(ref s) = from_id_str {
101 pairs.push(("fromId", s));
102 }
103 if let Some(ref s) = limit_str {
104 pairs.push(("limit", s));
105 }
106 self.signed_get("/fapi/v3/trades", &pairs).await
107 }
108
109 pub async fn get_income(
111 &self,
112 params: IncomeParams,
113 ) -> Result<ApiResponse<Vec<IncomeRecord>>, AsterDexError> {
114 let start_time_str = params.start_time.map(|t| t.to_string());
115 let end_time_str = params.end_time.map(|t| t.to_string());
116 let limit_str = params.limit.map(|l| l.to_string());
117
118 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
119 if let Some(ref s) = params.symbol {
120 pairs.push(("symbol", s));
121 }
122 if let Some(ref it) = params.income_type {
123 pairs.push(("incomeType", it));
124 }
125 if let Some(ref s) = start_time_str {
126 pairs.push(("startTime", s));
127 }
128 if let Some(ref s) = end_time_str {
129 pairs.push(("endTime", s));
130 }
131 if let Some(ref s) = limit_str {
132 pairs.push(("limit", s));
133 }
134 self.signed_get("/fapi/v3/income", &pairs).await
135 }
136
137 pub async fn set_position_margin(
139 &self,
140 params: PositionMarginParams,
141 ) -> Result<ApiResponse<PositionMarginResponse>, AsterDexError> {
142 let margin_type_str = params.margin_type.to_string();
143 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(4);
144 pairs.push(("symbol", ¶ms.symbol));
145 pairs.push(("amount", ¶ms.amount));
146 pairs.push(("type", &margin_type_str));
147 if let Some(ref ps) = params.position_side {
148 pairs.push(("positionSide", ps));
149 }
150 self.signed_post("/fapi/v3/positionMargin", &pairs).await
151 }
152
153 pub async fn get_position_margin_history(
155 &self,
156 symbol: &str,
157 margin_type: Option<u8>,
158 start_time: Option<u64>,
159 end_time: Option<u64>,
160 limit: Option<u32>,
161 ) -> Result<ApiResponse<Vec<PositionMarginHistoryRecord>>, AsterDexError> {
162 let margin_type_str = margin_type.map(|t| t.to_string());
163 let start_time_str = start_time.map(|t| t.to_string());
164 let end_time_str = end_time.map(|t| t.to_string());
165 let limit_str = limit.map(|l| l.to_string());
166
167 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
168 pairs.push(("symbol", symbol));
169 if let Some(ref s) = margin_type_str {
170 pairs.push(("type", s));
171 }
172 if let Some(ref s) = start_time_str {
173 pairs.push(("startTime", s));
174 }
175 if let Some(ref s) = end_time_str {
176 pairs.push(("endTime", s));
177 }
178 if let Some(ref s) = limit_str {
179 pairs.push(("limit", s));
180 }
181 self.signed_get("/fapi/v3/positionMargin/history", &pairs).await
182 }
183
184 pub async fn get_adl_quantile(
186 &self,
187 symbol: Option<&str>,
188 ) -> Result<ApiResponse<Vec<AdlQuantileResponse>>, AsterDexError> {
189 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(1);
190 if let Some(s) = symbol {
191 pairs.push(("symbol", s));
192 }
193 self.signed_get("/fapi/v3/adlQuantile", &pairs).await
194 }
195
196 pub async fn get_force_orders(
198 &self,
199 symbol: Option<&str>,
200 auto_close_type: Option<&str>,
201 start_time: Option<u64>,
202 end_time: Option<u64>,
203 limit: Option<u32>,
204 ) -> Result<ApiResponse<Vec<OrderResponse>>, AsterDexError> {
205 let start_time_str = start_time.map(|t| t.to_string());
206 let end_time_str = end_time.map(|t| t.to_string());
207 let limit_str = limit.map(|l| l.to_string());
208
209 let mut pairs: Vec<(&str, &str)> = Vec::with_capacity(5);
210 if let Some(s) = symbol {
211 pairs.push(("symbol", s));
212 }
213 if let Some(t) = auto_close_type {
214 pairs.push(("autoCloseType", t));
215 }
216 if let Some(ref s) = start_time_str {
217 pairs.push(("startTime", s));
218 }
219 if let Some(ref s) = end_time_str {
220 pairs.push(("endTime", s));
221 }
222 if let Some(ref s) = limit_str {
223 pairs.push(("limit", s));
224 }
225 self.signed_get("/fapi/v3/forceOrders", &pairs).await
226 }
227
228 pub async fn get_commission_rate(
230 &self,
231 symbol: &str,
232 ) -> Result<ApiResponse<CommissionRateResponse>, AsterDexError> {
233 self.signed_get("/fapi/v3/commissionRate", &[("symbol", symbol)]).await
234 }
235
236 pub async fn get_multi_assets_margin(
238 &self,
239 ) -> Result<ApiResponse<MultiAssetsModeResponse>, AsterDexError> {
240 self.signed_get("/fapi/v3/multiAssetsMargin", &[]).await
241 }
242
243 pub async fn set_multi_assets_margin(
245 &self,
246 enabled: bool,
247 ) -> Result<ApiResponse<StatusMsgResponse>, AsterDexError> {
248 let val = if enabled { "true" } else { "false" };
249 self.signed_post("/fapi/v3/multiAssetsMargin", &[("multiAssetsMargin", val)]).await
250 }
251
252 pub async fn countdown_cancel_all(
254 &self,
255 params: CountdownParams,
256 ) -> Result<ApiResponse<CountdownResponse>, AsterDexError> {
257 let ct_str = params.countdown_time.to_string();
258 self.signed_post(
259 "/fapi/v3/countdownCancelAll",
260 &[("symbol", params.symbol.as_str()), ("countdownTime", &ct_str)],
261 )
262 .await
263 }
264
265 pub async fn noop(&self) -> Result<ApiResponse<EmptyResponse>, AsterDexError> {
267 self.signed_post("/fapi/v3/noop", &[]).await
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::futures::models::trading::{AllOrdersParams, GetOrderParams};
275 use mockito::Server;
276
277 #[tokio::test]
279 async fn get_order_without_credentials_returns_config_error() {
280 let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
281 let params = GetOrderParams {
282 symbol: "BTCUSDT".to_string(),
283 order_id: Some(123456),
284 orig_client_order_id: None,
285 };
286 let result = client.get_order(params).await;
287 assert!(
288 matches!(result, Err(AsterDexError::ConfigError { .. })),
289 "expected ConfigError, got: {:?}",
290 result
291 );
292 }
293
294 #[tokio::test]
296 async fn get_all_orders_without_credentials_returns_config_error() {
297 let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
298 let params = AllOrdersParams {
299 symbol: "BTCUSDT".to_string(),
300 limit: Some(100),
301 ..Default::default()
302 };
303 let result = client.get_all_orders(params).await;
304 assert!(
305 matches!(result, Err(AsterDexError::ConfigError { .. })),
306 "expected ConfigError, got: {:?}",
307 result
308 );
309 }
310
311 #[tokio::test]
313 async fn noop_without_credentials_returns_config_error() {
314 let client = RestClient::new_public("https://fapi.asterdex.com").unwrap();
315 let result = client.noop().await;
316 assert!(
317 matches!(result, Err(AsterDexError::ConfigError { .. })),
318 "expected ConfigError, got: {:?}",
319 result
320 );
321 }
322
323 #[tokio::test]
325 async fn get_all_orders_mock() {
326 let mut server = Server::new_async().await;
327 let mock = server
328 .mock("GET", mockito::Matcher::Regex(r"^/fapi/v3/allOrders".to_string()))
329 .with_status(200)
330 .with_header("content-type", "application/json")
331 .with_body("[]")
332 .create_async()
333 .await;
334
335 use crate::auth::Credentials;
338 let creds = Credentials::new(
339 "0x0000000000000000000000000000000000000001",
340 "0x0000000000000000000000000000000000000001",
341 "0x0000000000000000000000000000000000000000000000000000000000000001",
342 714,
343 )
344 .unwrap();
345 let client = RestClient::new(&server.url(), creds).unwrap();
346
347 let params = AllOrdersParams {
348 symbol: "BTCUSDT".to_string(),
349 ..Default::default()
350 };
351 let resp = client.get_all_orders(params).await.unwrap();
352 assert!(resp.data.is_empty(), "expected empty vec, got {:?}", resp.data);
353
354 mock.assert_async().await;
355 }
356}