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