Skip to main content

asterdex_sdk/futures/endpoints/
batch.rs

1// US-009: Batch order endpoints
2
3use crate::futures::models::account::{BatchOrderResult, CancelAllResponse};
4use crate::futures::models::trading::PlaceOrderParams;
5use crate::rest::client::RestClient;
6use crate::rest::error::AsterDexError;
7use crate::rest::response::ApiResponse;
8
9const BATCH_ORDER_MAX: usize = 5;
10
11impl RestClient {
12    /// Place multiple orders (max 5).
13    /// Returns a mixed array — check each item for order_id (success) or code/msg (error).
14    pub async fn place_batch_orders(
15        &self,
16        orders: Vec<PlaceOrderParams>,
17    ) -> Result<ApiResponse<Vec<BatchOrderResult>>, AsterDexError> {
18        if orders.len() > BATCH_ORDER_MAX {
19            return Err(AsterDexError::InvalidParams {
20                message: format!("Too many orders: max {BATCH_ORDER_MAX}, got {}", orders.len()),
21            });
22        }
23        // Serialize orders to a JSON string and pass as a signed query param —
24        // the exchange (Binance-style) includes batchOrders in the EIP-712 signature.
25        let orders_json = serde_json::to_string(&orders).map_err(|e| AsterDexError::SerdeError {
26            message: e.to_string(),
27        })?;
28        self.signed_post("/fapi/v3/batchOrders", &[("batchOrders", &orders_json)]).await
29    }
30
31    /// Cancel multiple orders by ID list.
32    pub async fn cancel_batch_orders(
33        &self,
34        symbol: &str,
35        order_ids: Vec<i64>,
36    ) -> Result<ApiResponse<Vec<BatchOrderResult>>, AsterDexError> {
37        let ids_json = serde_json::to_string(&order_ids).map_err(|e| AsterDexError::SerdeError {
38            message: e.to_string(),
39        })?;
40        self.signed_delete("/fapi/v3/batchOrders", &[
41            ("symbol", symbol),
42            ("orderIdList", &ids_json),
43        ]).await
44    }
45
46    /// Cancel all open orders for a symbol.
47    pub async fn cancel_all_open_orders(
48        &self,
49        symbol: &str,
50    ) -> Result<ApiResponse<CancelAllResponse>, AsterDexError> {
51        self.signed_delete("/fapi/v3/allOpenOrders", &[("symbol", symbol)]).await
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::auth::Credentials;
59
60    // US-009 test 1: place_batch_orders with 6 orders returns InvalidParams before network call
61    // (formerly ApiError -1111; reclassified — this is a client-side validation failure,
62    // not an exchange-originated error)
63    #[tokio::test]
64    async fn place_batch_orders_too_many_returns_invalid_params() {
65        let client = RestClient::new_public("http://localhost:9999").unwrap();
66        let orders: Vec<PlaceOrderParams> = (0..6).map(|_| PlaceOrderParams::default()).collect();
67        let result = client.place_batch_orders(orders).await;
68        assert!(
69            matches!(result, Err(AsterDexError::InvalidParams { .. })),
70            "expected InvalidParams, got: {:?}",
71            result
72        );
73    }
74
75    // US-009 test 2: cancel_all_open_orders with mock returns code == 200
76    #[tokio::test]
77    async fn cancel_all_open_orders_mock() {
78        let mut server = mockito::Server::new_async().await;
79        let _mock = server
80            .mock("DELETE", "/fapi/v3/allOpenOrders")
81            .match_query(mockito::Matcher::Any)
82            .with_status(200)
83            .with_header("content-type", "application/json")
84            .with_body(r#"{"code":200,"msg":"The operation of cancel orders is done."}"#)
85            .create_async()
86            .await;
87
88        let creds = Credentials::new(
89            "0x0000000000000000000000000000000000000001",
90            "0x0000000000000000000000000000000000000002",
91            "0000000000000000000000000000000000000000000000000000000000000001",
92            714,
93        )
94        .unwrap();
95        let client = RestClient::new(&server.url(), creds).unwrap();
96        let resp = client.cancel_all_open_orders("BTCUSDT").await.unwrap();
97        assert_eq!(resp.data.code, 200);
98    }
99}