binance/
account.rs

1use crate::client::*;
2use crate::errors::*;
3use crate::rest_model::*;
4use crate::util::*;
5
6static API_V3_ACCOUNT: &str = "/api/v3/account";
7static API_V3_OPEN_ORDERS: &str = "/api/v3/openOrders";
8static API_V3_ALL_ORDERS: &str = "/api/v3/allOrders";
9static API_V3_MYTRADES: &str = "/api/v3/myTrades";
10static API_V3_ORDER: &str = "/api/v3/order";
11static API_V3_CANCEL_REPLACE: &str = "/api/v3/order/cancelReplace";
12/// Endpoint for test orders.
13/// Orders issued to this endpoint are validated, but not sent into the matching engine.
14static API_V3_ORDER_TEST: &str = "/api/v3/order/test";
15
16/// Account API access, full example provided in examples/binance_endpoints.rs
17#[derive(Clone)]
18pub struct Account {
19    pub client: Client,
20    pub recv_window: u64,
21}
22
23/// Order Request
24/// perform an order for the account
25#[derive(Default, Debug, Serialize, Deserialize, Clone)]
26#[serde(rename_all = "camelCase")]
27pub struct OrderRequest {
28    pub symbol: String,
29    pub side: OrderSide,
30    #[serde(rename = "type")]
31    pub order_type: OrderType,
32    pub time_in_force: Option<TimeInForce>,
33    pub quantity: Option<f64>,
34    pub quote_order_qty: Option<f64>,
35    pub price: Option<f64>,
36    /// A unique id for the order, automatically generated if not sent.
37    pub new_client_order_id: Option<String>,
38    /// Used with stop loss, stop loss limit, take profit and take profit limit order types.
39    pub stop_price: Option<f64>,
40    /// Used with limit, stop loss limit and take profit limit to create an iceberg order.
41    pub iceberg_qty: Option<f64>,
42    /// Set the response json, market and limit default to full others to ack.
43    pub new_order_resp_type: Option<OrderResponse>,
44    /// Cannot be greater than 60000
45    pub recv_window: Option<u64>,
46}
47
48impl OrderRequest {
49    fn valid(&self) -> Result<()> {
50        if self.iceberg_qty.is_some() && self.time_in_force != Some(TimeInForce::GTC) {
51            return Err(Error::InvalidOrderError {
52                msg: "Time in force has to be GTC for iceberg orders".to_string(),
53            });
54        }
55        Ok(())
56    }
57}
58
59/// Order Cancellation Request
60/// perform an order cancellation for the account
61/// only works if the parameters match an active order
62/// either order_id (binance side id) or orig_client_order_id (id originally given by the client) must be set
63#[derive(Default, Debug, Serialize, Deserialize, Clone)]
64#[serde(rename_all = "camelCase")]
65pub struct OrderCancellation {
66    pub symbol: String,
67    pub order_id: Option<u64>,
68    pub orig_client_order_id: Option<String>,
69    /// Used to uniquely identify this cancel. Automatically generated by default.
70    pub new_client_order_id: Option<String>,
71    /// Cannot be greater than 60000
72    pub recv_window: Option<u64>,
73}
74
75/// Order Cancellation and Replace Request
76/// Cancels an existing order and places a new order on the same symbol.
77#[derive(Default, Debug, Serialize, Deserialize, Clone)]
78#[serde(rename_all = "camelCase")]
79pub struct CancelReplaceRequest {
80    pub symbol: String,
81    pub side: OrderSide,
82    #[serde(rename = "type")]
83    pub order_type: OrderType,
84    pub cancel_replace_mode: CancelReplaceMode,
85    pub time_in_force: Option<TimeInForce>,
86    pub quantity: Option<f64>,
87    pub quote_order_qty: Option<f64>,
88    pub price: Option<f64>,
89    pub cancel_new_client_order_id: Option<String>,
90    pub cancel_orig_client_order_id: Option<String>,
91    pub cancel_order_id: Option<u64>,
92    pub new_client_order_id: Option<String>,
93    pub stop_price: Option<f64>,
94    pub iceberg_qty: Option<f64>,
95    pub new_order_resp_type: Option<OrderResponse>,
96    /// Cannot be greater than 60000
97    pub recv_window: Option<u64>,
98}
99
100impl CancelReplaceRequest {
101    pub fn valid(&self) -> Result<()> {
102        if self.iceberg_qty.is_some() && self.time_in_force != Some(TimeInForce::GTC) {
103            return Err(Error::InvalidOrderError {
104                msg: "Time in force has to be GTC for iceberg orders".to_string(),
105            });
106        }
107        Ok(())
108    }
109}
110
111/// Order Status Request
112/// perform an order status request for the account
113#[derive(Default, Debug, Serialize, Deserialize, Clone)]
114#[serde(rename_all = "camelCase")]
115pub struct OrderStatusRequest {
116    pub symbol: String,
117    pub order_id: Option<u64>,
118    pub orig_client_order_id: Option<String>,
119    /// Cannot be greater than 60000
120    pub recv_window: Option<u64>,
121}
122
123/// Order Status Request
124/// perform a query on all orders for the account
125#[derive(Default, Debug, Serialize, Deserialize, Clone)]
126#[serde(rename_all = "camelCase")]
127pub struct OrdersQuery {
128    pub symbol: String,
129    pub order_id: Option<u64>,
130    pub start_time: Option<u64>,
131    pub end_time: Option<u64>,
132    /// Default 500 max 1000
133    pub limit: Option<u32>,
134    /// Cannot be greater than 60000
135    pub recv_window: Option<u64>,
136}
137
138impl Account {
139    /// General account information
140    /// # Examples
141    /// ```rust,no_run
142    /// use binance::{api::*, account::*, config::*};
143    /// let account: Account = Binance::new_with_env(&Config::testnet());
144    /// let account = tokio_test::block_on(account.get_account());
145    /// assert!(account.is_ok(), "{:?}", account);
146    /// ```
147    pub async fn get_account(&self) -> Result<AccountInformation> {
148        // TODO: should parameters be Option<>?
149        let request = build_signed_request([("", "")], self.recv_window)?;
150        self.client.get_signed(API_V3_ACCOUNT, &request).await
151    }
152
153    /// Account balance for a single asset
154    /// # Examples
155    /// ```rust,no_run
156    /// use binance::{api::*, account::*, config::*};
157    /// let account: Account = Binance::new_with_env(&Config::testnet());
158    /// let balance = tokio_test::block_on(account.get_balance("BTC"));
159    /// assert!(balance.is_ok(), "{:?}", balance);
160    /// ```
161    pub async fn get_balance<S>(&self, asset: S) -> Result<Balance>
162    where
163        S: Into<String>,
164    {
165        match self.get_account().await {
166            Ok(account) => {
167                let cmp_asset = asset.into();
168                for balance in account.balances {
169                    if balance.asset == cmp_asset {
170                        return Ok(balance);
171                    }
172                }
173                Err(Error::Msg("Asset not found".to_string()))
174            }
175            Err(e) => Err(e),
176        }
177    }
178
179    /// All currently open orders for a single symbol
180    /// # Examples
181    /// ```rust,no_run
182    /// use binance::{api::*, account::*, config::*};
183    /// let account: Account = Binance::new_with_env(&Config::testnet());
184    /// let orders = tokio_test::block_on(account.get_open_orders("BTCUSDT"));
185    /// assert!(orders.is_ok(), "{:?}", orders);
186    /// ```
187    pub async fn get_open_orders<S>(&self, symbol: S) -> Result<Vec<Order>>
188    where
189        S: AsRef<str>,
190    {
191        let parameters = [("symbol", symbol.as_ref())];
192        let request = build_signed_request(parameters, self.recv_window)?;
193        self.client.get_signed(API_V3_OPEN_ORDERS, &request).await
194    }
195
196    /// All orders for the account
197    /// # Examples
198    /// ```rust,no_run
199    /// use binance::{api::*, account::*, config::*};
200    /// let account: Account = Binance::new_with_env(&Config::testnet());
201    /// let query = OrdersQuery {
202    ///     symbol: "BTCUSDT".to_string(),
203    ///     order_id: None,
204    ///     start_time: None,
205    ///     end_time: None,
206    ///     limit: None,
207    ///     recv_window: None,
208    /// };
209    /// let orders = tokio_test::block_on(account.get_all_orders(query));
210    /// assert!(orders.is_ok(), "{:?}", orders);
211    /// ```
212    pub async fn get_all_orders(&self, query: OrdersQuery) -> Result<Vec<Order>> {
213        let recv_window = query.recv_window.unwrap_or(self.recv_window);
214        let request = build_signed_request_p(query, recv_window)?;
215        self.client.get_signed(API_V3_ALL_ORDERS, &request).await
216    }
217
218    /// All currently open orders for the account
219    /// # Examples
220    /// ```rust,no_run
221    /// use binance::{api::*, account::*, config::*};
222    /// let account: Account = Binance::new_with_env(&Config::testnet());
223    /// let orders = tokio_test::block_on(account.get_all_open_orders());
224    /// assert!(orders.is_ok(), "{:?}", orders);
225    /// ```
226    pub async fn get_all_open_orders(&self) -> Result<Vec<Order>> {
227        let request = build_signed_request([("", "")], self.recv_window)?;
228        self.client.get_signed(API_V3_OPEN_ORDERS, &request).await
229    }
230
231    /// Cancels all currently open orders of specified symbol for the account
232    /// # Examples
233    /// ```rust,no_run
234    /// use binance::{api::*, account::*, config::*};
235    /// let account: Account = Binance::new_with_env(&Config::testnet());
236    /// let canceled_orders = tokio_test::block_on(account.cancel_all_open_orders("ETHBTC"));
237    /// assert!(canceled_orders.is_ok(), "{:?}", canceled_orders);
238    /// ```
239    pub async fn cancel_all_open_orders<S>(&self, symbol: S) -> Result<Vec<Order>>
240    where
241        S: AsRef<str>,
242    {
243        let params = [("symbol", symbol.as_ref())];
244        let request = build_signed_request(params, self.recv_window)?;
245        self.client.delete_signed(API_V3_OPEN_ORDERS, &request).await
246    }
247
248    /// Check an order's status
249    /// # Examples
250    /// ```rust,no_run
251    /// use binance::{api::*, account::*, config::*};
252    /// let account: Account = Binance::new_with_env(&Config::testnet());
253    /// let query = OrderStatusRequest {
254    ///     symbol: "BTCUSDT".to_string(),
255    ///     order_id: Some(1),
256    ///     orig_client_order_id: Some("my_id".to_string()),
257    ///     recv_window: None
258    /// };
259    /// let order = tokio_test::block_on(account.order_status(query));
260    /// assert!(order.is_ok(), "{:?}", order);
261    /// ```
262    pub async fn order_status(&self, osr: OrderStatusRequest) -> Result<Order> {
263        let recv_window = osr.recv_window.unwrap_or(self.recv_window);
264        let request = build_signed_request_p(osr, recv_window)?;
265        self.client.get_signed(API_V3_ORDER, &request).await
266    }
267
268    /// Place a test status order
269    ///
270    /// This order is sandboxed: it is validated, but not sent to the matching engine.
271    /// # Examples
272    /// ```rust,no_run
273    /// use binance::{api::*, account::*, config::*};
274    /// let account: Account = Binance::new_with_env(&Config::testnet());
275    /// let query = OrderStatusRequest {
276    ///     symbol: "BTCUSDT".to_string(),
277    ///     order_id: Some(1),
278    ///     orig_client_order_id: Some("my_id".to_string()),
279    ///     recv_window: None
280    /// };
281    /// let resp = tokio_test::block_on(account.test_order_status(query));
282    /// assert!(resp.is_ok(), "{:?}", resp);
283    /// ```
284    pub async fn test_order_status(&self, osr: OrderStatusRequest) -> Result<TestResponse> {
285        let recv_window = osr.recv_window.unwrap_or(self.recv_window);
286        let request = build_signed_request_p(osr, recv_window)?;
287        self.client.get_signed(API_V3_ORDER_TEST, &request).await
288    }
289
290    /// Place an order
291    /// Returns the Transaction if Ok
292    /// This methods validates the order request before sending, making sure it complies with Binance rules
293    /// # Examples
294    /// ```rust,no_run
295    /// use binance::{api::*, account::*, config::*, rest_model::*};
296    /// let account: Account = Binance::new_with_env(&Config::testnet());
297    /// let limit_buy = OrderRequest {
298    ///         symbol: "BTCUSDT".to_string(),
299    ///         quantity: Some(10.0),
300    ///         price: Some(0.014000),
301    ///         order_type: OrderType::Limit,
302    ///         side: OrderSide::Buy,
303    ///         time_in_force: Some(TimeInForce::FOK),
304    ///         ..OrderRequest::default()
305    ///     };
306    /// let transaction = tokio_test::block_on(account.place_order(limit_buy));
307    /// assert!(transaction.is_ok(), "{:?}", transaction);
308    /// ```
309    pub async fn place_order(&self, order: OrderRequest) -> Result<Transaction> {
310        order.valid()?;
311        let recv_window = order.recv_window.unwrap_or(self.recv_window);
312        let request = build_signed_request_p(order, recv_window)?;
313        self.client.post_signed(API_V3_ORDER, &request).await
314    }
315
316    /// Place a test order
317    ///
318    /// Despite being a test, this order is still validated before calls
319    /// This order is sandboxed: it is validated, but not sent to the matching engine.
320    /// # Examples
321    /// ```rust,no_run
322    /// use binance::{api::*, account::*, config::*, rest_model::*};
323    /// let account: Account = Binance::new_with_env(&Config::testnet());
324    /// let limit_buy = OrderRequest {
325    ///         symbol: "BTCUSDT".to_string(),
326    ///         quantity: Some(10.0),
327    ///         price: Some(0.014000),
328    ///         order_type: OrderType::Limit,
329    ///         side: OrderSide::Buy,
330    ///         time_in_force: Some(TimeInForce::FOK),
331    ///         ..OrderRequest::default()
332    ///     };
333    /// let resp = tokio_test::block_on(account.place_test_order(limit_buy));
334    /// assert!(resp.is_ok(), "{:?}", resp);
335    /// ```
336    pub async fn place_test_order(&self, order: OrderRequest) -> Result<TestResponse> {
337        order.valid()?;
338        let recv_window = order.recv_window.unwrap_or(self.recv_window);
339        let request = build_signed_request_p(order, recv_window)?;
340        self.client.post_signed(API_V3_ORDER_TEST, &request).await
341    }
342
343    /// Place a cancellation order
344    /// # Examples
345    /// ```rust,no_run
346    /// use binance::{api::*, account::*, config::*};
347    /// let account: Account = Binance::new_with_env(&Config::testnet());
348    /// let query = OrderCancellation {
349    ///     symbol: "BTCUSDT".to_string(),
350    ///     order_id: Some(1),
351    ///     orig_client_order_id: Some("my_id".to_string()),
352    ///     new_client_order_id: None,
353    ///     recv_window: None
354    /// };
355    /// let canceled = tokio_test::block_on(account.cancel_order(query));
356    /// assert!(canceled.is_ok(), "{:?}", canceled);
357    /// ```
358    pub async fn cancel_order(&self, o: OrderCancellation) -> Result<OrderCanceled> {
359        let recv_window = o.recv_window.unwrap_or(self.recv_window);
360        let request = build_signed_request_p(o, recv_window)?;
361        self.client.delete_signed(API_V3_ORDER, &request).await
362    }
363
364    pub async fn cancel_replace_order(&self, order: CancelReplaceRequest) -> Result<OrderCanceledReplaced> {
365        order.valid()?;
366        let recv_window = order.recv_window.unwrap_or(self.recv_window);
367        let request = build_signed_request_p(order, recv_window)?;
368        self.client.post_signed(API_V3_CANCEL_REPLACE, &request).await
369    }
370
371    /// Place a test cancel order
372    ///
373    /// This order is sandboxed: it is validated, but not sent to the matching engine.
374    /// # Examples
375    /// ```rust,no_run
376    /// use binance::{api::*, account::*, config::*};
377    /// let account: Account = Binance::new_with_env(&Config::testnet());
378    /// let query = OrderCancellation {
379    ///     symbol: "BTCUSDT".to_string(),
380    ///     order_id: Some(1),
381    ///     orig_client_order_id: Some("my_id".to_string()),
382    ///     new_client_order_id: None,
383    ///     recv_window: None
384    /// };
385    /// let response = tokio_test::block_on(account.test_cancel_order(query));
386    /// assert!(response.is_ok(), "{:?}", response);
387    /// ```
388    pub async fn test_cancel_order(&self, o: OrderCancellation) -> Result<TestResponse> {
389        let recv_window = o.recv_window.unwrap_or(self.recv_window);
390        let request = build_signed_request_p(o, recv_window)?;
391        self.client.delete_signed(API_V3_ORDER_TEST, &request).await
392    }
393
394    /// Trade history
395    /// # Examples
396    /// ```rust,no_run
397    /// use binance::{api::*, account::*, config::*};
398    /// let account: Account = Binance::new_with_env(&Config::testnet());
399    /// let trade_history = tokio_test::block_on(account.trade_history("BTCUSDT"));
400    /// assert!(trade_history.is_ok(), "{:?}", trade_history);
401    /// ```
402    pub async fn trade_history<S>(&self, symbol: S) -> Result<Vec<TradeHistory>>
403    where
404        S: AsRef<str>,
405    {
406        let parameters = [("symbol", symbol.as_ref())];
407        let request = build_signed_request(parameters, self.recv_window)?;
408        self.client.get_signed(API_V3_MYTRADES, &request).await
409    }
410}