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}