From 06df6967d4bb321b600a0468722ba6d51d448039 Mon Sep 17 00:00:00 2001 From: ulibte Date: Sat, 8 Apr 2023 05:39:55 +0000 Subject: [PATCH 01/17] creating send_quote_request for convert --- Cargo.toml | 1 - src/account.rs | 100 +++++++++++++++++++++++++++++++++++++++++ src/api.rs | 8 ++++ src/lib.rs | 2 + tests/account_tests.rs | 5 +++ tests/util_tests.rs | 6 +++ 6 files changed, 121 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d1acbeb1..820c4bc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ mockito = "0.31.0" env_logger = "0.9.0" criterion = "0.3" float-cmp = "0.9.0" -serde_json = "1.0" [[bench]] name = "websocket_benchmark" diff --git a/src/account.rs b/src/account.rs index 93ec116f..e4b06039 100644 --- a/src/account.rs +++ b/src/account.rs @@ -37,6 +37,34 @@ struct OrderQuoteQuantityRequest { pub time_in_force: TimeInForce, pub new_client_order_id: Option, } +pub enum WalletType { + SPOT, + FUNDING, +} + +pub enum ValidTime { + TenSeconds, + ThirtySeconds, + OneMinutes, + TwoMinutes, +} + +pub enum QtyType { + From(f64), + To(f64), +} + +struct OrderQuoteRequestConvert { + pub from_asset: String, + pub to_asset: String, + // When specified, it is the amount you will be debited after the conversion + pub from_amount: Option, + // When specified, it is the amount you will be credited after the conversion + pub to_amount: Option, + pub wallet_type: Option, + // default 10s + pub valid_time: Option, +} pub enum OrderType { Limit, @@ -765,4 +793,76 @@ impl Account { order_parameters } + + fn converter_order_to_btree_map() -> BTreeMap { + todo!() + } + + // função que faz o request pra converter + fn send_quote_request( + &self, symbol_from: S, symbol_to: S, qty: QtyType, + ) -> Result + where + S: Into, + F: Into, + { + // in qty argument, if the enum variant From has value then the variable from_amount will be Option with the qty value inside and the to_amount will be Option<64> with None inside. + let (from_amount, to_amount) = match qty { + QtyType::From(v) => (Some(v), None), + QtyType::To(v) => (None, Some(v)), + }; + + let params = OrderQuoteRequestConvert { + from_asset: symbol_from.into(), + to_asset: symbol_to.into(), + from_amount, + to_amount, + wallet_type: todo!(), + valid_time: todo!(), + }; + /* pub from_asset: String, + pub to_asset: String, + // When specified, it is the amount you will be debited after the conversion + pub from_amount: Option, + // When specified, it is the amount you will be credited after the conversion + pub to_amount: Option, + pub wallet_type: Option, + // default 10s + pub valid_time: Option, */ + let order = self.build_order(params); + let request = build_signed_request(order, self.recv_window)?; + self.client + .post_signed(API::Convert(Convert::QuoteRequest), request) + // TODO + } + + // method que aceita a negociação do convert + fn accept_quote() -> Result { + // TODO + } + + /// # Examples + /// Convert a currency to another. + /// + /// ``` + /// use mockito::mock; + /// use binance::api::Binance; + /// use binance::config::Config; + /// use binance::account::Account; + /// + /// let config = Config::default() + /// .set_rest_api_endpoint(mockito::server_url()) + /// .set_recv_window(1234); + /// let account: Account = Binance::new_with_config(None, None, &config); + /// let answer = account.convert("BTCETH", 1).unwrap(); + /// + /// assert_eq!(10, answer); + /// ``` + pub fn convert(&self, symbol: S, qty: F) -> Result + where + S: Into, + F: Into, + { + return Ok(10); + } } diff --git a/src/api.rs b/src/api.rs index 6e9df21f..8b69c35b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -14,6 +14,7 @@ use crate::savings::Savings; pub enum API { Spot(Spot), Savings(Sapi), + Convert(Convert), Futures(Futures), } @@ -53,6 +54,10 @@ pub enum Sapi { SpotFuturesTransfer, } +pub enum Convert { + QuoteRequest, +} + pub enum Futures { Ping, Time, @@ -127,6 +132,9 @@ impl From for String { Sapi::DepositAddress => "/sapi/v1/capital/deposit/address", Sapi::SpotFuturesTransfer => "/sapi/v1/futures/transfer", }, + API::Convert(route) => match route { + Convert::QuoteRequest => "/sapi/v1/convert/getQuote", + }, API::Futures(route) => match route { Futures::Ping => "/fapi/v1/ping", Futures::Time => "/fapi/v1/time", diff --git a/src/lib.rs b/src/lib.rs index 2b8e4396..02c9f44d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +//! Api in rust that use the binance api + #![deny( unstable_features, unused_must_use, diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 7fce4720..10086e12 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -854,4 +854,9 @@ mod tests { assert!(!history.is_maker); assert!(history.is_best_match); } + + #[test] + fn convert() { + assert!(true); + } } diff --git a/tests/util_tests.rs b/tests/util_tests.rs index cc51fc18..552992f3 100644 --- a/tests/util_tests.rs +++ b/tests/util_tests.rs @@ -59,4 +59,10 @@ mod tests { ulps = 2 )); } + + #[test] + fn test_print() { + println!("bosta"); + assert_eq!(1, 1); + } } From c48e365c54e2668eb5bf9e2a294cc45bcf3485cc Mon Sep 17 00:00:00 2001 From: ulibte Date: Sun, 9 Apr 2023 01:48:02 +0000 Subject: [PATCH 02/17] converter_order_to_btree_map --- src/account.rs | 66 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/account.rs b/src/account.rs index e4b06039..178400f6 100644 --- a/src/account.rs +++ b/src/account.rs @@ -794,13 +794,57 @@ impl Account { order_parameters } - fn converter_order_to_btree_map() -> BTreeMap { - todo!() + fn converter_order_to_btree_map( + &self, order: OrderQuoteRequestConvert, + ) -> BTreeMap { + let mut order_parameters: BTreeMap = BTreeMap::new(); + + order_parameters.insert("fromAsset".into(), order.from_asset.to_string()); + order_parameters.insert("toAsset".into(), order.to_asset.to_string()); + + if let Some(qty) = order.from_amount { + order_parameters.insert("fromAmount".into(), qty.to_string()); + } else { + // i wanted to use the qty from the if let but its not in scope here + order_parameters.insert("toAmount".into(), order.to_asset.to_string()); + } + + if let Some(wallet_type) = order.wallet_type { + match wallet_type { + WalletType::SPOT => { + order_parameters.insert("walletType".into(), "SPOT".to_string()); + } + WalletType::FUNDING => { + order_parameters.insert("walletType".into(), "FUNDING".to_string()); + } + } + } + + if let Some(time) = order.valid_time { + order_parameters.insert("validTime".into(), "FUNDING".to_string()); + match time { + ValidTime::TenSeconds => { + order_parameters.insert("validTime".into(), "10s".to_string()); + } + ValidTime::ThirtySeconds => { + order_parameters.insert("validTime".into(), "30s".to_string()); + } + ValidTime::OneMinutes => { + order_parameters.insert("validTime".into(), "1m".to_string()); + } + ValidTime::TwoMinutes => { + order_parameters.insert("validTime".into(), "2m".to_string()); + } + } + } + + order_parameters } // função que faz o request pra converter fn send_quote_request( - &self, symbol_from: S, symbol_to: S, qty: QtyType, + &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, + valid_time: Option, ) -> Result where S: Into, @@ -817,19 +861,11 @@ impl Account { to_asset: symbol_to.into(), from_amount, to_amount, - wallet_type: todo!(), - valid_time: todo!(), + wallet_type, + valid_time, }; - /* pub from_asset: String, - pub to_asset: String, - // When specified, it is the amount you will be debited after the conversion - pub from_amount: Option, - // When specified, it is the amount you will be credited after the conversion - pub to_amount: Option, - pub wallet_type: Option, - // default 10s - pub valid_time: Option, */ - let order = self.build_order(params); + + let order = self.converter_order_to_btree_map(params); let request = build_signed_request(order, self.recv_window)?; self.client .post_signed(API::Convert(Convert::QuoteRequest), request) From fecedd8d802d6a53b57911ee1c037b8955043c41 Mon Sep 17 00:00:00 2001 From: ulibte Date: Mon, 10 Apr 2023 03:09:49 +0000 Subject: [PATCH 03/17] I finished building the convert api, needs testing --- src/account.rs | 50 +++++++++++++++++++++++++++++++++----------------- src/api.rs | 2 ++ src/model.rs | 24 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/account.rs b/src/account.rs index 178400f6..d358000c 100644 --- a/src/account.rs +++ b/src/account.rs @@ -2,7 +2,8 @@ use error_chain::bail; use crate::util::build_signed_request; use crate::model::{ - AccountInformation, Balance, Empty, Order, OrderCanceled, TradeHistory, Transaction, + AccountInformation, Balance, Empty, Order, OrderCanceled, TradeHistory, Transaction, Quote, + QuoteResponse, }; use crate::client::Client; use crate::errors::Result; @@ -10,6 +11,7 @@ use std::collections::BTreeMap; use std::fmt::Display; use crate::api::API; use crate::api::Spot; +use crate::api::Convert; #[derive(Clone)] pub struct Account { @@ -49,17 +51,17 @@ pub enum ValidTime { TwoMinutes, } +///* "From" When specified, it is the amount you will be debited after the conversion +///* "To" When specified, it is the amount you will be credited after the conversion pub enum QtyType { From(f64), To(f64), } -struct OrderQuoteRequestConvert { +struct OrderQuoteRequest { pub from_asset: String, pub to_asset: String, - // When specified, it is the amount you will be debited after the conversion pub from_amount: Option, - // When specified, it is the amount you will be credited after the conversion pub to_amount: Option, pub wallet_type: Option, // default 10s @@ -794,9 +796,7 @@ impl Account { order_parameters } - fn converter_order_to_btree_map( - &self, order: OrderQuoteRequestConvert, - ) -> BTreeMap { + fn converter_order_to_btree_map(&self, order: OrderQuoteRequest) -> BTreeMap { let mut order_parameters: BTreeMap = BTreeMap::new(); order_parameters.insert("fromAsset".into(), order.from_asset.to_string()); @@ -842,21 +842,20 @@ impl Account { } // função que faz o request pra converter - fn send_quote_request( + fn send_quote_request( &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, valid_time: Option, - ) -> Result + ) -> Result where S: Into, - F: Into, { - // in qty argument, if the enum variant From has value then the variable from_amount will be Option with the qty value inside and the to_amount will be Option<64> with None inside. + // in qty argument, if the enum variant From has any value then the variable from_amount will be Some(qty) and the to_amount will be None. let (from_amount, to_amount) = match qty { QtyType::From(v) => (Some(v), None), QtyType::To(v) => (None, Some(v)), }; - let params = OrderQuoteRequestConvert { + let params = OrderQuoteRequest { from_asset: symbol_from.into(), to_asset: symbol_to.into(), from_amount, @@ -869,12 +868,18 @@ impl Account { let request = build_signed_request(order, self.recv_window)?; self.client .post_signed(API::Convert(Convert::QuoteRequest), request) - // TODO } // method que aceita a negociação do convert - fn accept_quote() -> Result { - // TODO + fn accept_quote(&self, quote: Result) -> Result { + let quote = quote?; + let mut params: BTreeMap = BTreeMap::new(); + + params.insert("quoteId".into(), quote.quote_id.to_string()); + + let request: String = build_signed_request(params, self.recv_window)?; + self.client + .post_signed(API::Convert(Convert::AcceptQuote), request) } /// # Examples @@ -894,11 +899,22 @@ impl Account { /// /// assert_eq!(10, answer); /// ``` - pub fn convert(&self, symbol: S, qty: F) -> Result + pub fn convert(&self, symbol_from: S, symbol_to: S, qty: F) -> Result where S: Into, F: Into, { - return Ok(10); + let qty = qty.into(); + let qty = QtyType::From(qty); + + let quote = self.send_quote_request( + symbol_from, + symbol_to, + qty, + None, + Some(ValidTime::TenSeconds), + ); + + self.accept_quote(quote) } } diff --git a/src/api.rs b/src/api.rs index 8b69c35b..fa56a9c9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -56,6 +56,7 @@ pub enum Sapi { pub enum Convert { QuoteRequest, + AcceptQuote, } pub enum Futures { @@ -134,6 +135,7 @@ impl From for String { }, API::Convert(route) => match route { Convert::QuoteRequest => "/sapi/v1/convert/getQuote", + Convert::AcceptQuote => "/sapi/v1/convert/acceptQuote", }, API::Futures(route) => match route { Futures::Ping => "/fapi/v1/ping", diff --git a/src/model.rs b/src/model.rs index 2ba3030d..e0d13ebd 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1438,3 +1438,27 @@ fn test_account_update_event() { assert_eq!(format!("{:?}", v), res); //let event = from_value::(json).unwrap(); } + +// Quote from the convert api +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Quote { + pub quote_id: u64, + #[serde(with = "string_or_float")] + pub ration: f64, + #[serde(with = "string_or_float")] + pub inverse_ratio: f64, + pub valid_timestamp: u64, + #[serde(with = "string_or_float")] + pub to_amount: f64, + #[serde(with = "string_or_float")] + pub from_amount: f64, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct QuoteResponse { + pub order_id: u64, + pub create_time: u64, + pub order_status: String, +} From 98f92d72c16544ba427818f0f2de5bc7d202e22c Mon Sep 17 00:00:00 2001 From: ulibte Date: Tue, 11 Apr 2023 06:30:49 +0000 Subject: [PATCH 04/17] did the convert test --- src/account.rs | 3 +- src/model.rs | 46 ++++++++++++++++++++++++++- tests/account_tests.rs | 35 ++++++++++++++++++-- tests/mocks/account/accept_quote.json | 5 +++ tests/mocks/account/quote.json | 8 +++++ 5 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 tests/mocks/account/accept_quote.json create mode 100644 tests/mocks/account/quote.json diff --git a/src/account.rs b/src/account.rs index d358000c..a075d475 100644 --- a/src/account.rs +++ b/src/account.rs @@ -821,7 +821,6 @@ impl Account { } if let Some(time) = order.valid_time { - order_parameters.insert("validTime".into(), "FUNDING".to_string()); match time { ValidTime::TenSeconds => { order_parameters.insert("validTime".into(), "10s".to_string()); @@ -882,7 +881,7 @@ impl Account { .post_signed(API::Convert(Convert::AcceptQuote), request) } - /// # Examples + /// # Example /// Convert a currency to another. /// /// ``` diff --git a/src/model.rs b/src/model.rs index e0d13ebd..c3324163 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1439,13 +1439,56 @@ fn test_account_update_event() { //let event = from_value::(json).unwrap(); } +// chatgpt code, i just copied +pub(crate) mod string_or_u64 { + use std::fmt; + + use serde::{de, Serializer, Deserialize, Deserializer}; + + pub fn serialize(value: &T, serializer: S) -> Result + where + T: fmt::Display, + S: Serializer, + { + if let Ok(v) = value.to_string().parse::() { + serializer.serialize_u64(v) + } else { + serializer.serialize_str(&value.to_string()) + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrU64 { + String(String), + U64(u64), + } + + match StringOrU64::deserialize(deserializer)? { + StringOrU64::String(s) => { + if s == "INF" { + Ok(u64::MAX) + } else { + s.parse().map_err(de::Error::custom) + } + } + StringOrU64::U64(i) => Ok(i), + } + } +} + // Quote from the convert api #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Quote { + #[serde(with = "string_or_u64")] pub quote_id: u64, #[serde(with = "string_or_float")] - pub ration: f64, + pub ratio: f64, #[serde(with = "string_or_float")] pub inverse_ratio: f64, pub valid_timestamp: u64, @@ -1458,6 +1501,7 @@ pub struct Quote { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct QuoteResponse { + #[serde(with = "string_or_u64")] pub order_id: u64, pub create_time: u64, pub order_status: String, diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 10086e12..c8358af7 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -6,6 +6,7 @@ use binance::model::*; #[cfg(test)] mod tests { use super::*; + use binance::errors::{ResultExt, BinanceContentError}; use mockito::{mock, Matcher}; use float_cmp::*; @@ -856,7 +857,37 @@ mod tests { } #[test] - fn convert() { - assert!(true); + fn test_convert() { + // Set up the first mock server to respond to the quote request + let mock_quote = mock("POST", "/sapi/v1/convert/getQuote") + .with_header("content-type", "application/json;charset=UTF-8") + .match_query(Matcher::Regex("fromAmount=1&fromAsset=BTC&recvWindow=1234×tamp=\\d+&toAsset=ETH&validTime=10s".into())) + .with_body_from_file("tests/mocks/account/quote.json") + .create(); + + // Set up the second mock server to respond to the accept quote request + let mock_accept_quote = mock("POST", "/sapi/v1/convert/acceptQuote") + .with_header("content-type", "application/json;charset=UTF-8") + .match_query(Matcher::Regex("quoteId=\\d+".into())) + .with_body_from_file("tests/mocks/account/accept_quote.json") + .create(); + + // Configure the Binance API client with the mock server's URL + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()) + .set_recv_window(1234); + + // Create a new Binance API client using the mock server's URL + let account: Account = Binance::new_with_config(None, None, &config); + let _ = env_logger::try_init(); + + // Call the convert function and assert that the returned QuoteResponse object matches the expected values + let convert_response = account.convert("BTC", "ETH", 1.0).unwrap(); + + assert_eq!(convert_response.order_status, "PROCESS"); + + // Assert that the mock servers were called as expected + mock_quote.assert(); + mock_accept_quote.assert(); } } diff --git a/tests/mocks/account/accept_quote.json b/tests/mocks/account/accept_quote.json new file mode 100644 index 00000000..4094b051 --- /dev/null +++ b/tests/mocks/account/accept_quote.json @@ -0,0 +1,5 @@ +{ + "orderId": "933256278426274426", + "createTime": 1623381330472, + "orderStatus": "PROCESS" +} \ No newline at end of file diff --git a/tests/mocks/account/quote.json b/tests/mocks/account/quote.json new file mode 100644 index 00000000..52e89354 --- /dev/null +++ b/tests/mocks/account/quote.json @@ -0,0 +1,8 @@ +{ + "quoteId": "12415572564", + "ratio": "38163.7", + "inverseRatio": "0.0000262", + "validTimestamp": 1623319461670, + "toAmount": "0.3", + "fromAmount": "1" +} \ No newline at end of file From 9b597a35588ec0902f8b5b57581b85e091576766 Mon Sep 17 00:00:00 2001 From: ulibte Date: Tue, 11 Apr 2023 06:55:15 +0000 Subject: [PATCH 05/17] qty from convert() now accepts QtyType --- src/account.rs | 22 +++++++++++----------- tests/account_tests.rs | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/account.rs b/src/account.rs index a075d475..0a407903 100644 --- a/src/account.rs +++ b/src/account.rs @@ -53,9 +53,9 @@ pub enum ValidTime { ///* "From" When specified, it is the amount you will be debited after the conversion ///* "To" When specified, it is the amount you will be credited after the conversion -pub enum QtyType { - From(f64), - To(f64), +pub enum QtyType> { + From(T), + To(T), } struct OrderQuoteRequest { @@ -841,17 +841,18 @@ impl Account { } // função que faz o request pra converter - fn send_quote_request( - &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, + fn send_quote_request( + &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, valid_time: Option, ) -> Result where S: Into, + F: Into, { // in qty argument, if the enum variant From has any value then the variable from_amount will be Some(qty) and the to_amount will be None. let (from_amount, to_amount) = match qty { - QtyType::From(v) => (Some(v), None), - QtyType::To(v) => (None, Some(v)), + QtyType::From(v) => (Some(v.into()), None), + QtyType::To(v) => (None, Some(v.into())), }; let params = OrderQuoteRequest { @@ -898,14 +899,13 @@ impl Account { /// /// assert_eq!(10, answer); /// ``` - pub fn convert(&self, symbol_from: S, symbol_to: S, qty: F) -> Result + pub fn convert( + &self, symbol_from: S, symbol_to: S, qty: QtyType, + ) -> Result where S: Into, F: Into, { - let qty = qty.into(); - let qty = QtyType::From(qty); - let quote = self.send_quote_request( symbol_from, symbol_to, diff --git a/tests/account_tests.rs b/tests/account_tests.rs index c8358af7..455db4f0 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -6,7 +6,7 @@ use binance::model::*; #[cfg(test)] mod tests { use super::*; - use binance::errors::{ResultExt, BinanceContentError}; + use mockito::{mock, Matcher}; use float_cmp::*; @@ -882,7 +882,7 @@ mod tests { let _ = env_logger::try_init(); // Call the convert function and assert that the returned QuoteResponse object matches the expected values - let convert_response = account.convert("BTC", "ETH", 1.0).unwrap(); + let convert_response = account.convert("BTC", "ETH", QtyType::From(1)).unwrap(); assert_eq!(convert_response.order_status, "PROCESS"); From 25cf54fb2a4ac2a09ef68965f1c18f1a0da258df Mon Sep 17 00:00:00 2001 From: ulibte Date: Tue, 11 Apr 2023 07:35:04 +0000 Subject: [PATCH 06/17] i change the QtyType --- src/account.rs | 34 ++++++++++++++++------------------ tests/account_tests.rs | 1 + 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/account.rs b/src/account.rs index 0a407903..241c30ba 100644 --- a/src/account.rs +++ b/src/account.rs @@ -58,11 +58,10 @@ pub enum QtyType> { To(T), } -struct OrderQuoteRequest { +struct OrderQuoteRequest> { pub from_asset: String, pub to_asset: String, - pub from_amount: Option, - pub to_amount: Option, + pub from_or_to_amount: QtyType, pub wallet_type: Option, // default 10s pub valid_time: Option, @@ -796,18 +795,24 @@ impl Account { order_parameters } - fn converter_order_to_btree_map(&self, order: OrderQuoteRequest) -> BTreeMap { + fn converter_order_to_btree_map>( + &self, order: OrderQuoteRequest, + ) -> BTreeMap { let mut order_parameters: BTreeMap = BTreeMap::new(); order_parameters.insert("fromAsset".into(), order.from_asset.to_string()); order_parameters.insert("toAsset".into(), order.to_asset.to_string()); - if let Some(qty) = order.from_amount { - order_parameters.insert("fromAmount".into(), qty.to_string()); - } else { - // i wanted to use the qty from the if let but its not in scope here - order_parameters.insert("toAmount".into(), order.to_asset.to_string()); - } + match order.from_or_to_amount { + QtyType::From(v) => { + let qty: f64 = v.into(); + order_parameters.insert("fromAmount".into(), qty.to_string()); + } + QtyType::To(v) => { + let qty: f64 = v.into(); + order_parameters.insert("toAmount".into(), qty.to_string()); + } + }; if let Some(wallet_type) = order.wallet_type { match wallet_type { @@ -849,17 +854,10 @@ impl Account { S: Into, F: Into, { - // in qty argument, if the enum variant From has any value then the variable from_amount will be Some(qty) and the to_amount will be None. - let (from_amount, to_amount) = match qty { - QtyType::From(v) => (Some(v.into()), None), - QtyType::To(v) => (None, Some(v.into())), - }; - let params = OrderQuoteRequest { from_asset: symbol_from.into(), to_asset: symbol_to.into(), - from_amount, - to_amount, + from_or_to_amount: qty, wallet_type, valid_time, }; diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 455db4f0..808e2242 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -861,6 +861,7 @@ mod tests { // Set up the first mock server to respond to the quote request let mock_quote = mock("POST", "/sapi/v1/convert/getQuote") .with_header("content-type", "application/json;charset=UTF-8") + // the test only works with this parameters .match_query(Matcher::Regex("fromAmount=1&fromAsset=BTC&recvWindow=1234×tamp=\\d+&toAsset=ETH&validTime=10s".into())) .with_body_from_file("tests/mocks/account/quote.json") .create(); From a4c9c75890a94a6c83ab80cfb411c16502e010e2 Mon Sep 17 00:00:00 2001 From: ulibte Date: Wed, 12 Apr 2023 19:09:57 +0000 Subject: [PATCH 07/17] convert doc --- src/account.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/account.rs b/src/account.rs index 241c30ba..058e2f94 100644 --- a/src/account.rs +++ b/src/account.rs @@ -884,18 +884,11 @@ impl Account { /// Convert a currency to another. /// /// ``` - /// use mockito::mock; - /// use binance::api::Binance; - /// use binance::config::Config; - /// use binance::account::Account; - /// - /// let config = Config::default() - /// .set_rest_api_endpoint(mockito::server_url()) - /// .set_recv_window(1234); /// let account: Account = Binance::new_with_config(None, None, &config); - /// let answer = account.convert("BTCETH", 1).unwrap(); /// - /// assert_eq!(10, answer); + /// // QtyType::From reduces the value of the first symbol in this case "BTC" + /// // QtyType::To reduces the value of the second symbol in this case "USDT" + /// let answer = account.convert("BTC", "USDT", QtyType::From(0.0001)).unwrap(); /// ``` pub fn convert( &self, symbol_from: S, symbol_to: S, qty: QtyType, From 90df8c8b71b6242882cc2924645b6b585286945b Mon Sep 17 00:00:00 2001 From: Felipe Viana <62855944+ulibte@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:48:58 -0300 Subject: [PATCH 08/17] Update account_tests.rs --- tests/account_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 808e2242..7f573348 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -869,7 +869,7 @@ mod tests { // Set up the second mock server to respond to the accept quote request let mock_accept_quote = mock("POST", "/sapi/v1/convert/acceptQuote") .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("quoteId=\\d+".into())) + .match_query(Matcher::Regex("quoteId=12415572564".into())) .with_body_from_file("tests/mocks/account/accept_quote.json") .create(); From b23a4569170c67f1d7652afd5054a7ae4b6679bd Mon Sep 17 00:00:00 2001 From: ulibte Date: Thu, 27 Apr 2023 01:37:19 +0000 Subject: [PATCH 09/17] accept_quote not enough funds --- src/account.rs | 8 +++++- src/model.rs | 78 +++++++++++++++++++++----------------------------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/account.rs b/src/account.rs index 058e2f94..75a867ed 100644 --- a/src/account.rs +++ b/src/account.rs @@ -871,9 +871,15 @@ impl Account { // method que aceita a negociação do convert fn accept_quote(&self, quote: Result) -> Result { let quote = quote?; + + //let quote = quote?; let mut params: BTreeMap = BTreeMap::new(); - params.insert("quoteId".into(), quote.quote_id.to_string()); + if let Some(quote_id) = quote.quote_id { + params.insert("quoteId".into(), quote_id); + } else { + bail!("Not enough funds") + } let request: String = build_signed_request(params, self.recv_window)?; self.client diff --git a/src/model.rs b/src/model.rs index c3324163..672bed8e 100644 --- a/src/model.rs +++ b/src/model.rs @@ -197,6 +197,23 @@ pub struct TransactionId { pub tran_id: u64, } +/* { + "symbol": "BTCUSDT", + "orderId": 28, + "orderListId": -1, //Unless OCO, value will be -1 + "clientOrderId": "6gCrw2kRUAF9CvJDGP16IP", + "transactTime": 1507725176595, + "price": "0.00000000", + "origQty": "10.00000000", + "executedQty": "10.00000000", + "cummulativeQuoteQty": "10.00000000", + "status": "FILLED", + "timeInForce": "GTC", + "type": "MARKET", + "side": "SELL", + "workingTime": 1507725176595, + "selfTradePreventionMode": "NONE" +} */ #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Transaction { @@ -1439,54 +1456,19 @@ fn test_account_update_event() { //let event = from_value::(json).unwrap(); } -// chatgpt code, i just copied -pub(crate) mod string_or_u64 { - use std::fmt; - - use serde::{de, Serializer, Deserialize, Deserializer}; - - pub fn serialize(value: &T, serializer: S) -> Result - where - T: fmt::Display, - S: Serializer, - { - if let Ok(v) = value.to_string().parse::() { - serializer.serialize_u64(v) - } else { - serializer.serialize_str(&value.to_string()) - } - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(untagged)] - enum StringOrU64 { - String(String), - U64(u64), - } - - match StringOrU64::deserialize(deserializer)? { - StringOrU64::String(s) => { - if s == "INF" { - Ok(u64::MAX) - } else { - s.parse().map_err(de::Error::custom) - } - } - StringOrU64::U64(i) => Ok(i), - } - } -} - +/* { + "quoteId":"12415572564", + "ratio":"38163.7", + "inverseRatio":"0.0000262", + "validTimestamp":1623319461670, + "toAmount":"3816.37", + "fromAmount":"0.1" +} */ // Quote from the convert api #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Quote { - #[serde(with = "string_or_u64")] - pub quote_id: u64, + pub quote_id: Option, #[serde(with = "string_or_float")] pub ratio: f64, #[serde(with = "string_or_float")] @@ -1498,11 +1480,15 @@ pub struct Quote { pub from_amount: f64, } +/* { + "orderId":"933256278426274426", + "createTime":1623381330472, + "orderStatus":"PROCESS" //PROCESS/ACCEPT_SUCCESS/SUCCESS/FAIL +} */ #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct QuoteResponse { - #[serde(with = "string_or_u64")] - pub order_id: u64, + pub order_id: String, pub create_time: u64, pub order_status: String, } From 42eebc9062a2e8d83607eeedc8bf842c9edb1d77 Mon Sep 17 00:00:00 2001 From: sulibte Date: Tue, 2 May 2023 02:41:39 +0000 Subject: [PATCH 10/17] daily_account_snapshot --- src/account.rs | 64 +++++++++++++++++-- src/api.rs | 2 + src/model.rs | 25 ++++++++ tests/account_tests.rs | 31 +++++++++ .../mocks/account/daily_account_snapshot.json | 25 ++++++++ 5 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 tests/mocks/account/daily_account_snapshot.json diff --git a/src/account.rs b/src/account.rs index 75a867ed..5123bb95 100644 --- a/src/account.rs +++ b/src/account.rs @@ -3,15 +3,13 @@ use error_chain::bail; use crate::util::build_signed_request; use crate::model::{ AccountInformation, Balance, Empty, Order, OrderCanceled, TradeHistory, Transaction, Quote, - QuoteResponse, + QuoteResponse, AccountSnapshot, }; use crate::client::Client; use crate::errors::Result; use std::collections::BTreeMap; use std::fmt::Display; -use crate::api::API; -use crate::api::Spot; -use crate::api::Convert; +use crate::api::{API, Spot, Convert, Sapi}; #[derive(Clone)] pub struct Account { @@ -67,6 +65,15 @@ struct OrderQuoteRequest> { pub valid_time: Option, } +struct AccountSnapshotRequest { + // "SPOT", "MARGIN", "FUTURES" + pub type_: String, + pub start_time: Option, + pub end_time: Option, + // min 7, max 30, default 7 + pub limit: Option, +} + pub enum OrderType { Limit, Market, @@ -890,7 +897,7 @@ impl Account { /// Convert a currency to another. /// /// ``` - /// let account: Account = Binance::new_with_config(None, None, &config); + /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); /// /// // QtyType::From reduces the value of the first symbol in this case "BTC" /// // QtyType::To reduces the value of the second symbol in this case "USDT" @@ -913,4 +920,51 @@ impl Account { self.accept_quote(quote) } + + fn daily_account_snapshot_to_btree_map( + &self, params: AccountSnapshotRequest, + ) -> BTreeMap { + let mut parameters: BTreeMap = BTreeMap::new(); + + parameters.insert("type".into(), params.type_); + + if let Some(start_time) = params.start_time { + parameters.insert("startTime".into(), start_time.to_string()); + } + + if let Some(end_time) = params.end_time { + parameters.insert("endTime".into(), end_time.to_string()); + } + + if let Some(limit) = params.limit { + parameters.insert("limit".into(), limit.to_string()); + } + + parameters + } + + /// # Example + /// Get the daily account snapshot. + /// + /// ``` + /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); + /// let answer = account.daily_account_snapshot().unwrap(); + /// ``` + pub fn daily_account_snapshot(&self) -> Result { + let params = AccountSnapshotRequest { + type_: "SPOT".to_string(), + start_time: None, + end_time: None, + limit: None, + }; + let btree_params = self.daily_account_snapshot_to_btree_map(params); + + // this gets the timestamp and recv_windows to the btreemap + let request = build_signed_request(btree_params, self.recv_window)?; + + eprintln!("{:#?}", request); + + self.client + .get_signed(API::Savings(Sapi::AccountSnapshot), Some(request)) + } } diff --git a/src/api.rs b/src/api.rs index fa56a9c9..847ca66c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -52,6 +52,7 @@ pub enum Sapi { AssetDetail, DepositAddress, SpotFuturesTransfer, + AccountSnapshot, } pub enum Convert { @@ -132,6 +133,7 @@ impl From for String { Sapi::AssetDetail => "/sapi/v1/asset/assetDetail", Sapi::DepositAddress => "/sapi/v1/capital/deposit/address", Sapi::SpotFuturesTransfer => "/sapi/v1/futures/transfer", + Sapi::AccountSnapshot => "/sapi/v1/accountSnapshot", }, API::Convert(route) => match route { Convert::QuoteRequest => "/sapi/v1/convert/getQuote", diff --git a/src/model.rs b/src/model.rs index 672bed8e..a93fa953 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1492,3 +1492,28 @@ pub struct QuoteResponse { pub create_time: u64, pub order_status: String, } + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AccountSnapshot { + pub code: u16, + // error message + pub msg: String, + pub snapshot_vos: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SnapshotVo { + pub data: Data, + #[serde(rename = "type")] + pub account_type: String, + pub update_time: u64, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Data { + pub balances: Vec, + pub total_asset_of_btc: String, +} diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 7f573348..94f08390 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -891,4 +891,35 @@ mod tests { mock_quote.assert(); mock_accept_quote.assert(); } + + #[test] + fn test_daily_account_snapshot() { + let mock_server: mockito::Mock = mock("GET", "/sapi/v1/accountSnapshot") + .with_header("content-type", "application/json;charset=UTF-8") + // the test only works with this parameters + .match_query(Matcher::Regex( + "recvWindow=1234×tamp=\\d+&type=SPOT".into(), + )) + .with_body_from_file("tests/mocks/account/daily_account_snapshot.json") + .create(); + + // config of mock server's URL + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()) + .set_recv_window(1234); + + // binance client using the mock server's URL + let account: Account = Binance::new_with_config(None, None, &config); + let _ = env_logger::try_init(); + + let convert_response = account.daily_account_snapshot().expect("erro merda"); + + assert_eq!( + convert_response.snapshot_vos[0].data.total_asset_of_btc, + "0.09942700" + ); + + // assert that the mock servers were called + mock_server.assert(); + } } diff --git a/tests/mocks/account/daily_account_snapshot.json b/tests/mocks/account/daily_account_snapshot.json new file mode 100644 index 00000000..3a8dac9e --- /dev/null +++ b/tests/mocks/account/daily_account_snapshot.json @@ -0,0 +1,25 @@ +{ + "code": 200, + "msg": "", + "snapshotVos": [ + { + "data": { + "balances": [ + { + "asset": "BTC", + "free": "0.09905021", + "locked": "0.00000000" + }, + { + "asset": "USDT", + "free": "1.89109409", + "locked": "0.00000000" + } + ], + "totalAssetOfBtc": "0.09942700" + }, + "type": "spot", + "updateTime": 1576281599000 + } + ] +} \ No newline at end of file From 25d7ec054d1cfdeeb9168049c0455a21e6952678 Mon Sep 17 00:00:00 2001 From: Felipe Viana <62855944+ulibte@users.noreply.github.com> Date: Tue, 23 May 2023 15:44:04 -0300 Subject: [PATCH 11/17] Update rust.yml --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c558f369..255a9c89 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: include: - build: msrv os: ubuntu-latest - rust: 1.56.1 + rust: 1.59 - build: stable os: ubuntu-latest rust: stable From 45af3c4157860d2639f252d922ef60c1f3fd15f3 Mon Sep 17 00:00:00 2001 From: sulibte Date: Tue, 23 May 2023 18:51:18 +0000 Subject: [PATCH 12/17] examples --- src/account.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/account.rs b/src/account.rs index 5123bb95..eb69abc1 100644 --- a/src/account.rs +++ b/src/account.rs @@ -896,13 +896,13 @@ impl Account { /// # Example /// Convert a currency to another. /// - /// ``` + /// /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); /// /// // QtyType::From reduces the value of the first symbol in this case "BTC" /// // QtyType::To reduces the value of the second symbol in this case "USDT" /// let answer = account.convert("BTC", "USDT", QtyType::From(0.0001)).unwrap(); - /// ``` + /// pub fn convert( &self, symbol_from: S, symbol_to: S, qty: QtyType, ) -> Result @@ -946,10 +946,10 @@ impl Account { /// # Example /// Get the daily account snapshot. /// - /// ``` + /// /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); /// let answer = account.daily_account_snapshot().unwrap(); - /// ``` + /// pub fn daily_account_snapshot(&self) -> Result { let params = AccountSnapshotRequest { type_: "SPOT".to_string(), From 5dde804e58d53909fcda33179c58ab8d74c89b98 Mon Sep 17 00:00:00 2001 From: Felipe Viana <62855944+ulibte@users.noreply.github.com> Date: Tue, 23 May 2023 15:55:56 -0300 Subject: [PATCH 13/17] Update rust.yml --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 255a9c89..3659a6c7 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: include: - build: msrv os: ubuntu-latest - rust: 1.59 + rust: 1.60.0 - build: stable os: ubuntu-latest rust: stable From 0bf3128baf8386e58d7eb1fb2a6836fe09126614 Mon Sep 17 00:00:00 2001 From: sulibte Date: Tue, 23 May 2023 20:11:06 +0000 Subject: [PATCH 14/17] reqwest update --- Cargo.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 820c4bc1..626917e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,15 +26,18 @@ sha2 = "0.10.6" serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0" error-chain = { version = "0.12.4", default-features = false } -reqwest = { version = "0.11.4", features = ["blocking", "json"] } +reqwest = { version = "0.11.18", features = ["blocking", "json"] } tungstenite = { version = "0.18.0", features = ["native-tls"] } url = "2.2.2" [features] -vendored-tls = ["reqwest/native-tls-vendored", "tungstenite/native-tls-vendored"] +vendored-tls = [ + "reqwest/native-tls-vendored", + "tungstenite/native-tls-vendored", +] [dev-dependencies] -csv ="1.1.6" +csv = "1.1.6" mockito = "0.31.0" env_logger = "0.9.0" criterion = "0.3" From 6e236d477ebb1b95b14082406950c0780dcdd1fb Mon Sep 17 00:00:00 2001 From: sulibte Date: Tue, 23 May 2023 20:15:37 +0000 Subject: [PATCH 15/17] tungstenite update --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 626917e3..bf7c552a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0" error-chain = { version = "0.12.4", default-features = false } reqwest = { version = "0.11.18", features = ["blocking", "json"] } -tungstenite = { version = "0.18.0", features = ["native-tls"] } +tungstenite = { version = "0.19.0", features = ["native-tls"] } url = "2.2.2" [features] From 1bae91f8969f2b9479d72280e3899093b85ba0f9 Mon Sep 17 00:00:00 2001 From: sulibte Date: Fri, 26 May 2023 01:54:54 +0000 Subject: [PATCH 16/17] last --- .rustfmt.toml | 4 - Cargo.toml | 2 +- benches/websocket_benchmark.rs | 30 +- examples/binance_endpoints.rs | 13 +- examples/binance_futures_endpoints.rs | 26 +- examples/binance_futures_userstream.rs | 11 +- examples/binance_futures_websockets.rs | 30 +- examples/binance_save_all_trades.rs | 32 +- examples/binance_websockets.rs | 111 ++++--- src/account.rs | 341 ++++++++++++++----- src/api.rs | 160 ++++++--- src/client.rs | 122 +++++-- src/config.rs | 32 +- src/errors.rs | 2 +- src/futures/account.rs | 251 ++++++++++---- src/futures/general.rs | 6 +- src/futures/market.rs | 67 ++-- src/futures/model.rs | 13 +- src/futures/userstream.rs | 6 +- src/futures/websockets.rs | 150 ++++++--- src/general.rs | 6 +- src/market.rs | 49 ++- src/model.rs | 57 +++- src/savings.rs | 54 +++- src/userstream.rs | 6 +- src/util.rs | 20 +- src/websockets.rs | 93 ++++-- tests/account_tests.rs | 432 ++++++++++++++++++------- tests/futures_account_tests.rs | 44 ++- tests/futures_market_test.rs | 23 +- tests/general_tests.rs | 68 +++- tests/market_tests.rs | 315 +++++++++++++----- tests/util_tests.rs | 19 +- 33 files changed, 1889 insertions(+), 706 deletions(-) delete mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml deleted file mode 100644 index 18a1f7a1..00000000 --- a/.rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -binop_separator = "Back" -fn_args_layout = "Compressed" -newline_style = "Unix" -reorder_imports = false diff --git a/Cargo.toml b/Cargo.toml index bf7c552a..77ccf480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ vendored-tls = [ [dev-dependencies] csv = "1.1.6" -mockito = "0.31.0" +mockito = "0.32.5" env_logger = "0.9.0" criterion = "0.3" float-cmp = "0.9.0" diff --git a/benches/websocket_benchmark.rs b/benches/websocket_benchmark.rs index d0a9df96..bbcacd5e 100644 --- a/benches/websocket_benchmark.rs +++ b/benches/websocket_benchmark.rs @@ -7,27 +7,33 @@ use core::time::Duration; fn criterion_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("websockets-decoder"); - let all_symbols_json = reqwest::blocking::get("https://api.binance.com/api/v3/ticker/price") - .unwrap() - .text() - .unwrap(); - - let btc_symbol_json = - reqwest::blocking::get("https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT") - .unwrap() - .text() - .unwrap(); + let all_symbols_json = reqwest::blocking::get( + "https://api.binance.com/api/v3/ticker/price", + ) + .unwrap() + .text() + .unwrap(); + + let btc_symbol_json = reqwest::blocking::get( + "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT", + ) + .unwrap() + .text() + .unwrap(); let mut web_socket_subscribed: WebSockets<'_> = WebSockets::new(|_event: WebsocketEvent| Ok(())); web_socket_subscribed.connect("!ticker@arr").unwrap(); - let mut web_socket: WebSockets<'_> = WebSockets::new(|_event: WebsocketEvent| Ok(())); + let mut web_socket: WebSockets<'_> = + WebSockets::new(|_event: WebsocketEvent| Ok(())); group.sample_size(200); group.measurement_time(Duration::new(35, 0)); group.bench_function("handle_msg all symbols", |b| { - b.iter(|| web_socket_subscribed.test_handle_msg(&all_symbols_json)); + b.iter(|| { + web_socket_subscribed.test_handle_msg(&all_symbols_json) + }); }); group.bench_function("handle_msg BTCUSDT symbol", |b| { b.iter(|| web_socket.test_handle_msg(&btc_symbol_json)); diff --git a/examples/binance_endpoints.rs b/examples/binance_endpoints.rs index 8ea7bc6d..724f2eb6 100644 --- a/examples/binance_endpoints.rs +++ b/examples/binance_endpoints.rs @@ -1,11 +1,11 @@ +use binance::account::*; use binance::api::*; -use binance::savings::*; use binance::config::*; +use binance::errors::ErrorKind as BinanceLibErrorKind; use binance::general::*; -use binance::account::*; use binance::market::*; use binance::model::KlineSummary; -use binance::errors::ErrorKind as BinanceLibErrorKind; +use binance::savings::*; fn main() { // The general spot API endpoints; shown with @@ -24,7 +24,8 @@ fn main() { fn general(use_testnet: bool) { let general: General = if use_testnet { - let config = Config::default().set_rest_api_endpoint("https://testnet.binance.vision"); + let config = Config::default() + .set_rest_api_endpoint("https://testnet.binance.vision"); Binance::new_with_config(None, None, &config) } else { Binance::new(None, None) @@ -231,7 +232,9 @@ fn market_data() { match market.get_klines("BNBETH", "5m", 10, None, None) { Ok(klines) => { match klines { - binance::model::KlineSummaries::AllKlineSummaries(klines) => { + binance::model::KlineSummaries::AllKlineSummaries( + klines, + ) => { let kline: KlineSummary = klines[0].clone(); // You need to iterate over the klines println!( "Open: {}, High: {}, Low: {}", diff --git a/examples/binance_futures_endpoints.rs b/examples/binance_futures_endpoints.rs index 7cb1969f..bc155530 100644 --- a/examples/binance_futures_endpoints.rs +++ b/examples/binance_futures_endpoints.rs @@ -1,8 +1,8 @@ use binance::api::*; +use binance::errors::ErrorKind as BinanceLibErrorKind; use binance::futures::general::*; use binance::futures::market::*; use binance::futures::model::*; -use binance::errors::ErrorKind as BinanceLibErrorKind; fn main() { general(); @@ -47,22 +47,30 @@ fn market_data() { let market: FuturesMarket = Binance::new(None, None); match market.get_depth("btcusdt") { - Ok(answer) => println!("Depth update ID: {:?}", answer.last_update_id), + Ok(answer) => { + println!("Depth update ID: {:?}", answer.last_update_id) + } Err(e) => println!("Error: {}", e), } match market.get_trades("btcusdt") { - Ok(Trades::AllTrades(answer)) => println!("First trade: {:?}", answer[0]), + Ok(Trades::AllTrades(answer)) => { + println!("First trade: {:?}", answer[0]) + } Err(e) => println!("Error: {}", e), } match market.get_agg_trades("btcusdt", None, None, None, None) { - Ok(AggTrades::AllAggTrades(answer)) => println!("First aggregated trade: {:?}", answer[0]), + Ok(AggTrades::AllAggTrades(answer)) => { + println!("First aggregated trade: {:?}", answer[0]) + } Err(e) => println!("Error: {}", e), } match market.get_klines("btcusdt", "5m", 10, None, None) { - Ok(KlineSummaries::AllKlineSummaries(answer)) => println!("First kline: {:?}", answer[0]), + Ok(KlineSummaries::AllKlineSummaries(answer)) => { + println!("First kline: {:?}", answer[0]) + } Err(e) => println!("Error: {}", e), } @@ -77,7 +85,9 @@ fn market_data() { } match market.get_all_book_tickers() { - Ok(BookTickers::AllBookTickers(answer)) => println!("First book ticker: {:?}", answer[0]), + Ok(BookTickers::AllBookTickers(answer)) => { + println!("First book ticker: {:?}", answer[0]) + } Err(e) => println!("Error: {}", e), } @@ -87,7 +97,9 @@ fn market_data() { } match market.get_mark_prices() { - Ok(MarkPrices::AllMarkPrices(answer)) => println!("First mark Prices: {:?}", answer[0]), + Ok(MarkPrices::AllMarkPrices(answer)) => { + println!("First mark Prices: {:?}", answer[0]) + } Err(e) => println!("Error: {}", e), } diff --git a/examples/binance_futures_userstream.rs b/examples/binance_futures_userstream.rs index 0307638e..bec1d0b5 100644 --- a/examples/binance_futures_userstream.rs +++ b/examples/binance_futures_userstream.rs @@ -7,14 +7,17 @@ fn main() { fn user_stream() { let api_key_user = Some("YOUR_API_KEY".into()); - let user_stream: FuturesUserStream = Binance::new(api_key_user, None); + let user_stream: FuturesUserStream = + Binance::new(api_key_user, None); if let Ok(answer) = user_stream.start() { println!("Data Stream Started ..."); let listen_key = answer.listen_key; match user_stream.keep_alive(&listen_key) { - Ok(msg) => println!("Keepalive user data stream: {:?}", msg), + Ok(msg) => { + println!("Keepalive user data stream: {:?}", msg) + } Err(e) => println!("Error: {}", e), } @@ -23,6 +26,8 @@ fn user_stream() { Err(e) => println!("Error: {}", e), } } else { - println!("Not able to start an User Stream (Check your API_KEY)"); + println!( + "Not able to start an User Stream (Check your API_KEY)" + ); } } diff --git a/examples/binance_futures_websockets.rs b/examples/binance_futures_websockets.rs index 54d08635..f1d4dba3 100755 --- a/examples/binance_futures_websockets.rs +++ b/examples/binance_futures_websockets.rs @@ -12,9 +12,9 @@ fn market_websocket() { let keep_running = AtomicBool::new(true); let stream_examples_usd_m = vec![ // taken from https://binance-docs.github.io/apidocs/futures/en/#websocket-market-streams - "btcusdt@aggTrade", // @aggTrade - "btcusdt@markPrice", // @markPrice OR @markPrice@1s - "btcusdt@kline_1m", // @kline_ + "btcusdt@aggTrade", // @aggTrade + "btcusdt@markPrice", // @markPrice OR @markPrice@1s + "btcusdt@kline_1m", // @kline_ "btcusdt_perpetual@continuousKline_1m", // _@continuousKline_ e.g. "btcusd_next_quarter@continuousKline_1m" "btcusdt@miniTicker", // @miniTicker "!miniTicker@arr", @@ -27,7 +27,7 @@ fn market_websocket() { "btcusdt@forceOrder", // @forceOrder "!forceOrder@arr", "btcusdt@depth20@100ms", // @depth OR @depth@500ms OR @depth@100ms. - "btcusdt@depth@100ms", // @depth OR @depth@500ms OR @depth@100ms + "btcusdt@depth@100ms", // @depth OR @depth@500ms OR @depth@100ms ]; let stream_examples_coin_m = vec![ @@ -36,15 +36,15 @@ fn market_websocket() { // A possible symbol is btcusd_210924. This needs updates if the current date // is greater than 2021-09-24. It'd be nice to make this symbol automatically // generated, or find a that always works. - "btcusd_210924@aggTrade", // @aggTrade - "btcusd@indexPrice@1s", //@indexPrice OR @indexPrice@1s - "btcusd_210924@markPrice", // @markPrice OR @markPrice@1s - "btcusd@markPrice", // @markPrice OR @markPrice@1s - "btcusd_210924@kline_1m", // @kline_ + "btcusd_210924@aggTrade", // @aggTrade + "btcusd@indexPrice@1s", //@indexPrice OR @indexPrice@1s + "btcusd_210924@markPrice", // @markPrice OR @markPrice@1s + "btcusd@markPrice", // @markPrice OR @markPrice@1s + "btcusd_210924@kline_1m", // @kline_ "btcusd_next_quarter@continuousKline_1m", // _@continuousKline_ - "btcusd@indexPriceKline_1m", // @indexPriceKline_ - "btcusd_210924@markPriceKline_1m", // @markPriceKline_ - "btcusd_210924@miniTicker", // @miniTicker + "btcusd@indexPriceKline_1m", // @indexPriceKline_ + "btcusd_210924@markPriceKline_1m", // @markPriceKline_ + "btcusd_210924@miniTicker", // @miniTicker "!miniTicker@arr", "btcusd_210924@ticker", // @ticker "!ticker@arr", @@ -75,7 +75,8 @@ fn market_websocket() { println!("Starting with USD_M {:?}", stream_example); keep_running.swap(true, Ordering::Relaxed); - let mut web_socket: FuturesWebSockets<'_> = FuturesWebSockets::new(callback_fn); + let mut web_socket: FuturesWebSockets<'_> = + FuturesWebSockets::new(callback_fn); web_socket .connect(&FuturesMarket::USDM, stream_example) .unwrap(); @@ -88,7 +89,8 @@ fn market_websocket() { println!("Starting with COIN_M {:?}", stream_example); keep_running.swap(true, Ordering::Relaxed); - let mut web_socket: FuturesWebSockets<'_> = FuturesWebSockets::new(callback_fn); + let mut web_socket: FuturesWebSockets<'_> = + FuturesWebSockets::new(callback_fn); web_socket .connect(&FuturesMarket::COINM, stream_example) .unwrap(); diff --git a/examples/binance_save_all_trades.rs b/examples/binance_save_all_trades.rs index fe711775..5a9f7beb 100644 --- a/examples/binance_save_all_trades.rs +++ b/examples/binance_save_all_trades.rs @@ -1,10 +1,10 @@ +use csv::Writer; use std::error::Error; use std::fs::File; -use csv::Writer; -use std::sync::atomic::{AtomicBool}; +use std::sync::atomic::AtomicBool; +use binance::model::DayTickerEvent; use binance::websockets::*; -use binance::model::{DayTickerEvent}; fn main() { save_all_trades_websocket(); @@ -21,7 +21,10 @@ fn save_all_trades_websocket() { } // serialize DayTickerEvent as CSV records - pub fn write_to_file(&mut self, events: Vec) -> Result<(), Box> { + pub fn write_to_file( + &mut self, + events: Vec, + ) -> Result<(), Box> { for event in events { self.wrt.serialize(event)?; } @@ -35,17 +38,20 @@ fn save_all_trades_websocket() { let mut web_socket_handler = WebSocketHandler::new(local_wrt); let agg_trade = String::from("!ticker@arr"); - let mut web_socket = WebSockets::new(move |event: WebsocketEvent| { - if let WebsocketEvent::DayTickerAll(events) = event { - // You can break the event_loop if some condition is met be setting keep_running to false - // keep_running.store(false, Ordering::Relaxed); - if let Err(error) = web_socket_handler.write_to_file(events) { - println!("{}", error); + let mut web_socket = + WebSockets::new(move |event: WebsocketEvent| { + if let WebsocketEvent::DayTickerAll(events) = event { + // You can break the event_loop if some condition is met be setting keep_running to false + // keep_running.store(false, Ordering::Relaxed); + if let Err(error) = + web_socket_handler.write_to_file(events) + { + println!("{}", error); + } } - } - Ok(()) - }); + Ok(()) + }); web_socket.connect(&agg_trade).unwrap(); // check error if let Err(e) = web_socket.event_loop(&keep_running) { diff --git a/examples/binance_websockets.rs b/examples/binance_websockets.rs index f6cc6296..68491b99 100644 --- a/examples/binance_websockets.rs +++ b/examples/binance_websockets.rs @@ -24,7 +24,9 @@ fn user_stream() { let listen_key = answer.listen_key; match user_stream.keep_alive(&listen_key) { - Ok(msg) => println!("Keepalive user data stream: {:?}", msg), + Ok(msg) => { + println!("Keepalive user data stream: {:?}", msg) + } Err(e) => println!("Error: {}", e), } @@ -33,7 +35,9 @@ fn user_stream() { Err(e) => println!("Error: {}", e), } } else { - println!("Not able to start an User Stream (Check your API_KEY)"); + println!( + "Not able to start an User Stream (Check your API_KEY)" + ); } } @@ -45,30 +49,32 @@ fn user_stream_websocket() { if let Ok(answer) = user_stream.start() { let listen_key = answer.listen_key; - let mut web_socket: WebSockets<'_> = WebSockets::new(|event: WebsocketEvent| { - match event { - WebsocketEvent::AccountUpdate(account_update) => { - for balance in &account_update.data.balances { - println!( + let mut web_socket: WebSockets<'_> = WebSockets::new( + |event: WebsocketEvent| { + match event { + WebsocketEvent::AccountUpdate(account_update) => { + for balance in &account_update.data.balances { + println!( "Asset: {}, wallet_balance: {}, cross_wallet_balance: {}, balance: {}", balance.asset, balance.wallet_balance, balance.cross_wallet_balance, balance.balance_change ); + } } - } - WebsocketEvent::OrderTrade(trade) => { - println!( + WebsocketEvent::OrderTrade(trade) => { + println!( "Symbol: {}, Side: {}, Price: {}, Execution Type: {}", trade.symbol, trade.side, trade.price, trade.execution_type ); - } - _ => (), - }; + } + _ => (), + }; - Ok(()) - }); + Ok(()) + }, + ); web_socket.connect(&listen_key).unwrap(); // check error if let Err(e) = web_socket.event_loop(&keep_running) { @@ -78,38 +84,45 @@ fn user_stream_websocket() { web_socket.disconnect().unwrap(); println!("Userstrem closed and disconnected"); } else { - println!("Not able to start an User Stream (Check your API_KEY)"); + println!( + "Not able to start an User Stream (Check your API_KEY)" + ); } } fn market_websocket() { let keep_running = AtomicBool::new(true); // Used to control the event loop let agg_trade = String::from("ethbtc@aggTrade"); - let mut web_socket: WebSockets<'_> = WebSockets::new(|event: WebsocketEvent| { - match event { - WebsocketEvent::Trade(trade) => { - println!( - "Symbol: {}, price: {}, qty: {}", - trade.symbol, trade.price, trade.qty - ); - } - WebsocketEvent::DepthOrderBook(depth_order_book) => { - println!( - "Symbol: {}, Bids: {:?}, Ask: {:?}", - depth_order_book.symbol, depth_order_book.bids, depth_order_book.asks - ); - } - WebsocketEvent::OrderBook(order_book) => { - println!( - "last_update_id: {}, Bids: {:?}, Ask: {:?}", - order_book.last_update_id, order_book.bids, order_book.asks - ); - } - _ => (), - }; + let mut web_socket: WebSockets<'_> = + WebSockets::new(|event: WebsocketEvent| { + match event { + WebsocketEvent::Trade(trade) => { + println!( + "Symbol: {}, price: {}, qty: {}", + trade.symbol, trade.price, trade.qty + ); + } + WebsocketEvent::DepthOrderBook(depth_order_book) => { + println!( + "Symbol: {}, Bids: {:?}, Ask: {:?}", + depth_order_book.symbol, + depth_order_book.bids, + depth_order_book.asks + ); + } + WebsocketEvent::OrderBook(order_book) => { + println!( + "last_update_id: {}, Bids: {:?}, Ask: {:?}", + order_book.last_update_id, + order_book.bids, + order_book.asks + ); + } + _ => (), + }; - Ok(()) - }); + Ok(()) + }); web_socket.connect(&agg_trade).unwrap(); // check error if let Err(e) = web_socket.event_loop(&keep_running) { @@ -127,7 +140,9 @@ fn all_trades_websocket() { for tick_event in ticker_events { println!( "Symbol: {}, price: {}, qty: {}", - tick_event.symbol, tick_event.best_bid, tick_event.best_bid_qty + tick_event.symbol, + tick_event.best_bid, + tick_event.best_bid_qty ); } } @@ -150,7 +165,9 @@ fn kline_websocket() { if let WebsocketEvent::Kline(kline_event) = event { println!( "Symbol: {}, high: {}, low: {}", - kline_event.kline.symbol, kline_event.kline.low, kline_event.kline.high + kline_event.kline.symbol, + kline_event.kline.low, + kline_event.kline.high ); } @@ -173,7 +190,8 @@ fn last_price_for_one_symbol() { let mut web_socket = WebSockets::new(|event: WebsocketEvent| { if let WebsocketEvent::DayTicker(ticker_event) = event { btcusdt = ticker_event.average_price.parse().unwrap(); - let btcusdt_close: f32 = ticker_event.current_close.parse().unwrap(); + let btcusdt_close: f32 = + ticker_event.current_close.parse().unwrap(); println!("{} - {}", btcusdt, btcusdt_close); if btcusdt_close as i32 == 7000 { @@ -194,12 +212,13 @@ fn last_price_for_one_symbol() { } fn multiple_streams() { - let endpoints = - ["ETHBTC", "BNBETH"].map(|symbol| format!("{}@depth@100ms", symbol.to_lowercase())); + let endpoints = ["ETHBTC", "BNBETH"] + .map(|symbol| format!("{}@depth@100ms", symbol.to_lowercase())); let keep_running = AtomicBool::new(true); let mut web_socket = WebSockets::new(|event: WebsocketEvent| { - if let WebsocketEvent::DepthOrderBook(depth_order_book) = event { + if let WebsocketEvent::DepthOrderBook(depth_order_book) = event + { println!("{:?}", depth_order_book); } diff --git a/src/account.rs b/src/account.rs index eb69abc1..bdda537f 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,15 +1,15 @@ use error_chain::bail; -use crate::util::build_signed_request; -use crate::model::{ - AccountInformation, Balance, Empty, Order, OrderCanceled, TradeHistory, Transaction, Quote, - QuoteResponse, AccountSnapshot, -}; +use crate::api::{Convert, Sapi, Spot, API}; use crate::client::Client; use crate::errors::Result; +use crate::model::{ + AccountInformation, AccountSnapshot, Balance, Empty, Order, + OrderCanceled, Quote, QuoteResponse, TradeHistory, Transaction, +}; +use crate::util::build_signed_request; use std::collections::BTreeMap; use std::fmt::Display; -use crate::api::{API, Spot, Convert, Sapi}; #[derive(Clone)] pub struct Account { @@ -124,7 +124,8 @@ impl Display for TimeInForce { impl Account { // Account Information pub fn get_account(&self) -> Result { - let request = build_signed_request(BTreeMap::new(), self.recv_window)?; + let request = + build_signed_request(BTreeMap::new(), self.recv_window)?; self.client .get_signed(API::Spot(Spot::Account), Some(request)) } @@ -156,7 +157,8 @@ impl Account { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::OpenOrders), Some(request)) } @@ -165,25 +167,34 @@ impl Account { pub fn get_all_open_orders(&self) -> Result> { let parameters: BTreeMap = BTreeMap::new(); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::OpenOrders), Some(request)) } // Cancel all open orders for a single symbol - pub fn cancel_all_open_orders(&self, symbol: S) -> Result> + pub fn cancel_all_open_orders( + &self, + symbol: S, + ) -> Result> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::OpenOrders), Some(request)) } // Check an order's status - pub fn order_status(&self, symbol: S, order_id: u64) -> Result + pub fn order_status( + &self, + symbol: S, + order_id: u64, + ) -> Result where S: Into, { @@ -191,7 +202,8 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::Order), Some(request)) } @@ -199,7 +211,11 @@ impl Account { /// Place a test status order /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_order_status(&self, symbol: S, order_id: u64) -> Result<()> + pub fn test_order_status( + &self, + symbol: S, + order_id: u64, + ) -> Result<()> where S: Into, { @@ -207,14 +223,23 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client - .get_signed::(API::Spot(Spot::OrderTest), Some(request)) + .get_signed::( + API::Spot(Spot::OrderTest), + Some(request), + ) .map(|_| ()) } // Place a LIMIT order - BUY - pub fn limit_buy(&self, symbol: S, qty: F, price: f64) -> Result + pub fn limit_buy( + &self, + symbol: S, + qty: F, + price: f64, + ) -> Result where S: Into, F: Into, @@ -237,7 +262,12 @@ impl Account { /// Place a test limit order - BUY /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_limit_buy(&self, symbol: S, qty: F, price: f64) -> Result<()> + pub fn test_limit_buy( + &self, + symbol: S, + qty: F, + price: f64, + ) -> Result<()> where S: Into, F: Into, @@ -260,7 +290,12 @@ impl Account { } // Place a LIMIT order - SELL - pub fn limit_sell(&self, symbol: S, qty: F, price: f64) -> Result + pub fn limit_sell( + &self, + symbol: S, + qty: F, + price: f64, + ) -> Result where S: Into, F: Into, @@ -283,7 +318,12 @@ impl Account { /// Place a test LIMIT order - SELL /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_limit_sell(&self, symbol: S, qty: F, price: f64) -> Result<()> + pub fn test_limit_sell( + &self, + symbol: S, + qty: F, + price: f64, + ) -> Result<()> where S: Into, F: Into, @@ -306,7 +346,11 @@ impl Account { } // Place a MARKET order - BUY - pub fn market_buy(&self, symbol: S, qty: F) -> Result + pub fn market_buy( + &self, + symbol: S, + qty: F, + ) -> Result where S: Into, F: Into, @@ -353,7 +397,9 @@ impl Account { // Place a MARKET order with quote quantity - BUY pub fn market_buy_using_quote_quantity( - &self, symbol: S, quote_order_qty: F, + &self, + symbol: S, + quote_order_qty: F, ) -> Result where S: Into, @@ -377,7 +423,9 @@ impl Account { /// /// This order is sandboxed: it is validated, but not sent to the matching engine. pub fn test_market_buy_using_quote_quantity( - &self, symbol: S, quote_order_qty: F, + &self, + symbol: S, + quote_order_qty: F, ) -> Result<()> where S: Into, @@ -400,7 +448,11 @@ impl Account { } // Place a MARKET order - SELL - pub fn market_sell(&self, symbol: S, qty: F) -> Result + pub fn market_sell( + &self, + symbol: S, + qty: F, + ) -> Result where S: Into, F: Into, @@ -423,7 +475,11 @@ impl Account { /// Place a test MARKET order - SELL /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_market_sell(&self, symbol: S, qty: F) -> Result<()> + pub fn test_market_sell( + &self, + symbol: S, + qty: F, + ) -> Result<()> where S: Into, F: Into, @@ -447,7 +503,9 @@ impl Account { // Place a MARKET order with quote quantity - SELL pub fn market_sell_using_quote_quantity( - &self, symbol: S, quote_order_qty: F, + &self, + symbol: S, + quote_order_qty: F, ) -> Result where S: Into, @@ -471,7 +529,9 @@ impl Account { /// /// This order is sandboxed: it is validated, but not sent to the matching engine. pub fn test_market_sell_using_quote_quantity( - &self, symbol: S, quote_order_qty: F, + &self, + symbol: S, + quote_order_qty: F, ) -> Result<()> where S: Into, @@ -508,7 +568,12 @@ impl Account { /// } /// ``` pub fn stop_limit_buy_order( - &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: f64, + time_in_force: TimeInForce, ) -> Result where S: Into, @@ -546,7 +611,12 @@ impl Account { /// } /// ``` pub fn test_stop_limit_buy_order( - &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: f64, + time_in_force: TimeInForce, ) -> Result<()> where S: Into, @@ -584,7 +654,12 @@ impl Account { /// } /// ``` pub fn stop_limit_sell_order( - &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: f64, + time_in_force: TimeInForce, ) -> Result where S: Into, @@ -622,7 +697,12 @@ impl Account { /// } /// ``` pub fn test_stop_limit_sell_order( - &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: f64, + time_in_force: TimeInForce, ) -> Result<()> where S: Into, @@ -648,8 +728,15 @@ impl Account { /// Place a custom order #[allow(clippy::too_many_arguments)] pub fn custom_order( - &self, symbol: S, qty: F, price: f64, stop_price: Option, order_side: OrderSide, - order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: Option, + order_side: OrderSide, + order_type: OrderType, + time_in_force: TimeInForce, + new_client_order_id: Option, ) -> Result where S: Into, @@ -675,8 +762,15 @@ impl Account { /// This order is sandboxed: it is validated, but not sent to the matching engine. #[allow(clippy::too_many_arguments)] pub fn test_custom_order( - &self, symbol: S, qty: F, price: f64, stop_price: Option, order_side: OrderSide, - order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option, + &self, + symbol: S, + qty: F, + price: f64, + stop_price: Option, + order_side: OrderSide, + order_type: OrderType, + time_in_force: TimeInForce, + new_client_order_id: Option, ) -> Result<()> where S: Into, @@ -700,7 +794,11 @@ impl Account { } // Check an order's status - pub fn cancel_order(&self, symbol: S, order_id: u64) -> Result + pub fn cancel_order( + &self, + symbol: S, + order_id: u64, + ) -> Result where S: Into, { @@ -708,126 +806,180 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::Order), Some(request)) } pub fn cancel_order_with_client_id( - &self, symbol: S, orig_client_order_id: String, + &self, + symbol: S, + orig_client_order_id: String, ) -> Result where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - parameters.insert("origClientOrderId".into(), orig_client_order_id); + parameters + .insert("origClientOrderId".into(), orig_client_order_id); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::Order), Some(request)) } /// Place a test cancel order /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_cancel_order(&self, symbol: S, order_id: u64) -> Result<()> + pub fn test_cancel_order( + &self, + symbol: S, + order_id: u64, + ) -> Result<()> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client - .delete_signed::(API::Spot(Spot::OrderTest), Some(request)) + .delete_signed::( + API::Spot(Spot::OrderTest), + Some(request), + ) .map(|_| ()) } // Trade history - pub fn trade_history(&self, symbol: S) -> Result> + pub fn trade_history( + &self, + symbol: S, + ) -> Result> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::MyTrades), Some(request)) } - fn build_order(&self, order: OrderRequest) -> BTreeMap { - let mut order_parameters: BTreeMap = BTreeMap::new(); + fn build_order( + &self, + order: OrderRequest, + ) -> BTreeMap { + let mut order_parameters: BTreeMap = + BTreeMap::new(); order_parameters.insert("symbol".into(), order.symbol); - order_parameters.insert("side".into(), order.order_side.to_string()); - order_parameters.insert("type".into(), order.order_type.to_string()); - order_parameters.insert("quantity".into(), order.qty.to_string()); + order_parameters + .insert("side".into(), order.order_side.to_string()); + order_parameters + .insert("type".into(), order.order_type.to_string()); + order_parameters + .insert("quantity".into(), order.qty.to_string()); if let Some(stop_price) = order.stop_price { - order_parameters.insert("stopPrice".into(), stop_price.to_string()); + order_parameters + .insert("stopPrice".into(), stop_price.to_string()); } if order.price != 0.0 { - order_parameters.insert("price".into(), order.price.to_string()); - order_parameters.insert("timeInForce".into(), order.time_in_force.to_string()); + order_parameters + .insert("price".into(), order.price.to_string()); + order_parameters.insert( + "timeInForce".into(), + order.time_in_force.to_string(), + ); } if let Some(client_order_id) = order.new_client_order_id { - order_parameters.insert("newClientOrderId".into(), client_order_id); + order_parameters + .insert("newClientOrderId".into(), client_order_id); } order_parameters } fn build_quote_quantity_order( - &self, order: OrderQuoteQuantityRequest, + &self, + order: OrderQuoteQuantityRequest, ) -> BTreeMap { - let mut order_parameters: BTreeMap = BTreeMap::new(); + let mut order_parameters: BTreeMap = + BTreeMap::new(); order_parameters.insert("symbol".into(), order.symbol); - order_parameters.insert("side".into(), order.order_side.to_string()); - order_parameters.insert("type".into(), order.order_type.to_string()); - order_parameters.insert("quoteOrderQty".into(), order.quote_order_qty.to_string()); + order_parameters + .insert("side".into(), order.order_side.to_string()); + order_parameters + .insert("type".into(), order.order_type.to_string()); + order_parameters.insert( + "quoteOrderQty".into(), + order.quote_order_qty.to_string(), + ); if order.price != 0.0 { - order_parameters.insert("price".into(), order.price.to_string()); - order_parameters.insert("timeInForce".into(), order.time_in_force.to_string()); + order_parameters + .insert("price".into(), order.price.to_string()); + order_parameters.insert( + "timeInForce".into(), + order.time_in_force.to_string(), + ); } if let Some(client_order_id) = order.new_client_order_id { - order_parameters.insert("newClientOrderId".into(), client_order_id); + order_parameters + .insert("newClientOrderId".into(), client_order_id); } order_parameters } fn converter_order_to_btree_map>( - &self, order: OrderQuoteRequest, + &self, + order: OrderQuoteRequest, ) -> BTreeMap { - let mut order_parameters: BTreeMap = BTreeMap::new(); + let mut order_parameters: BTreeMap = + BTreeMap::new(); - order_parameters.insert("fromAsset".into(), order.from_asset.to_string()); - order_parameters.insert("toAsset".into(), order.to_asset.to_string()); + order_parameters + .insert("fromAsset".into(), order.from_asset.to_string()); + order_parameters + .insert("toAsset".into(), order.to_asset.to_string()); match order.from_or_to_amount { QtyType::From(v) => { let qty: f64 = v.into(); - order_parameters.insert("fromAmount".into(), qty.to_string()); + order_parameters + .insert("fromAmount".into(), qty.to_string()); } QtyType::To(v) => { let qty: f64 = v.into(); - order_parameters.insert("toAmount".into(), qty.to_string()); + order_parameters + .insert("toAmount".into(), qty.to_string()); } }; if let Some(wallet_type) = order.wallet_type { match wallet_type { WalletType::SPOT => { - order_parameters.insert("walletType".into(), "SPOT".to_string()); + order_parameters.insert( + "walletType".into(), + "SPOT".to_string(), + ); } WalletType::FUNDING => { - order_parameters.insert("walletType".into(), "FUNDING".to_string()); + order_parameters.insert( + "walletType".into(), + "FUNDING".to_string(), + ); } } } @@ -835,16 +987,20 @@ impl Account { if let Some(time) = order.valid_time { match time { ValidTime::TenSeconds => { - order_parameters.insert("validTime".into(), "10s".to_string()); + order_parameters + .insert("validTime".into(), "10s".to_string()); } ValidTime::ThirtySeconds => { - order_parameters.insert("validTime".into(), "30s".to_string()); + order_parameters + .insert("validTime".into(), "30s".to_string()); } ValidTime::OneMinutes => { - order_parameters.insert("validTime".into(), "1m".to_string()); + order_parameters + .insert("validTime".into(), "1m".to_string()); } ValidTime::TwoMinutes => { - order_parameters.insert("validTime".into(), "2m".to_string()); + order_parameters + .insert("validTime".into(), "2m".to_string()); } } } @@ -854,7 +1010,11 @@ impl Account { // função que faz o request pra converter fn send_quote_request( - &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, + &self, + symbol_from: S, + symbol_to: S, + qty: QtyType, + wallet_type: Option, valid_time: Option, ) -> Result where @@ -876,7 +1036,10 @@ impl Account { } // method que aceita a negociação do convert - fn accept_quote(&self, quote: Result) -> Result { + fn accept_quote( + &self, + quote: Result, + ) -> Result { let quote = quote?; //let quote = quote?; @@ -888,7 +1051,8 @@ impl Account { bail!("Not enough funds") } - let request: String = build_signed_request(params, self.recv_window)?; + let request: String = + build_signed_request(params, self.recv_window)?; self.client .post_signed(API::Convert(Convert::AcceptQuote), request) } @@ -904,7 +1068,10 @@ impl Account { /// let answer = account.convert("BTC", "USDT", QtyType::From(0.0001)).unwrap(); /// pub fn convert( - &self, symbol_from: S, symbol_to: S, qty: QtyType, + &self, + symbol_from: S, + symbol_to: S, + qty: QtyType, ) -> Result where S: Into, @@ -922,14 +1089,16 @@ impl Account { } fn daily_account_snapshot_to_btree_map( - &self, params: AccountSnapshotRequest, + &self, + params: AccountSnapshotRequest, ) -> BTreeMap { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("type".into(), params.type_); if let Some(start_time) = params.start_time { - parameters.insert("startTime".into(), start_time.to_string()); + parameters + .insert("startTime".into(), start_time.to_string()); } if let Some(end_time) = params.end_time { @@ -957,14 +1126,18 @@ impl Account { end_time: None, limit: None, }; - let btree_params = self.daily_account_snapshot_to_btree_map(params); + let btree_params = + self.daily_account_snapshot_to_btree_map(params); // this gets the timestamp and recv_windows to the btreemap - let request = build_signed_request(btree_params, self.recv_window)?; + let request = + build_signed_request(btree_params, self.recv_window)?; eprintln!("{:#?}", request); - self.client - .get_signed(API::Savings(Sapi::AccountSnapshot), Some(request)) + self.client.get_signed( + API::Savings(Sapi::AccountSnapshot), + Some(request), + ) } } diff --git a/src/api.rs b/src/api.rs index 847ca66c..8ac3160c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -7,8 +7,8 @@ use crate::futures::market::FuturesMarket; use crate::futures::userstream::FuturesUserStream; use crate::general::General; use crate::market::Market; -use crate::userstream::UserStream; use crate::savings::Savings; +use crate::userstream::UserStream; #[allow(clippy::all)] pub enum API { @@ -131,8 +131,12 @@ impl From for String { API::Savings(route) => match route { Sapi::AllCoins => "/sapi/v1/capital/config/getall", Sapi::AssetDetail => "/sapi/v1/asset/assetDetail", - Sapi::DepositAddress => "/sapi/v1/capital/deposit/address", - Sapi::SpotFuturesTransfer => "/sapi/v1/futures/transfer", + Sapi::DepositAddress => { + "/sapi/v1/capital/deposit/address" + } + Sapi::SpotFuturesTransfer => { + "/sapi/v1/futures/transfer" + } Sapi::AccountSnapshot => "/sapi/v1/accountSnapshot", }, API::Convert(route) => match route { @@ -145,11 +149,17 @@ impl From for String { Futures::ExchangeInfo => "/fapi/v1/exchangeInfo", Futures::Depth => "/fapi/v1/depth", Futures::Trades => "/fapi/v1/trades", - Futures::HistoricalTrades => "/fapi/v1/historicalTrades", + Futures::HistoricalTrades => { + "/fapi/v1/historicalTrades" + } Futures::AggTrades => "/fapi/v1/aggTrades", Futures::Klines => "/fapi/v1/klines", - Futures::ContinuousKlines => "/fapi/v1/continuousKlines", - Futures::IndexPriceKlines => "/fapi/v1/indexPriceKlines", + Futures::ContinuousKlines => { + "/fapi/v1/continuousKlines" + } + Futures::IndexPriceKlines => { + "/fapi/v1/indexPriceKlines" + } Futures::MarkPriceKlines => "/fapi/v1/markPriceKlines", Futures::PremiumIndex => "/fapi/v1/premiumIndex", Futures::FundingRate => "/fapi/v1/fundingRate", @@ -165,11 +175,21 @@ impl From for String { Futures::PositionRisk => "/fapi/v2/positionRisk", Futures::Balance => "/fapi/v2/balance", Futures::OpenInterest => "/fapi/v1/openInterest", - Futures::OpenInterestHist => "/futures/data/openInterestHist", - Futures::TopLongShortAccountRatio => "/futures/data/topLongShortAccountRatio", - Futures::TopLongShortPositionRatio => "/futures/data/topLongShortPositionRatio", - Futures::GlobalLongShortAccountRatio => "/futures/data/globalLongShortAccountRatio", - Futures::TakerlongshortRatio => "/futures/data/takerlongshortRatio", + Futures::OpenInterestHist => { + "/futures/data/openInterestHist" + } + Futures::TopLongShortAccountRatio => { + "/futures/data/topLongShortAccountRatio" + } + Futures::TopLongShortPositionRatio => { + "/futures/data/topLongShortPositionRatio" + } + Futures::GlobalLongShortAccountRatio => { + "/futures/data/globalLongShortAccountRatio" + } + Futures::TakerlongshortRatio => { + "/futures/data/takerlongshortRatio" + } Futures::LvtKlines => "/fapi/v1/lvtKlines", Futures::IndexInfo => "/fapi/v1/indexInfo", Futures::ChangeInitialLeverage => "/fapi/v1/leverage", @@ -183,81 +203,129 @@ impl From for String { } pub trait Binance { - fn new(api_key: Option, secret_key: Option) -> Self; + fn new(api_key: Option, secret_key: Option) + -> Self; fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> Self; } impl Binance for General { - fn new(api_key: Option, secret_key: Option) -> General { + fn new( + api_key: Option, + secret_key: Option, + ) -> General { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> General { General { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + client: Client::new( + api_key, + secret_key, + config.rest_api_endpoint.clone(), + ), } } } impl Binance for Account { - fn new(api_key: Option, secret_key: Option) -> Account { + fn new( + api_key: Option, + secret_key: Option, + ) -> Account { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> Account { Account { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + client: Client::new( + api_key, + secret_key, + config.rest_api_endpoint.clone(), + ), recv_window: config.recv_window, } } } impl Binance for Savings { - fn new(api_key: Option, secret_key: Option) -> Self { + fn new( + api_key: Option, + secret_key: Option, + ) -> Self { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> Self { Self { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + client: Client::new( + api_key, + secret_key, + config.rest_api_endpoint.clone(), + ), recv_window: config.recv_window, } } } impl Binance for Market { - fn new(api_key: Option, secret_key: Option) -> Market { + fn new( + api_key: Option, + secret_key: Option, + ) -> Market { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> Market { Market { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + client: Client::new( + api_key, + secret_key, + config.rest_api_endpoint.clone(), + ), recv_window: config.recv_window, } } } impl Binance for UserStream { - fn new(api_key: Option, secret_key: Option) -> UserStream { + fn new( + api_key: Option, + secret_key: Option, + ) -> UserStream { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> UserStream { UserStream { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + client: Client::new( + api_key, + secret_key, + config.rest_api_endpoint.clone(), + ), recv_window: config.recv_window, } } @@ -268,12 +336,17 @@ impl Binance for UserStream { // ***************************************************** impl Binance for FuturesGeneral { - fn new(api_key: Option, secret_key: Option) -> FuturesGeneral { + fn new( + api_key: Option, + secret_key: Option, + ) -> FuturesGeneral { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> FuturesGeneral { FuturesGeneral { client: Client::new( @@ -286,12 +359,17 @@ impl Binance for FuturesGeneral { } impl Binance for FuturesMarket { - fn new(api_key: Option, secret_key: Option) -> FuturesMarket { + fn new( + api_key: Option, + secret_key: Option, + ) -> FuturesMarket { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> FuturesMarket { FuturesMarket { client: Client::new( @@ -305,12 +383,17 @@ impl Binance for FuturesMarket { } impl Binance for FuturesAccount { - fn new(api_key: Option, secret_key: Option) -> Self { + fn new( + api_key: Option, + secret_key: Option, + ) -> Self { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> Self { Self { client: Client::new( @@ -324,12 +407,17 @@ impl Binance for FuturesAccount { } impl Binance for FuturesUserStream { - fn new(api_key: Option, secret_key: Option) -> FuturesUserStream { + fn new( + api_key: Option, + secret_key: Option, + ) -> FuturesUserStream { Self::new_with_config(api_key, secret_key, &Config::default()) } fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, + api_key: Option, + secret_key: Option, + config: &Config, ) -> FuturesUserStream { FuturesUserStream { client: Client::new( diff --git a/src/client.rs b/src/client.rs index 35032262..e740e202 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,13 +1,15 @@ +use crate::api::API; +use crate::errors::{BinanceContentError, ErrorKind, Result}; use error_chain::bail; use hex::encode as hex_encode; use hmac::{Hmac, Mac}; -use crate::errors::{BinanceContentError, ErrorKind, Result}; -use reqwest::StatusCode; use reqwest::blocking::Response; -use reqwest::header::{HeaderMap, HeaderName, HeaderValue, USER_AGENT, CONTENT_TYPE}; -use sha2::Sha256; +use reqwest::header::{ + HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE, USER_AGENT, +}; +use reqwest::StatusCode; use serde::de::DeserializeOwned; -use crate::api::API; +use sha2::Sha256; #[derive(Clone)] pub struct Client { @@ -18,7 +20,11 @@ pub struct Client { } impl Client { - pub fn new(api_key: Option, secret_key: Option, host: String) -> Self { + pub fn new( + api_key: Option, + secret_key: Option, + host: String, + ) -> Self { Client { api_key: api_key.unwrap_or_default(), secret_key: secret_key.unwrap_or_default(), @@ -31,7 +37,9 @@ impl Client { } pub fn get_signed( - &self, endpoint: API, request: Option, + &self, + endpoint: API, + request: Option, ) -> Result { let url = self.sign_request(endpoint, request); let client = &self.inner_client; @@ -43,7 +51,11 @@ impl Client { self.handler(response) } - pub fn post_signed(&self, endpoint: API, request: String) -> Result { + pub fn post_signed( + &self, + endpoint: API, + request: String, + ) -> Result { let url = self.sign_request(endpoint, Some(request)); let client = &self.inner_client; let response = client @@ -55,7 +67,9 @@ impl Client { } pub fn delete_signed( - &self, endpoint: API, request: Option, + &self, + endpoint: API, + request: Option, ) -> Result { let url = self.sign_request(endpoint, request); let client = &self.inner_client; @@ -67,8 +81,13 @@ impl Client { self.handler(response) } - pub fn get(&self, endpoint: API, request: Option) -> Result { - let mut url: String = format!("{}{}", self.host, String::from(endpoint)); + pub fn get( + &self, + endpoint: API, + request: Option, + ) -> Result { + let mut url: String = + format!("{}{}", self.host, String::from(endpoint)); if let Some(request) = request { if !request.is_empty() { url.push_str(format!("?{}", request).as_str()); @@ -81,8 +100,12 @@ impl Client { self.handler(response) } - pub fn post(&self, endpoint: API) -> Result { - let url: String = format!("{}{}", self.host, String::from(endpoint)); + pub fn post( + &self, + endpoint: API, + ) -> Result { + let url: String = + format!("{}{}", self.host, String::from(endpoint)); let client = &self.inner_client; let response = client @@ -93,8 +116,13 @@ impl Client { self.handler(response) } - pub fn put(&self, endpoint: API, listen_key: &str) -> Result { - let url: String = format!("{}{}", self.host, String::from(endpoint)); + pub fn put( + &self, + endpoint: API, + listen_key: &str, + ) -> Result { + let url: String = + format!("{}{}", self.host, String::from(endpoint)); let data: String = format!("listenKey={}", listen_key); let client = &self.inner_client; @@ -107,8 +135,13 @@ impl Client { self.handler(response) } - pub fn delete(&self, endpoint: API, listen_key: &str) -> Result { - let url: String = format!("{}{}", self.host, String::from(endpoint)); + pub fn delete( + &self, + endpoint: API, + listen_key: &str, + ) -> Result { + let url: String = + format!("{}{}", self.host, String::from(endpoint)); let data: String = format!("listenKey={}", listen_key); let client = &self.inner_client; @@ -122,30 +155,56 @@ impl Client { } // Request must be signed - fn sign_request(&self, endpoint: API, request: Option) -> String { + fn sign_request( + &self, + endpoint: API, + request: Option, + ) -> String { if let Some(request) = request { - let mut signed_key = - Hmac::::new_from_slice(self.secret_key.as_bytes()).unwrap(); + let mut signed_key = Hmac::::new_from_slice( + self.secret_key.as_bytes(), + ) + .unwrap(); signed_key.update(request.as_bytes()); - let signature = hex_encode(signed_key.finalize().into_bytes()); - let request_body: String = format!("{}&signature={}", request, signature); - format!("{}{}?{}", self.host, String::from(endpoint), request_body) + let signature = + hex_encode(signed_key.finalize().into_bytes()); + let request_body: String = + format!("{}&signature={}", request, signature); + format!( + "{}{}?{}", + self.host, + String::from(endpoint), + request_body + ) } else { - let signed_key = Hmac::::new_from_slice(self.secret_key.as_bytes()).unwrap(); - let signature = hex_encode(signed_key.finalize().into_bytes()); - let request_body: String = format!("&signature={}", signature); - format!("{}{}?{}", self.host, String::from(endpoint), request_body) + let signed_key = Hmac::::new_from_slice( + self.secret_key.as_bytes(), + ) + .unwrap(); + let signature = + hex_encode(signed_key.finalize().into_bytes()); + let request_body: String = + format!("&signature={}", signature); + format!( + "{}{}?{}", + self.host, + String::from(endpoint), + request_body + ) } } fn build_headers(&self, content_type: bool) -> Result { let mut custom_headers = HeaderMap::new(); - custom_headers.insert(USER_AGENT, HeaderValue::from_static("binance-rs")); + custom_headers + .insert(USER_AGENT, HeaderValue::from_static("binance-rs")); if content_type { custom_headers.insert( CONTENT_TYPE, - HeaderValue::from_static("application/x-www-form-urlencoded"), + HeaderValue::from_static( + "application/x-www-form-urlencoded", + ), ); } custom_headers.insert( @@ -156,7 +215,10 @@ impl Client { Ok(custom_headers) } - fn handler(&self, response: Response) -> Result { + fn handler( + &self, + response: Response, + ) -> Result { match response.status() { StatusCode::OK => Ok(response.json::()?), StatusCode::INTERNAL_SERVER_ERROR => { diff --git a/src/config.rs b/src/config.rs index 6bc849dc..486b3c6a 100755 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,8 @@ impl Default for Config { rest_api_endpoint: "https://api.binance.com".into(), ws_endpoint: "wss://stream.binance.com:9443/ws".into(), - futures_rest_api_endpoint: "https://fapi.binance.com".into(), + futures_rest_api_endpoint: "https://fapi.binance.com" + .into(), futures_ws_endpoint: "wss://fstream.binance.com/ws".into(), recv_window: 5000, @@ -28,27 +29,42 @@ impl Config { Self::default() .set_rest_api_endpoint("https://testnet.binance.vision") .set_ws_endpoint("wss://testnet.binance.vision/ws") - .set_futures_rest_api_endpoint("https://testnet.binancefuture.com") - .set_futures_ws_endpoint("https://testnet.binancefuture.com/ws") + .set_futures_rest_api_endpoint( + "https://testnet.binancefuture.com", + ) + .set_futures_ws_endpoint( + "https://testnet.binancefuture.com/ws", + ) } - pub fn set_rest_api_endpoint>(mut self, rest_api_endpoint: T) -> Self { + pub fn set_rest_api_endpoint>( + mut self, + rest_api_endpoint: T, + ) -> Self { self.rest_api_endpoint = rest_api_endpoint.into(); self } - pub fn set_ws_endpoint>(mut self, ws_endpoint: T) -> Self { + pub fn set_ws_endpoint>( + mut self, + ws_endpoint: T, + ) -> Self { self.ws_endpoint = ws_endpoint.into(); self } pub fn set_futures_rest_api_endpoint>( - mut self, futures_rest_api_endpoint: T, + mut self, + futures_rest_api_endpoint: T, ) -> Self { - self.futures_rest_api_endpoint = futures_rest_api_endpoint.into(); + self.futures_rest_api_endpoint = + futures_rest_api_endpoint.into(); self } - pub fn set_futures_ws_endpoint>(mut self, futures_ws_endpoint: T) -> Self { + pub fn set_futures_ws_endpoint>( + mut self, + futures_ws_endpoint: T, + ) -> Self { self.futures_ws_endpoint = futures_ws_endpoint.into(); self } diff --git a/src/errors.rs b/src/errors.rs index 208636ed..bd267f55 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,5 @@ -use serde::Deserialize; use error_chain::error_chain; +use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct BinanceContentError { diff --git a/src/futures/account.rs b/src/futures/account.rs index 71fa8a8d..fde3f087 100644 --- a/src/futures/account.rs +++ b/src/futures/account.rs @@ -1,17 +1,17 @@ use std::collections::BTreeMap; use std::fmt::Display; -use crate::util::build_signed_request; -use crate::errors::Result; -use crate::client::Client; -use crate::api::{API, Futures}; -use crate::model::Empty; use crate::account::OrderSide; +use crate::api::{Futures, API}; +use crate::client::Client; +use crate::errors::Result; use crate::futures::model::{Order, TradeHistory}; +use crate::model::Empty; +use crate::util::build_signed_request; use super::model::{ - ChangeLeverageResponse, Transaction, CanceledOrder, PositionRisk, AccountBalance, - AccountInformation, + AccountBalance, AccountInformation, CanceledOrder, + ChangeLeverageResponse, PositionRisk, Transaction, }; #[derive(Clone)] @@ -34,7 +34,9 @@ impl From for String { ContractType::Perpetual => String::from("PERPETUAL"), ContractType::CurrentMonth => String::from("CURRENT_MONTH"), ContractType::NextMonth => String::from("NEXT_MONTH"), - ContractType::CurrentQuarter => String::from("CURRENT_QUARTER"), + ContractType::CurrentQuarter => { + String::from("CURRENT_QUARTER") + } ContractType::NextQuarter => String::from("NEXT_QUARTER"), } } @@ -75,7 +77,9 @@ impl Display for OrderType { Self::StopMarket => write!(f, "STOP_MARKET"), Self::TakeProfit => write!(f, "TAKE_PROFIT"), Self::TakeProfitMarket => write!(f, "TAKE_PROFIT_MARKET"), - Self::TrailingStopMarket => write!(f, "TRAILING_STOP_MARKET"), + Self::TrailingStopMarket => { + write!(f, "TRAILING_STOP_MARKET") + } } } } @@ -191,22 +195,35 @@ impl Display for IncomeType { Self::COMMISSION_REBATE => write!(f, "COMMISSION_REBATE"), Self::API_REBATE => write!(f, "API_REBATE"), Self::CONTEST_REWARD => write!(f, "CONTEST_REWARD"), - Self::CROSS_COLLATERAL_TRANSFER => write!(f, "CROSS_COLLATERAL_TRANSFER"), - Self::OPTIONS_PREMIUM_FEE => write!(f, "OPTIONS_PREMIUM_FEE"), - Self::OPTIONS_SETTLE_PROFIT => write!(f, "OPTIONS_SETTLE_PROFIT"), + Self::CROSS_COLLATERAL_TRANSFER => { + write!(f, "CROSS_COLLATERAL_TRANSFER") + } + Self::OPTIONS_PREMIUM_FEE => { + write!(f, "OPTIONS_PREMIUM_FEE") + } + Self::OPTIONS_SETTLE_PROFIT => { + write!(f, "OPTIONS_SETTLE_PROFIT") + } Self::INTERNAL_TRANSFER => write!(f, "INTERNAL_TRANSFER"), Self::AUTO_EXCHANGE => write!(f, "AUTO_EXCHANGE"), - Self::DELIVERED_SETTELMENT => write!(f, "DELIVERED_SETTELMENT"), + Self::DELIVERED_SETTELMENT => { + write!(f, "DELIVERED_SETTELMENT") + } Self::COIN_SWAP_DEPOSIT => write!(f, "COIN_SWAP_DEPOSIT"), Self::COIN_SWAP_WITHDRAW => write!(f, "COIN_SWAP_WITHDRAW"), - Self::POSITION_LIMIT_INCREASE_FEE => write!(f, "POSITION_LIMIT_INCREASE_FEE"), + Self::POSITION_LIMIT_INCREASE_FEE => { + write!(f, "POSITION_LIMIT_INCREASE_FEE") + } } } } impl FuturesAccount { pub fn limit_buy( - &self, symbol: impl Into, qty: impl Into, price: f64, + &self, + symbol: impl Into, + qty: impl Into, + price: f64, time_in_force: TimeInForce, ) -> Result { let buy = OrderRequest { @@ -232,7 +249,10 @@ impl FuturesAccount { } pub fn limit_sell( - &self, symbol: impl Into, qty: impl Into, price: f64, + &self, + symbol: impl Into, + qty: impl Into, + price: f64, time_in_force: TimeInForce, ) -> Result { let sell = OrderRequest { @@ -258,7 +278,11 @@ impl FuturesAccount { } // Place a MARKET order - BUY - pub fn market_buy(&self, symbol: S, qty: F) -> Result + pub fn market_buy( + &self, + symbol: S, + qty: F, + ) -> Result where S: Into, F: Into, @@ -286,7 +310,11 @@ impl FuturesAccount { } // Place a MARKET order - SELL - pub fn market_sell(&self, symbol: S, qty: F) -> Result + pub fn market_sell( + &self, + symbol: S, + qty: F, + ) -> Result where S: Into, F: Into, @@ -313,7 +341,11 @@ impl FuturesAccount { .post_signed(API::Futures(Futures::Order), request) } - pub fn cancel_order(&self, symbol: S, order_id: u64) -> Result + pub fn cancel_order( + &self, + symbol: S, + order_id: u64, + ) -> Result where S: Into, { @@ -321,28 +353,37 @@ impl FuturesAccount { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Futures(Futures::Order), Some(request)) } pub fn cancel_order_with_client_id( - &self, symbol: S, orig_client_order_id: String, + &self, + symbol: S, + orig_client_order_id: String, ) -> Result where S: Into, { let mut parameters = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - parameters.insert("origClientOrderId".into(), orig_client_order_id); + parameters + .insert("origClientOrderId".into(), orig_client_order_id); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Futures(Futures::Order), Some(request)) } // Place a STOP_MARKET close - BUY - pub fn stop_market_close_buy(&self, symbol: S, stop_price: F) -> Result + pub fn stop_market_close_buy( + &self, + symbol: S, + stop_price: F, + ) -> Result where S: Into, F: Into, @@ -370,7 +411,11 @@ impl FuturesAccount { } // Place a STOP_MARKET close - SELL - pub fn stop_market_close_sell(&self, symbol: S, stop_price: F) -> Result + pub fn stop_market_close_sell( + &self, + symbol: S, + stop_price: F, + ) -> Result where S: Into, F: Into, @@ -398,7 +443,10 @@ impl FuturesAccount { } // Custom order for for professional traders - pub fn custom_order(&self, order_request: CustomOrderRequest) -> Result { + pub fn custom_order( + &self, + order_request: CustomOrderRequest, + ) -> Result { let order = OrderRequest { symbol: order_request.symbol, side: order_request.side, @@ -422,7 +470,12 @@ impl FuturesAccount { } pub fn get_all_orders( - &self, symbol: S, order_id: F, start_time: F, end_time: F, limit: N, + &self, + symbol: S, + order_id: F, + start_time: F, + end_time: F, + limit: N, ) -> Result> where S: Into, @@ -435,7 +488,8 @@ impl FuturesAccount { parameters.insert("orderId".into(), order_id.to_string()); } if let Some(start_time) = start_time.into() { - parameters.insert("startTime".into(), start_time.to_string()); + parameters + .insert("startTime".into(), start_time.to_string()); } if let Some(end_time) = end_time.into() { parameters.insert("endTime".into(), end_time.to_string()); @@ -444,13 +498,19 @@ impl FuturesAccount { parameters.insert("limit".into(), limit.to_string()); } - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Futures(Futures::AllOrders), Some(request)) } pub fn get_user_trades( - &self, symbol: S, from_id: F, start_time: F, end_time: F, limit: N, + &self, + symbol: S, + from_id: F, + start_time: F, + end_time: F, + limit: N, ) -> Result> where S: Into, @@ -463,7 +523,8 @@ impl FuturesAccount { parameters.insert("fromId".into(), order_id.to_string()); } if let Some(start_time) = start_time.into() { - parameters.insert("startTime".into(), start_time.to_string()); + parameters + .insert("startTime".into(), start_time.to_string()); } if let Some(end_time) = end_time.into() { parameters.insert("endTime".into(), end_time.to_string()); @@ -472,33 +533,49 @@ impl FuturesAccount { parameters.insert("limit".into(), limit.to_string()); } - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .get_signed(API::Futures(Futures::UserTrades), Some(request)) + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.get_signed( + API::Futures(Futures::UserTrades), + Some(request), + ) } - fn build_order(&self, order: OrderRequest) -> BTreeMap { + fn build_order( + &self, + order: OrderRequest, + ) -> BTreeMap { let mut parameters = BTreeMap::new(); parameters.insert("symbol".into(), order.symbol); parameters.insert("side".into(), order.side.to_string()); parameters.insert("type".into(), order.order_type.to_string()); if let Some(position_side) = order.position_side { - parameters.insert("positionSide".into(), position_side.to_string()); + parameters.insert( + "positionSide".into(), + position_side.to_string(), + ); } if let Some(time_in_force) = order.time_in_force { - parameters.insert("timeInForce".into(), time_in_force.to_string()); + parameters.insert( + "timeInForce".into(), + time_in_force.to_string(), + ); } if let Some(qty) = order.qty { parameters.insert("quantity".into(), qty.to_string()); } if let Some(reduce_only) = order.reduce_only { - parameters.insert("reduceOnly".into(), reduce_only.to_string().to_uppercase()); + parameters.insert( + "reduceOnly".into(), + reduce_only.to_string().to_uppercase(), + ); } if let Some(price) = order.price { parameters.insert("price".into(), price.to_string()); } if let Some(stop_price) = order.stop_price { - parameters.insert("stopPrice".into(), stop_price.to_string()); + parameters + .insert("stopPrice".into(), stop_price.to_string()); } if let Some(close_position) = order.close_position { parameters.insert( @@ -507,13 +584,20 @@ impl FuturesAccount { ); } if let Some(activation_price) = order.activation_price { - parameters.insert("activationPrice".into(), activation_price.to_string()); + parameters.insert( + "activationPrice".into(), + activation_price.to_string(), + ); } if let Some(callback_rate) = order.callback_rate { - parameters.insert("callbackRate".into(), callback_rate.to_string()); + parameters.insert( + "callbackRate".into(), + callback_rate.to_string(), + ); } if let Some(working_type) = order.working_type { - parameters.insert("workingType".into(), working_type.to_string()); + parameters + .insert("workingType".into(), working_type.to_string()); } if let Some(price_protect) = order.price_protect { parameters.insert( @@ -525,22 +609,29 @@ impl FuturesAccount { parameters } - pub fn position_information(&self, symbol: S) -> Result> + pub fn position_information( + &self, + symbol: S, + ) -> Result> where S: Into, { let mut parameters = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .get_signed(API::Futures(Futures::PositionRisk), Some(request)) + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.get_signed( + API::Futures(Futures::PositionRisk), + Some(request), + ) } pub fn account_information(&self) -> Result { let parameters = BTreeMap::new(); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Futures(Futures::Account), Some(request)) } @@ -548,13 +639,16 @@ impl FuturesAccount { pub fn account_balance(&self) -> Result> { let parameters = BTreeMap::new(); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Futures(Futures::Balance), Some(request)) } pub fn change_initial_leverage( - &self, symbol: S, leverage: u8, + &self, + symbol: S, + leverage: u8, ) -> Result where S: Into, @@ -563,19 +657,30 @@ impl FuturesAccount { parameters.insert("symbol".into(), symbol.into()); parameters.insert("leverage".into(), leverage.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .post_signed(API::Futures(Futures::ChangeInitialLeverage), request) + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.post_signed( + API::Futures(Futures::ChangeInitialLeverage), + request, + ) } - pub fn change_position_mode(&self, dual_side_position: bool) -> Result<()> { + pub fn change_position_mode( + &self, + dual_side_position: bool, + ) -> Result<()> { let mut parameters: BTreeMap = BTreeMap::new(); - let dual_side = if dual_side_position { "true" } else { "false" }; + let dual_side = + if dual_side_position { "true" } else { "false" }; parameters.insert("dualSidePosition".into(), dual_side.into()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client - .post_signed::(API::Futures(Futures::PositionSide), request) + .post_signed::( + API::Futures(Futures::PositionSide), + request, + ) .map(|_| ()) } @@ -585,35 +690,48 @@ impl FuturesAccount { { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client - .delete_signed::(API::Futures(Futures::AllOpenOrders), Some(request)) + .delete_signed::( + API::Futures(Futures::AllOpenOrders), + Some(request), + ) .map(|_| ()) } - pub fn get_all_open_orders(&self, symbol: S) -> Result> + pub fn get_all_open_orders( + &self, + symbol: S, + ) -> Result> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .get_signed(API::Futures(Futures::OpenOrders), Some(request)) + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.get_signed( + API::Futures(Futures::OpenOrders), + Some(request), + ) } pub fn get_income( - &self, income_request: IncomeRequest, + &self, + income_request: IncomeRequest, ) -> Result> { let mut parameters: BTreeMap = BTreeMap::new(); if let Some(symbol) = income_request.symbol { parameters.insert("symbol".into(), symbol); } if let Some(income_type) = income_request.income_type { - parameters.insert("incomeType".into(), income_type.to_string()); + parameters + .insert("incomeType".into(), income_type.to_string()); } if let Some(start_time) = income_request.start_time { - parameters.insert("startTime".into(), start_time.to_string()); + parameters + .insert("startTime".into(), start_time.to_string()); } if let Some(end_time) = income_request.end_time { parameters.insert("endTime".into(), end_time.to_string()); @@ -622,7 +740,8 @@ impl FuturesAccount { parameters.insert("limit".into(), limit.to_string()); } - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; println!("{}", request); self.client .get_signed(API::Futures(Futures::Income), Some(request)) diff --git a/src/futures/general.rs b/src/futures/general.rs index 0b53b50e..57d3116f 100644 --- a/src/futures/general.rs +++ b/src/futures/general.rs @@ -1,10 +1,10 @@ use error_chain::bail; -use crate::futures::model::{ExchangeInformation, ServerTime, Symbol}; +use crate::api::Futures; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use crate::api::API; -use crate::api::Futures; +use crate::futures::model::{ExchangeInformation, ServerTime, Symbol}; #[derive(Clone)] pub struct FuturesGeneral { diff --git a/src/futures/market.rs b/src/futures/market.rs index 6def5883..bb97d361 100644 --- a/src/futures/market.rs +++ b/src/futures/market.rs @@ -20,17 +20,18 @@ - [ ] `Taker Buy/Sell Volume (MARKET_DATA)` */ -use crate::util::{build_request, build_signed_request}; -use crate::futures::model::{ - AggTrades, BookTickers, KlineSummaries, KlineSummary, LiquidationOrders, MarkPrices, - OpenInterest, OpenInterestHist, OrderBook, PriceStats, SymbolPrice, Tickers, Trades, -}; +use crate::api::Futures; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use std::collections::BTreeMap; +use crate::futures::model::{ + AggTrades, BookTickers, KlineSummaries, KlineSummary, + LiquidationOrders, MarkPrices, OpenInterest, OpenInterestHist, + OrderBook, PriceStats, SymbolPrice, Tickers, Trades, +}; +use crate::util::{build_request, build_signed_request}; use serde_json::Value; -use crate::api::API; -use crate::api::Futures; +use std::collections::BTreeMap; use std::convert::TryInto; // TODO @@ -60,7 +61,11 @@ impl FuturesMarket { // Order book at a custom depth. Currently supported values // are 5, 10, 20, 50, 100, 500, 1000 - pub fn get_custom_depth(&self, symbol: S, depth: u64) -> Result + pub fn get_custom_depth( + &self, + symbol: S, + depth: u64, + ) -> Result where S: Into, { @@ -84,7 +89,10 @@ impl FuturesMarket { // TODO This may be incomplete, as it hasn't been tested pub fn get_historical_trades( - &self, symbol: S1, from_id: S2, limit: S3, + &self, + symbol: S1, + from_id: S2, + limit: S3, ) -> Result where S1: Into, @@ -103,14 +111,22 @@ impl FuturesMarket { parameters.insert("fromId".into(), format!("{}", fi)); } - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; - self.client - .get_signed(API::Futures(Futures::HistoricalTrades), Some(request)) + self.client.get_signed( + API::Futures(Futures::HistoricalTrades), + Some(request), + ) } pub fn get_agg_trades( - &self, symbol: S1, from_id: S2, start_time: S3, end_time: S4, limit: S5, + &self, + symbol: S1, + from_id: S2, + start_time: S3, + end_time: S4, + limit: S5, ) -> Result where S1: Into, @@ -146,7 +162,12 @@ impl FuturesMarket { // Returns up to 'limit' klines for given symbol and interval ("1m", "5m", ...) // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data pub fn get_klines( - &self, symbol: S1, interval: S2, limit: S3, start_time: S4, end_time: S5, + &self, + symbol: S1, + interval: S2, + limit: S3, + start_time: S4, + end_time: S5, ) -> Result where S1: Into, @@ -187,7 +208,10 @@ impl FuturesMarket { } // 24hr ticker price change statistics - pub fn get_24h_price_stats(&self, symbol: S) -> Result + pub fn get_24h_price_stats( + &self, + symbol: S, + ) -> Result where S: Into, { @@ -246,7 +270,9 @@ impl FuturesMarket { self.client.get(API::Futures(Futures::PremiumIndex), None) } - pub fn get_all_liquidation_orders(&self) -> Result { + pub fn get_all_liquidation_orders( + &self, + ) -> Result { self.client.get(API::Futures(Futures::AllForceOrders), None) } @@ -262,7 +288,12 @@ impl FuturesMarket { } pub fn open_interest_statistics( - &self, symbol: S1, period: S2, limit: S3, start_time: S4, end_time: S5, + &self, + symbol: S1, + period: S2, + limit: S3, + start_time: S4, + end_time: S5, ) -> Result> where S1: Into, diff --git a/src/futures/model.rs b/src/futures/model.rs index af209c46..8d3afdc2 100644 --- a/src/futures/model.rs +++ b/src/futures/model.rs @@ -1,9 +1,11 @@ +use crate::model::{ + string_or_bool, string_or_float, string_or_float_opt, +}; use serde::{Deserialize, Serialize}; -use crate::model::{string_or_float, string_or_float_opt, string_or_bool}; pub use crate::model::{ - Asks, Bids, BookTickers, Filters, KlineSummaries, KlineSummary, RateLimit, ServerTime, - SymbolPrice, Tickers, + Asks, Bids, BookTickers, Filters, KlineSummaries, KlineSummary, + RateLimit, ServerTime, SymbolPrice, Tickers, }; #[derive(Debug, Serialize, Deserialize, Clone)] @@ -234,7 +236,10 @@ pub struct Order { #[serde(rename = "type")] pub order_type: String, pub orig_type: String, - #[serde(with = "string_or_float", default = "default_activation_price")] + #[serde( + with = "string_or_float", + default = "default_activation_price" + )] pub activation_price: f64, #[serde(with = "string_or_float", default = "default_price_rate")] pub price_rate: f64, diff --git a/src/futures/userstream.rs b/src/futures/userstream.rs index fdcad313..5eb1d7bb 100644 --- a/src/futures/userstream.rs +++ b/src/futures/userstream.rs @@ -1,8 +1,8 @@ -use crate::model::{Success, UserDataStream}; +use crate::api::Futures; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use crate::api::API; -use crate::api::Futures; +use crate::model::{Success, UserDataStream}; #[derive(Clone)] pub struct FuturesUserStream { diff --git a/src/futures/websockets.rs b/src/futures/websockets.rs index 4fe76ff8..0863bae9 100755 --- a/src/futures/websockets.rs +++ b/src/futures/websockets.rs @@ -1,20 +1,22 @@ -use crate::errors::Result; use crate::config::Config; +use crate::errors::Result; +use crate::futures::model; use crate::model::{ - AccountUpdateEvent, AggrTradesEvent, BookTickerEvent, ContinuousKlineEvent, DayTickerEvent, - DepthOrderBookEvent, IndexKlineEvent, IndexPriceEvent, KlineEvent, LiquidationEvent, - MarkPriceEvent, MiniTickerEvent, OrderBook, TradeEvent, UserDataStreamExpiredEvent, + AccountUpdateEvent, AggrTradesEvent, BookTickerEvent, + ContinuousKlineEvent, DayTickerEvent, DepthOrderBookEvent, + IndexKlineEvent, IndexPriceEvent, KlineEvent, LiquidationEvent, + MarkPriceEvent, MiniTickerEvent, OrderBook, TradeEvent, + UserDataStreamExpiredEvent, }; -use crate::futures::model; use error_chain::bail; -use url::Url; use serde::{Deserialize, Serialize}; -use std::sync::atomic::{AtomicBool, Ordering}; use std::net::TcpStream; -use tungstenite::{connect, Message}; +use std::sync::atomic::{AtomicBool, Ordering}; +use tungstenite::handshake::client::Response; use tungstenite::protocol::WebSocket; use tungstenite::stream::MaybeTlsStream; -use tungstenite::handshake::client::Response; +use tungstenite::{connect, Message}; +use url::Url; #[allow(clippy::all)] enum FuturesWebsocketAPI { Default, @@ -29,7 +31,11 @@ pub enum FuturesMarket { } impl FuturesWebsocketAPI { - fn params(self, market: &FuturesMarket, subscription: &str) -> String { + fn params( + self, + market: &FuturesMarket, + subscription: &str, + ) -> String { let baseurl = match market { FuturesMarket::USDM => "wss://fstream.binance.com", FuturesMarket::COINM => "wss://dstream.binance.com", @@ -73,7 +79,8 @@ pub enum FuturesWebsocketEvent { } pub struct FuturesWebSockets<'a> { - pub socket: Option<(WebSocket>, Response)>, + pub socket: + Option<(WebSocket>, Response)>, handler: Box Result<()> + 'a>, } @@ -112,22 +119,37 @@ impl<'a> FuturesWebSockets<'a> { } } - pub fn connect(&mut self, market: &FuturesMarket, subscription: &'a str) -> Result<()> { - self.connect_wss(&FuturesWebsocketAPI::Default.params(market, subscription)) + pub fn connect( + &mut self, + market: &FuturesMarket, + subscription: &'a str, + ) -> Result<()> { + self.connect_wss( + &FuturesWebsocketAPI::Default.params(market, subscription), + ) } pub fn connect_with_config( - &mut self, market: &FuturesMarket, subscription: &'a str, config: &'a Config, + &mut self, + market: &FuturesMarket, + subscription: &'a str, + config: &'a Config, ) -> Result<()> { self.connect_wss( - &FuturesWebsocketAPI::Custom(config.ws_endpoint.clone()).params(market, subscription), + &FuturesWebsocketAPI::Custom(config.ws_endpoint.clone()) + .params(market, subscription), ) } pub fn connect_multiple_streams( - &mut self, market: &FuturesMarket, endpoints: &[String], + &mut self, + market: &FuturesMarket, + endpoints: &[String], ) -> Result<()> { - self.connect_wss(&FuturesWebsocketAPI::MultiStream.params(market, &endpoints.join("/"))) + self.connect_wss( + &FuturesWebsocketAPI::MultiStream + .params(market, &endpoints.join("/")), + ) } fn connect_wss(&mut self, wss: &str) -> Result<()> { @@ -161,26 +183,64 @@ impl<'a> FuturesWebSockets<'a> { return Ok(()); } - if let Ok(events) = serde_json::from_value::(value) { + if let Ok(events) = + serde_json::from_value::(value) + { let action = match events { - FuturesEvents::Vec(v) => FuturesWebsocketEvent::DayTickerAll(v), - FuturesEvents::DayTickerEvent(v) => FuturesWebsocketEvent::DayTicker(v), - FuturesEvents::BookTickerEvent(v) => FuturesWebsocketEvent::BookTicker(v), - FuturesEvents::MiniTickerEvent(v) => FuturesWebsocketEvent::MiniTicker(v), - FuturesEvents::VecMiniTickerEvent(v) => FuturesWebsocketEvent::MiniTickerAll(v), - FuturesEvents::AccountUpdateEvent(v) => FuturesWebsocketEvent::AccountUpdate(v), - FuturesEvents::OrderTradeEvent(v) => FuturesWebsocketEvent::OrderTrade(v), - FuturesEvents::IndexPriceEvent(v) => FuturesWebsocketEvent::IndexPrice(v), - FuturesEvents::MarkPriceEvent(v) => FuturesWebsocketEvent::MarkPrice(v), - FuturesEvents::VecMarkPriceEvent(v) => FuturesWebsocketEvent::MarkPriceAll(v), - FuturesEvents::TradeEvent(v) => FuturesWebsocketEvent::Trade(v), - FuturesEvents::ContinuousKlineEvent(v) => FuturesWebsocketEvent::ContinuousKline(v), - FuturesEvents::IndexKlineEvent(v) => FuturesWebsocketEvent::IndexKline(v), - FuturesEvents::LiquidationEvent(v) => FuturesWebsocketEvent::Liquidation(v), - FuturesEvents::KlineEvent(v) => FuturesWebsocketEvent::Kline(v), - FuturesEvents::OrderBook(v) => FuturesWebsocketEvent::OrderBook(v), - FuturesEvents::DepthOrderBookEvent(v) => FuturesWebsocketEvent::DepthOrderBook(v), - FuturesEvents::AggrTradesEvent(v) => FuturesWebsocketEvent::AggrTrades(v), + FuturesEvents::Vec(v) => { + FuturesWebsocketEvent::DayTickerAll(v) + } + FuturesEvents::DayTickerEvent(v) => { + FuturesWebsocketEvent::DayTicker(v) + } + FuturesEvents::BookTickerEvent(v) => { + FuturesWebsocketEvent::BookTicker(v) + } + FuturesEvents::MiniTickerEvent(v) => { + FuturesWebsocketEvent::MiniTicker(v) + } + FuturesEvents::VecMiniTickerEvent(v) => { + FuturesWebsocketEvent::MiniTickerAll(v) + } + FuturesEvents::AccountUpdateEvent(v) => { + FuturesWebsocketEvent::AccountUpdate(v) + } + FuturesEvents::OrderTradeEvent(v) => { + FuturesWebsocketEvent::OrderTrade(v) + } + FuturesEvents::IndexPriceEvent(v) => { + FuturesWebsocketEvent::IndexPrice(v) + } + FuturesEvents::MarkPriceEvent(v) => { + FuturesWebsocketEvent::MarkPrice(v) + } + FuturesEvents::VecMarkPriceEvent(v) => { + FuturesWebsocketEvent::MarkPriceAll(v) + } + FuturesEvents::TradeEvent(v) => { + FuturesWebsocketEvent::Trade(v) + } + FuturesEvents::ContinuousKlineEvent(v) => { + FuturesWebsocketEvent::ContinuousKline(v) + } + FuturesEvents::IndexKlineEvent(v) => { + FuturesWebsocketEvent::IndexKline(v) + } + FuturesEvents::LiquidationEvent(v) => { + FuturesWebsocketEvent::Liquidation(v) + } + FuturesEvents::KlineEvent(v) => { + FuturesWebsocketEvent::Kline(v) + } + FuturesEvents::OrderBook(v) => { + FuturesWebsocketEvent::OrderBook(v) + } + FuturesEvents::DepthOrderBookEvent(v) => { + FuturesWebsocketEvent::DepthOrderBook(v) + } + FuturesEvents::AggrTradesEvent(v) => { + FuturesWebsocketEvent::AggrTrades(v) + } FuturesEvents::UserDataStreamExpiredEvent(v) => { FuturesWebsocketEvent::UserDataStreamExpiredEvent(v) } @@ -197,14 +257,24 @@ impl<'a> FuturesWebSockets<'a> { match message { Message::Text(msg) => { if let Err(e) = self.handle_msg(&msg) { - bail!(format!("Error on handling stream message: {}", e)); + bail!(format!( + "Error on handling stream message: {}", + e + )); } } Message::Ping(_) => { - socket.0.write_message(Message::Pong(vec![])).unwrap(); + socket + .0 + .write_message(Message::Pong(vec![])) + .unwrap(); + } + Message::Pong(_) + | Message::Binary(_) + | Message::Frame(_) => (), + Message::Close(e) => { + bail!(format!("Disconnected {:?}", e)) } - Message::Pong(_) | Message::Binary(_) | Message::Frame(_) => (), - Message::Close(e) => bail!(format!("Disconnected {:?}", e)), } } } diff --git a/src/general.rs b/src/general.rs index 357356b2..cadc3456 100644 --- a/src/general.rs +++ b/src/general.rs @@ -1,10 +1,10 @@ use error_chain::bail; -use crate::model::{Empty, ExchangeInformation, ServerTime, Symbol}; +use crate::api::Spot; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use crate::api::API; -use crate::api::Spot; +use crate::model::{Empty, ExchangeInformation, ServerTime, Symbol}; #[derive(Clone)] pub struct General { diff --git a/src/market.rs b/src/market.rs index a3317268..0349dbee 100644 --- a/src/market.rs +++ b/src/market.rs @@ -1,14 +1,14 @@ -use crate::util::build_request; -use crate::model::{ - AggTrade, AveragePrice, BookTickers, KlineSummaries, KlineSummary, OrderBook, PriceStats, - Prices, SymbolPrice, Tickers, -}; +use crate::api::Spot; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use std::collections::BTreeMap; +use crate::model::{ + AggTrade, AveragePrice, BookTickers, KlineSummaries, KlineSummary, + OrderBook, PriceStats, Prices, SymbolPrice, Tickers, +}; +use crate::util::build_request; use serde_json::Value; -use crate::api::API; -use crate::api::Spot; +use std::collections::BTreeMap; use std::convert::TryInto; #[derive(Clone)] @@ -32,7 +32,11 @@ impl Market { // Order book at a custom depth. Currently supported values // are 5, 10, 20, 50, 100, 500, 1000 and 5000 - pub fn get_custom_depth(&self, symbol: S, depth: u64) -> Result + pub fn get_custom_depth( + &self, + symbol: S, + depth: u64, + ) -> Result where S: Into, { @@ -60,7 +64,10 @@ impl Market { } // Average price for ONE symbol. - pub fn get_average_price(&self, symbol: S) -> Result + pub fn get_average_price( + &self, + symbol: S, + ) -> Result where S: Into, { @@ -88,7 +95,10 @@ impl Market { } // 24hr ticker price change statistics - pub fn get_24h_price_stats(&self, symbol: S) -> Result + pub fn get_24h_price_stats( + &self, + symbol: S, + ) -> Result where S: Into, { @@ -108,7 +118,12 @@ impl Market { /// If you provide start_time, you also need to provide end_time. /// If from_id, start_time and end_time are omitted, the most recent trades are fetched. pub fn get_agg_trades( - &self, symbol: S1, from_id: S2, start_time: S3, end_time: S4, limit: S5, + &self, + symbol: S1, + from_id: S2, + start_time: S3, + end_time: S4, + limit: S5, ) -> Result> where S1: Into, @@ -143,7 +158,12 @@ impl Market { // Returns up to 'limit' klines for given symbol and interval ("1m", "5m", ...) // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data pub fn get_klines( - &self, symbol: S1, interval: S2, limit: S3, start_time: S4, end_time: S5, + &self, + symbol: S1, + interval: S2, + limit: S3, + start_time: S4, + end_time: S5, ) -> Result where S1: Into, @@ -169,7 +189,8 @@ impl Market { } let request = build_request(parameters); - let data: Vec> = self.client.get(API::Spot(Spot::Klines), Some(request))?; + let data: Vec> = + self.client.get(API::Spot(Spot::Klines), Some(request))?; let klines = KlineSummaries::AllKlineSummaries( data.iter() diff --git a/src/model.rs b/src/model.rs index a93fa953..72f22a28 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,7 +1,7 @@ +use crate::errors::{Error, ErrorKind, Result}; use serde::{Deserialize, Serialize}; use serde_json::{from_value, Value}; use std::convert::TryFrom; -use crate::errors::{Error, ErrorKind, Result}; #[derive(Deserialize, Clone)] pub struct Empty {} @@ -982,7 +982,11 @@ pub struct KlineSummary { pub taker_buy_quote_asset_volume: String, } -fn get_value(row: &[Value], index: usize, name: &'static str) -> Result { +fn get_value( + row: &[Value], + index: usize, + name: &'static str, +) -> Result { Ok(row .get(index) .ok_or_else(|| ErrorKind::KlineValueMissingError(index, name))? @@ -1001,8 +1005,16 @@ impl TryFrom<&Vec> for KlineSummary { close: from_value(get_value(row, 4, "close")?)?, volume: from_value(get_value(row, 5, "volume")?)?, close_time: from_value(get_value(row, 6, "close_time")?)?, - quote_asset_volume: from_value(get_value(row, 7, "quote_asset_volume")?)?, - number_of_trades: from_value(get_value(row, 8, "number_of_trades")?)?, + quote_asset_volume: from_value(get_value( + row, + 7, + "quote_asset_volume", + )?)?, + number_of_trades: from_value(get_value( + row, + 8, + "number_of_trades", + )?)?, taker_buy_base_asset_volume: from_value(get_value( row, 9, @@ -1293,9 +1305,12 @@ pub struct DepositAddress { pub(crate) mod string_or_float { use std::fmt; - use serde::{de, Serializer, Deserialize, Deserializer}; + use serde::{de, Deserialize, Deserializer, Serializer}; - pub fn serialize(value: &T, serializer: S) -> Result + pub fn serialize( + value: &T, + serializer: S, + ) -> Result where T: fmt::Display, S: Serializer, @@ -1330,20 +1345,27 @@ pub(crate) mod string_or_float { pub(crate) mod string_or_float_opt { use std::fmt; - use serde::{Serializer, Deserialize, Deserializer}; + use serde::{Deserialize, Deserializer, Serializer}; - pub fn serialize(value: &Option, serializer: S) -> Result + pub fn serialize( + value: &Option, + serializer: S, + ) -> Result where T: fmt::Display, S: Serializer, { match value { - Some(v) => crate::model::string_or_float::serialize(v, serializer), + Some(v) => { + crate::model::string_or_float::serialize(v, serializer) + } None => serializer.serialize_none(), } } - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + pub fn deserialize<'de, D>( + deserializer: D, + ) -> Result, D::Error> where D: Deserializer<'de>, { @@ -1363,9 +1385,12 @@ pub(crate) mod string_or_float_opt { pub(crate) mod string_or_bool { use std::fmt; - use serde::{de, Serializer, Deserialize, Deserializer}; + use serde::{de, Deserialize, Deserializer, Serializer}; - pub fn serialize(value: &T, serializer: S) -> Result + pub fn serialize( + value: &T, + serializer: S, + ) -> Result where T: fmt::Display, S: Serializer, @@ -1373,7 +1398,9 @@ pub(crate) mod string_or_bool { serializer.collect_str(value) } - pub fn deserialize<'de, D>(deserializer: D) -> Result + pub fn deserialize<'de, D>( + deserializer: D, + ) -> Result where D: Deserializer<'de>, { @@ -1385,7 +1412,9 @@ pub(crate) mod string_or_bool { } match StringOrFloat::deserialize(deserializer)? { - StringOrFloat::String(s) => s.parse().map_err(de::Error::custom), + StringOrFloat::String(s) => { + s.parse().map_err(de::Error::custom) + } StringOrFloat::Bool(i) => Ok(i), } } diff --git a/src/savings.rs b/src/savings.rs index fbde1279..2b82bef8 100644 --- a/src/savings.rs +++ b/src/savings.rs @@ -1,10 +1,13 @@ -use crate::util::build_signed_request; -use crate::model::{AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TransactionId}; +use crate::api::Sapi; +use crate::api::API; use crate::client::Client; use crate::errors::Result; +use crate::model::{ + AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, + TransactionId, +}; +use crate::util::build_signed_request; use std::collections::BTreeMap; -use crate::api::API; -use crate::api::Sapi; #[derive(Clone)] pub struct Savings { @@ -15,18 +18,23 @@ pub struct Savings { impl Savings { /// Get all coins available for deposit and withdrawal pub fn get_all_coins(&self) -> Result> { - let request = build_signed_request(BTreeMap::new(), self.recv_window)?; + let request = + build_signed_request(BTreeMap::new(), self.recv_window)?; self.client .get_signed(API::Savings(Sapi::AllCoins), Some(request)) } /// Fetch details of assets supported on Binance. - pub fn asset_detail(&self, asset: Option) -> Result> { + pub fn asset_detail( + &self, + asset: Option, + ) -> Result> { let mut parameters = BTreeMap::new(); if let Some(asset) = asset { parameters.insert("asset".into(), asset); } - let request = build_signed_request(parameters, self.recv_window)?; + let request = + build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Savings(Sapi::AssetDetail), Some(request)) } @@ -35,7 +43,11 @@ impl Savings { /// /// You can get the available networks using `get_all_coins`. /// If no network is specified, the address for the default network is returned. - pub fn deposit_address(&self, coin: S, network: Option) -> Result + pub fn deposit_address( + &self, + coin: S, + network: Option, + ) -> Result where S: Into, { @@ -44,13 +56,19 @@ impl Savings { if let Some(network) = network { parameters.insert("network".into(), network); } - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .get_signed(API::Savings(Sapi::DepositAddress), Some(request)) + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.get_signed( + API::Savings(Sapi::DepositAddress), + Some(request), + ) } pub fn transfer_funds( - &self, asset: S, amount: f64, transfer_type: SpotFuturesTransferType, + &self, + asset: S, + amount: f64, + transfer_type: SpotFuturesTransferType, ) -> Result where S: Into, @@ -58,9 +76,13 @@ impl Savings { let mut parameters = BTreeMap::new(); parameters.insert("asset".into(), asset.into()); parameters.insert("amount".into(), amount.to_string()); - parameters.insert("type".into(), (transfer_type as u8).to_string()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client - .post_signed(API::Savings(Sapi::SpotFuturesTransfer), request) + parameters + .insert("type".into(), (transfer_type as u8).to_string()); + let request = + build_signed_request(parameters, self.recv_window)?; + self.client.post_signed( + API::Savings(Sapi::SpotFuturesTransfer), + request, + ) } } diff --git a/src/userstream.rs b/src/userstream.rs index 5c279162..fbe23470 100644 --- a/src/userstream.rs +++ b/src/userstream.rs @@ -1,8 +1,8 @@ -use crate::model::{Success, UserDataStream}; +use crate::api::Spot; +use crate::api::API; use crate::client::Client; use crate::errors::Result; -use crate::api::API; -use crate::api::Spot; +use crate::model::{Success, UserDataStream}; #[derive(Clone)] pub struct UserStream { diff --git a/src/util.rs b/src/util.rs index 5761bf4c..ecb68875 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,8 @@ use crate::errors::Result; -use std::collections::BTreeMap; -use std::time::{SystemTime, UNIX_EPOCH}; use error_chain::bail; use serde_json::Value; +use std::collections::BTreeMap; +use std::time::{SystemTime, UNIX_EPOCH}; pub fn build_request(parameters: BTreeMap) -> String { let mut request = String::new(); @@ -15,13 +15,20 @@ pub fn build_request(parameters: BTreeMap) -> String { } pub fn build_signed_request( - parameters: BTreeMap, recv_window: u64, + parameters: BTreeMap, + recv_window: u64, ) -> Result { - build_signed_request_custom(parameters, recv_window, SystemTime::now()) + build_signed_request_custom( + parameters, + recv_window, + SystemTime::now(), + ) } pub fn build_signed_request_custom( - mut parameters: BTreeMap, recv_window: u64, start: SystemTime, + mut parameters: BTreeMap, + recv_window: u64, + start: SystemTime, ) -> Result { if recv_window > 0 { parameters.insert("recvWindow".into(), recv_window.to_string()); @@ -43,5 +50,6 @@ pub fn to_f64(v: &Value) -> f64 { fn get_timestamp(start: SystemTime) -> Result { let since_epoch = start.duration_since(UNIX_EPOCH)?; - Ok(since_epoch.as_secs() * 1000 + u64::from(since_epoch.subsec_nanos()) / 1_000_000) + Ok(since_epoch.as_secs() * 1000 + + u64::from(since_epoch.subsec_nanos()) / 1_000_000) } diff --git a/src/websockets.rs b/src/websockets.rs index 0ed7fb2f..98e18aee 100644 --- a/src/websockets.rs +++ b/src/websockets.rs @@ -1,19 +1,20 @@ -use crate::errors::Result; use crate::config::Config; +use crate::errors::Result; use crate::model::{ - AccountUpdateEvent, AggrTradesEvent, BalanceUpdateEvent, BookTickerEvent, DayTickerEvent, - DepthOrderBookEvent, KlineEvent, OrderBook, OrderTradeEvent, TradeEvent, + AccountUpdateEvent, AggrTradesEvent, BalanceUpdateEvent, + BookTickerEvent, DayTickerEvent, DepthOrderBookEvent, KlineEvent, + OrderBook, OrderTradeEvent, TradeEvent, }; use error_chain::bail; -use url::Url; use serde::{Deserialize, Serialize}; +use url::Url; -use std::sync::atomic::{AtomicBool, Ordering}; use std::net::TcpStream; -use tungstenite::{connect, Message}; +use std::sync::atomic::{AtomicBool, Ordering}; +use tungstenite::handshake::client::Response; use tungstenite::protocol::WebSocket; use tungstenite::stream::MaybeTlsStream; -use tungstenite::handshake::client::Response; +use tungstenite::{connect, Message}; #[allow(clippy::all)] enum WebsocketAPI { @@ -25,12 +26,17 @@ enum WebsocketAPI { impl WebsocketAPI { fn params(self, subscription: &str) -> String { match self { - WebsocketAPI::Default => format!("wss://stream.binance.com:9443/ws/{}", subscription), + WebsocketAPI::Default => format!( + "wss://stream.binance.com:9443/ws/{}", + subscription + ), WebsocketAPI::MultiStream => format!( "wss://stream.binance.com:9443/stream?streams={}", subscription ), - WebsocketAPI::Custom(url) => format!("{}/{}", url, subscription), + WebsocketAPI::Custom(url) => { + format!("{}/{}", url, subscription) + } } } } @@ -52,7 +58,8 @@ pub enum WebsocketEvent { } pub struct WebSockets<'a> { - pub socket: Option<(WebSocket>, Response)>, + pub socket: + Option<(WebSocket>, Response)>, handler: Box Result<()> + 'a>, } @@ -87,12 +94,24 @@ impl<'a> WebSockets<'a> { self.connect_wss(&WebsocketAPI::Default.params(subscription)) } - pub fn connect_with_config(&mut self, subscription: &str, config: &Config) -> Result<()> { - self.connect_wss(&WebsocketAPI::Custom(config.ws_endpoint.clone()).params(subscription)) + pub fn connect_with_config( + &mut self, + subscription: &str, + config: &Config, + ) -> Result<()> { + self.connect_wss( + &WebsocketAPI::Custom(config.ws_endpoint.clone()) + .params(subscription), + ) } - pub fn connect_multiple_streams(&mut self, endpoints: &[String]) -> Result<()> { - self.connect_wss(&WebsocketAPI::MultiStream.params(&endpoints.join("/"))) + pub fn connect_multiple_streams( + &mut self, + endpoints: &[String], + ) -> Result<()> { + self.connect_wss( + &WebsocketAPI::MultiStream.params(&endpoints.join("/")), + ) } fn connect_wss(&mut self, wss: &str) -> Result<()> { @@ -129,16 +148,30 @@ impl<'a> WebSockets<'a> { if let Ok(events) = serde_json::from_value::(value) { let action = match events { Events::Vec(v) => WebsocketEvent::DayTickerAll(v), - Events::BookTickerEvent(v) => WebsocketEvent::BookTicker(v), - Events::BalanceUpdateEvent(v) => WebsocketEvent::BalanceUpdate(v), - Events::AccountUpdateEvent(v) => WebsocketEvent::AccountUpdate(v), - Events::OrderTradeEvent(v) => WebsocketEvent::OrderTrade(v), - Events::AggrTradesEvent(v) => WebsocketEvent::AggrTrades(v), + Events::BookTickerEvent(v) => { + WebsocketEvent::BookTicker(v) + } + Events::BalanceUpdateEvent(v) => { + WebsocketEvent::BalanceUpdate(v) + } + Events::AccountUpdateEvent(v) => { + WebsocketEvent::AccountUpdate(v) + } + Events::OrderTradeEvent(v) => { + WebsocketEvent::OrderTrade(v) + } + Events::AggrTradesEvent(v) => { + WebsocketEvent::AggrTrades(v) + } Events::TradeEvent(v) => WebsocketEvent::Trade(v), - Events::DayTickerEvent(v) => WebsocketEvent::DayTicker(v), + Events::DayTickerEvent(v) => { + WebsocketEvent::DayTicker(v) + } Events::KlineEvent(v) => WebsocketEvent::Kline(v), Events::OrderBook(v) => WebsocketEvent::OrderBook(v), - Events::DepthOrderBookEvent(v) => WebsocketEvent::DepthOrderBook(v), + Events::DepthOrderBookEvent(v) => { + WebsocketEvent::DepthOrderBook(v) + } }; (self.handler)(action)?; } @@ -152,14 +185,24 @@ impl<'a> WebSockets<'a> { match message { Message::Text(msg) => { if let Err(e) = self.handle_msg(&msg) { - bail!(format!("Error on handling stream message: {}", e)); + bail!(format!( + "Error on handling stream message: {}", + e + )); } } Message::Ping(_) => { - socket.0.write_message(Message::Pong(vec![])).unwrap(); + socket + .0 + .write_message(Message::Pong(vec![])) + .unwrap(); + } + Message::Pong(_) + | Message::Binary(_) + | Message::Frame(_) => (), + Message::Close(e) => { + bail!(format!("Disconnected {:?}", e)) } - Message::Pong(_) | Message::Binary(_) | Message::Frame(_) => (), - Message::Close(e) => bail!(format!("Disconnected {:?}", e)), } } } diff --git a/tests/account_tests.rs b/tests/account_tests.rs index 94f08390..407669c5 100755 --- a/tests/account_tests.rs +++ b/tests/account_tests.rs @@ -1,19 +1,22 @@ +use binance::account::*; use binance::api::*; use binance::config::*; -use binance::account::*; use binance::model::*; #[cfg(test)] mod tests { use super::*; - use mockito::{mock, Matcher}; use float_cmp::*; + use mockito::{mock, Matcher}; #[test] fn get_account() { let mock_get_account = mock("GET", "/api/v3/account") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex( "recvWindow=1234×tamp=\\d+&signature=.*".into(), )) @@ -23,16 +26,37 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let account = account.get_account().unwrap(); mock_get_account.assert(); - assert!(approx_eq!(f32, account.maker_commission, 15.0, ulps = 2)); - assert!(approx_eq!(f32, account.taker_commission, 15.0, ulps = 2)); - assert!(approx_eq!(f32, account.buyer_commission, 0.0, ulps = 2)); - assert!(approx_eq!(f32, account.seller_commission, 0.0, ulps = 2)); + assert!(approx_eq!( + f32, + account.maker_commission, + 15.0, + ulps = 2 + )); + assert!(approx_eq!( + f32, + account.taker_commission, + 15.0, + ulps = 2 + )); + assert!(approx_eq!( + f32, + account.buyer_commission, + 0.0, + ulps = 2 + )); + assert!(approx_eq!( + f32, + account.seller_commission, + 0.0, + ulps = 2 + )); assert!(account.can_trade); assert!(account.can_withdraw); assert!(account.can_deposit); @@ -53,7 +77,10 @@ mod tests { #[test] fn get_balance() { let mock_get_account = mock("GET", "/api/v3/account") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex( "recvWindow=1234×tamp=\\d+&signature=.*".into(), )) @@ -63,7 +90,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let balance = account.get_balance("BTC").unwrap(); @@ -77,17 +105,23 @@ mod tests { #[test] fn get_open_orders() { let mock_open_orders = mock("GET", "/api/v3/openOrders") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex( "recvWindow=1234&symbol=LTCBTC×tamp=\\d+".into(), )) - .with_body_from_file("tests/mocks/account/get_open_orders.json") + .with_body_from_file( + "tests/mocks/account/get_open_orders.json", + ) .create(); let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let open_orders = account.get_open_orders("LTCBTC").unwrap(); @@ -119,15 +153,23 @@ mod tests { #[test] fn get_all_open_orders() { let mock_open_orders = mock("GET", "/api/v3/openOrders") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("recvWindow=1234×tamp=\\d+".into())) - .with_body_from_file("tests/mocks/account/get_open_orders.json") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex( + "recvWindow=1234×tamp=\\d+".into(), + )) + .with_body_from_file( + "tests/mocks/account/get_open_orders.json", + ) .create(); let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let open_orders = account.get_all_open_orders().unwrap(); @@ -158,26 +200,36 @@ mod tests { #[test] fn cancel_all_open_orders() { - let mock_cancel_all_open_orders = mock("DELETE", "/api/v3/openOrders") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex( - "recvWindow=1234&symbol=BTCUSDT×tamp=\\d+".into(), - )) - .with_body_from_file("tests/mocks/account/cancel_all_open_orders.json") - .create(); + let mock_cancel_all_open_orders = + mock("DELETE", "/api/v3/openOrders") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex( + "recvWindow=1234&symbol=BTCUSDT×tamp=\\d+" + .into(), + )) + .with_body_from_file( + "tests/mocks/account/cancel_all_open_orders.json", + ) + .create(); let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let cancel_all_open_orders = account.cancel_all_open_orders("BTCUSDT").unwrap(); + let cancel_all_open_orders = + account.cancel_all_open_orders("BTCUSDT").unwrap(); mock_cancel_all_open_orders.assert(); assert!(cancel_all_open_orders.len() == 3); - let first_order_cancelled: OrderCanceled = cancel_all_open_orders[0].clone(); + let first_order_cancelled: OrderCanceled = + cancel_all_open_orders[0].clone(); assert_eq!(first_order_cancelled.symbol, "BTCUSDT"); assert_eq!( first_order_cancelled.orig_client_order_id.unwrap(), @@ -189,7 +241,8 @@ mod tests { "pXLV6Hz6mprAcVYpVMTGgx" ); - let second_order_cancelled: OrderCanceled = cancel_all_open_orders[1].clone(); + let second_order_cancelled: OrderCanceled = + cancel_all_open_orders[1].clone(); assert_eq!(second_order_cancelled.symbol, "BTCUSDT"); assert_eq!( second_order_cancelled.orig_client_order_id.unwrap(), @@ -215,9 +268,11 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let order_status: Order = account.order_status("LTCBTC", 1).unwrap(); + let order_status: Order = + account.order_status("LTCBTC", 1).unwrap(); mock_order_status.assert(); @@ -233,7 +288,12 @@ mod tests { assert_eq!(order_status.time_in_force, "GTC"); //Migrate to TimeInForce enum assert_eq!(order_status.type_name, "LIMIT"); assert_eq!(order_status.side, "BUY"); - assert!(approx_eq!(f64, order_status.stop_price, 0.0, ulps = 2)); + assert!(approx_eq!( + f64, + order_status.stop_price, + 0.0, + ulps = 2 + )); assert_eq!(order_status.iceberg_qty, "0.0"); assert_eq!(order_status.time, 1499827319559); assert_eq!(order_status.update_time, 1499827319559); @@ -254,7 +314,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_order_status("LTCBTC", 1).unwrap(); @@ -272,20 +333,30 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.limit_buy("LTCBTC", 1, 0.1).unwrap(); + let transaction: Transaction = + account.limit_buy("LTCBTC", 1, 0.1).unwrap(); mock_limit_buy.assert(); assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, @@ -309,7 +380,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_limit_buy("LTCBTC", 1, 0.1).unwrap(); @@ -327,20 +399,30 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.limit_sell("LTCBTC", 1, 0.1).unwrap(); + let transaction: Transaction = + account.limit_sell("LTCBTC", 1, 0.1).unwrap(); mock_limit_sell.assert(); assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, @@ -364,7 +446,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_limit_sell("LTCBTC", 1, 0.1).unwrap(); @@ -385,20 +468,30 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.market_buy("LTCBTC", 1).unwrap(); + let transaction: Transaction = + account.market_buy("LTCBTC", 1).unwrap(); mock_market_buy.assert(); assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, @@ -425,7 +518,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_market_buy("LTCBTC", 1).unwrap(); @@ -443,7 +537,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); match account.market_buy_using_quote_quantity("BNBBTC", 0.002) { Ok(answer) => { @@ -466,7 +561,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account .test_market_buy_using_quote_quantity("BNBBTC", 0.002) @@ -489,20 +585,30 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.market_sell("LTCBTC", 1).unwrap(); + let transaction: Transaction = + account.market_sell("LTCBTC", 1).unwrap(); mock_market_sell.assert(); assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, @@ -529,7 +635,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_market_sell("LTCBTC", 1).unwrap(); @@ -547,9 +654,11 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - match account.market_sell_using_quote_quantity("BNBBTC", 0.002) { + match account.market_sell_using_quote_quantity("BNBBTC", 0.002) + { Ok(answer) => { assert!(answer.order_id == 1); } @@ -570,7 +679,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account .test_market_sell_using_quote_quantity("BNBBTC", 0.002) @@ -590,10 +700,17 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let transaction: Transaction = account - .stop_limit_buy_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC) + .stop_limit_buy_order( + "LTCBTC", + 1, + 0.1, + 0.09, + TimeInForce::GTC, + ) .unwrap(); mock_stop_limit_buy_order.assert(); @@ -601,18 +718,31 @@ mod tests { assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, 0.0, ulps = 2 )); - assert!(approx_eq!(f64, transaction.stop_price, 0.09, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.stop_price, + 0.09, + ulps = 2 + )); assert_eq!(transaction.status, "NEW"); assert_eq!(transaction.time_in_force, "GTC"); //Migrate to TimeInForce enum assert_eq!(transaction.type_name, "STOP_LOSS_LIMIT"); @@ -630,10 +760,17 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account - .test_stop_limit_buy_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC) + .test_stop_limit_buy_order( + "LTCBTC", + 1, + 0.1, + 0.09, + TimeInForce::GTC, + ) .unwrap(); mock_test_stop_limit_buy_order.assert(); @@ -650,10 +787,17 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let transaction: Transaction = account - .stop_limit_sell_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC) + .stop_limit_sell_order( + "LTCBTC", + 1, + 0.1, + 0.09, + TimeInForce::GTC, + ) .unwrap(); mock_stop_limit_sell_order.assert(); @@ -661,18 +805,31 @@ mod tests { assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, 0.0, ulps = 2 )); - assert!(approx_eq!(f64, transaction.stop_price, 0.09, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.stop_price, + 0.09, + ulps = 2 + )); assert_eq!(transaction.status, "NEW"); assert_eq!(transaction.time_in_force, "GTC"); //Migrate to TimeInForce enum assert_eq!(transaction.type_name, "STOP_LOSS_LIMIT"); @@ -690,10 +847,17 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account - .test_stop_limit_sell_order("LTCBTC", 1, 0.1, 0.09, TimeInForce::GTC) + .test_stop_limit_sell_order( + "LTCBTC", + 1, + 0.1, + 0.09, + TimeInForce::GTC, + ) .unwrap(); mock_test_stop_limit_sell_order.assert(); @@ -710,7 +874,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let transaction: Transaction = account .custom_order( @@ -730,18 +895,31 @@ mod tests { assert_eq!(transaction.symbol, "LTCBTC"); assert_eq!(transaction.order_id, 1); assert_eq!(transaction.order_list_id.unwrap(), -1); - assert_eq!(transaction.client_order_id, "6gCrw2kRUAF9CvJDGP16IP"); + assert_eq!( + transaction.client_order_id, + "6gCrw2kRUAF9CvJDGP16IP" + ); assert_eq!(transaction.transact_time, 1507725176595); assert!(approx_eq!(f64, transaction.price, 0.1, ulps = 2)); assert!(approx_eq!(f64, transaction.orig_qty, 1.0, ulps = 2)); - assert!(approx_eq!(f64, transaction.executed_qty, 1.0, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.executed_qty, + 1.0, + ulps = 2 + )); assert!(approx_eq!( f64, transaction.cummulative_quote_qty, 0.0, ulps = 2 )); - assert!(approx_eq!(f64, transaction.stop_price, 0.09, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.stop_price, + 0.09, + ulps = 2 + )); assert_eq!(transaction.status, "NEW"); assert_eq!(transaction.time_in_force, "GTC"); //Migrate to TimeInForce enum assert_eq!(transaction.type_name, "STOP_LOSS_LIMIT"); @@ -759,7 +937,8 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account .test_custom_order( @@ -790,32 +969,45 @@ mod tests { let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let cancelled_order = account.cancel_order("BTCUSDT", 1).unwrap(); + let cancelled_order = + account.cancel_order("BTCUSDT", 1).unwrap(); mock_cancel_order.assert(); assert_eq!(cancelled_order.symbol, "LTCBTC"); - assert_eq!(cancelled_order.orig_client_order_id.unwrap(), "myOrder1"); + assert_eq!( + cancelled_order.orig_client_order_id.unwrap(), + "myOrder1" + ); assert_eq!(cancelled_order.order_id.unwrap(), 4); - assert_eq!(cancelled_order.client_order_id.unwrap(), "cancelMyOrder1"); + assert_eq!( + cancelled_order.client_order_id.unwrap(), + "cancelMyOrder1" + ); } #[test] fn test_cancel_order() { - let mock_test_cancel_order = mock("DELETE", "/api/v3/order/test") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex( - "orderId=1&recvWindow=1234&symbol=BTCUSDT×tamp=\\d+".into(), - )) - .with_body_from_file("tests/mocks/account/cancel_order.json") - .create(); + let mock_test_cancel_order = mock( + "DELETE", + "/api/v3/order/test", + ) + .with_header("content-type", "application/json;charset=UTF-8") + .match_query(Matcher::Regex( + "orderId=1&recvWindow=1234&symbol=BTCUSDT×tamp=\\d+" + .into(), + )) + .with_body_from_file("tests/mocks/account/cancel_order.json") + .create(); let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.test_cancel_order("BTCUSDT", 1).unwrap(); @@ -825,17 +1017,23 @@ mod tests { #[test] fn trade_history() { let mock_trade_history = mock("GET", "/api/v3/myTrades") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex( "recvWindow=1234&symbol=BTCUSDT×tamp=\\d+".into(), )) - .with_body_from_file("tests/mocks/account/trade_history.json") + .with_body_from_file( + "tests/mocks/account/trade_history.json", + ) .create(); let config = Config::default() .set_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let histories = account.trade_history("BTCUSDT").unwrap(); @@ -867,11 +1065,19 @@ mod tests { .create(); // Set up the second mock server to respond to the accept quote request - let mock_accept_quote = mock("POST", "/sapi/v1/convert/acceptQuote") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("quoteId=12415572564".into())) - .with_body_from_file("tests/mocks/account/accept_quote.json") - .create(); + let mock_accept_quote = + mock("POST", "/sapi/v1/convert/acceptQuote") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex( + "quoteId=12415572564".into(), + )) + .with_body_from_file( + "tests/mocks/account/accept_quote.json", + ) + .create(); // Configure the Binance API client with the mock server's URL let config = Config::default() @@ -879,11 +1085,13 @@ mod tests { .set_recv_window(1234); // Create a new Binance API client using the mock server's URL - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); // Call the convert function and assert that the returned QuoteResponse object matches the expected values - let convert_response = account.convert("BTC", "ETH", QtyType::From(1)).unwrap(); + let convert_response = + account.convert("BTC", "ETH", QtyType::From(1)).unwrap(); assert_eq!(convert_response.order_status, "PROCESS"); @@ -894,14 +1102,20 @@ mod tests { #[test] fn test_daily_account_snapshot() { - let mock_server: mockito::Mock = mock("GET", "/sapi/v1/accountSnapshot") - .with_header("content-type", "application/json;charset=UTF-8") - // the test only works with this parameters - .match_query(Matcher::Regex( - "recvWindow=1234×tamp=\\d+&type=SPOT".into(), - )) - .with_body_from_file("tests/mocks/account/daily_account_snapshot.json") - .create(); + let mock_server: mockito::Mock = + mock("GET", "/sapi/v1/accountSnapshot") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + // the test only works with this parameters + .match_query(Matcher::Regex( + "recvWindow=1234×tamp=\\d+&type=SPOT".into(), + )) + .with_body_from_file( + "tests/mocks/account/daily_account_snapshot.json", + ) + .create(); // config of mock server's URL let config = Config::default() @@ -909,10 +1123,12 @@ mod tests { .set_recv_window(1234); // binance client using the mock server's URL - let account: Account = Binance::new_with_config(None, None, &config); + let account: Account = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let convert_response = account.daily_account_snapshot().expect("erro merda"); + let convert_response = + account.daily_account_snapshot().expect("erro merda"); assert_eq!( convert_response.snapshot_vos[0].data.total_asset_of_btc, diff --git a/tests/futures_account_tests.rs b/tests/futures_account_tests.rs index 895a2955..ec6a2a33 100644 --- a/tests/futures_account_tests.rs +++ b/tests/futures_account_tests.rs @@ -5,10 +5,10 @@ use binance::futures::account::*; #[cfg(test)] mod tests { use super::*; - use mockito::{mock, Matcher}; - use float_cmp::*; use binance::account::OrderSide; use binance::futures::model::Transaction; + use float_cmp::*; + use mockito::{mock, Matcher}; #[test] fn change_initial_leverage() { @@ -23,9 +23,11 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let response = account.change_initial_leverage("LTCUSDT", 2).unwrap(); + let response = + account.change_initial_leverage("LTCUSDT", 2).unwrap(); mock_change_leverage.assert(); @@ -52,7 +54,8 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.cancel_all_open_orders("BTCUSDT").unwrap(); @@ -72,7 +75,8 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); account.change_position_mode(true).unwrap(); @@ -90,9 +94,11 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.stop_market_close_buy("SRMUSDT", 10.5).unwrap(); + let transaction: Transaction = + account.stop_market_close_buy("SRMUSDT", 10.5).unwrap(); mock_stop_market_close_sell.assert(); @@ -100,7 +106,12 @@ mod tests { assert_eq!(transaction.side, "BUY"); assert_eq!(transaction.orig_type, "STOP_MARKET"); assert!(transaction.close_position); - assert!(approx_eq!(f64, transaction.stop_price, 10.5, ulps = 2)); + assert!(approx_eq!( + f64, + transaction.stop_price, + 10.5, + ulps = 2 + )); } #[test] @@ -114,9 +125,11 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); - let transaction: Transaction = account.stop_market_close_sell("SRMUSDT", 7.4).unwrap(); + let transaction: Transaction = + account.stop_market_close_sell("SRMUSDT", 7.4).unwrap(); mock_stop_market_close_sell.assert(); @@ -138,7 +151,8 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let custom_order = CustomOrderRequest { symbol: "SRMUSDT".into(), @@ -156,7 +170,8 @@ mod tests { working_type: None, price_protect: None, }; - let transaction: Transaction = account.custom_order(custom_order).unwrap(); + let transaction: Transaction = + account.custom_order(custom_order).unwrap(); mock_custom_order.assert(); @@ -182,7 +197,8 @@ mod tests { let config = Config::default() .set_futures_rest_api_endpoint(mockito::server_url()) .set_recv_window(1234); - let account: FuturesAccount = Binance::new_with_config(None, None, &config); + let account: FuturesAccount = + Binance::new_with_config(None, None, &config); let _ = env_logger::try_init(); let income_request = IncomeRequest { symbol: Some("BTCUSDT".into()), diff --git a/tests/futures_market_test.rs b/tests/futures_market_test.rs index c11c9e41..3a6bdeb4 100644 --- a/tests/futures_market_test.rs +++ b/tests/futures_market_test.rs @@ -10,14 +10,23 @@ mod tests { #[test] fn open_interest_statistics() { - let mock_open_interest_statistics = mock("GET", "/futures/data/openInterestHist") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("limit=10&period=5m&symbol=BTCUSDT".into())) - .with_body_from_file("tests/mocks/futures/market/open_interest_statistics.json") - .create(); + let mock_open_interest_statistics = mock( + "GET", + "/futures/data/openInterestHist", + ) + .with_header("content-type", "application/json;charset=UTF-8") + .match_query(Matcher::Regex( + "limit=10&period=5m&symbol=BTCUSDT".into(), + )) + .with_body_from_file( + "tests/mocks/futures/market/open_interest_statistics.json", + ) + .create(); - let config = Config::default().set_futures_rest_api_endpoint(mockito::server_url()); - let market: FuturesMarket = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_futures_rest_api_endpoint(mockito::server_url()); + let market: FuturesMarket = + Binance::new_with_config(None, None, &config); let open_interest_hists = market .open_interest_statistics("BTCUSDT", "5m", 10, None, None) diff --git a/tests/general_tests.rs b/tests/general_tests.rs index ca667aff..a9d23737 100755 --- a/tests/general_tests.rs +++ b/tests/general_tests.rs @@ -6,18 +6,23 @@ use binance::model::*; #[cfg(test)] mod tests { use super::*; - use mockito::mock; use float_cmp::*; + use mockito::mock; #[test] fn ping() { let mock_ping = mock("GET", "/api/v3/ping") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .with_body("{}") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let general: General = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let general: General = + Binance::new_with_config(None, None, &config); let pong = general.ping().unwrap(); mock_ping.assert(); @@ -28,12 +33,17 @@ mod tests { #[test] fn get_server_time() { let mock_server_time = mock("GET", "/api/v3/time") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .with_body_from_file("tests/mocks/general/server_time.json") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let general: General = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let general: General = + Binance::new_with_config(None, None, &config); let server_time = general.get_server_time().unwrap(); mock_server_time.assert(); @@ -44,12 +54,19 @@ mod tests { #[test] fn exchange_info() { let mock_exchange_info = mock("GET", "/api/v3/exchangeInfo") - .with_header("content-type", "application/json;charset=UTF-8") - .with_body_from_file("tests/mocks/general/exchange_info.json") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .with_body_from_file( + "tests/mocks/general/exchange_info.json", + ) .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let general: General = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let general: General = + Binance::new_with_config(None, None, &config); let exchange_info = general.exchange_info().unwrap(); mock_exchange_info.assert(); @@ -60,12 +77,19 @@ mod tests { #[test] fn get_symbol_info() { let mock_exchange_info = mock("GET", "/api/v3/exchangeInfo") - .with_header("content-type", "application/json;charset=UTF-8") - .with_body_from_file("tests/mocks/general/exchange_info.json") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .with_body_from_file( + "tests/mocks/general/exchange_info.json", + ) .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let general: General = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let general: General = + Binance::new_with_config(None, None, &config); let symbol = general.get_symbol_info("BNBBTC").unwrap(); mock_exchange_info.assert(); @@ -108,7 +132,12 @@ mod tests { } => { assert_eq!(multiplier_up, "5"); assert_eq!(multiplier_down, "0.2"); - assert!(approx_eq!(f64, avg_price_mins.unwrap(), 5.0, ulps = 2)); + assert!(approx_eq!( + f64, + avg_price_mins.unwrap(), + 5.0, + ulps = 2 + )); } Filters::LotSize { min_qty, @@ -128,7 +157,12 @@ mod tests { assert!(notional.is_none()); assert_eq!(min_notional.unwrap(), "0.00010000"); assert!(apply_to_market.unwrap()); - assert!(approx_eq!(f64, avg_price_mins.unwrap(), 5.0, ulps = 2)); + assert!(approx_eq!( + f64, + avg_price_mins.unwrap(), + 5.0, + ulps = 2 + )); } Filters::IcebergParts { limit } => { assert_eq!(limit.unwrap(), 10); diff --git a/tests/market_tests.rs b/tests/market_tests.rs index 126b2423..26372829 100644 --- a/tests/market_tests.rs +++ b/tests/market_tests.rs @@ -6,54 +6,79 @@ use binance::model::*; #[cfg(test)] mod tests { use super::*; - use mockito::{mock, Matcher}; use float_cmp::*; + use mockito::{mock, Matcher}; #[test] fn get_depth() { let mock_get_depth = mock("GET", "/api/v3/depth") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex("symbol=LTCBTC".into())) .with_body_from_file("tests/mocks/market/get_depth.json") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let order_book = market.get_depth("LTCBTC").unwrap(); mock_get_depth.assert(); assert_eq!(order_book.last_update_id, 1027024); - assert_eq!(order_book.bids[0], Bids::new(4.00000000, 431.00000000)); + assert_eq!( + order_book.bids[0], + Bids::new(4.00000000, 431.00000000) + ); } #[test] fn get_custom_depth() { let mock_get_custom_depth = mock("GET", "/api/v3/depth") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("limit=10&symbol=LTCBTC".into())) + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex( + "limit=10&symbol=LTCBTC".into(), + )) .with_body_from_file("tests/mocks/market/get_depth.json") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let order_book = market.get_custom_depth("LTCBTC", 10).unwrap(); mock_get_custom_depth.assert(); assert_eq!(order_book.last_update_id, 1027024); - assert_eq!(order_book.bids[0], Bids::new(4.00000000, 431.00000000)); + assert_eq!( + order_book.bids[0], + Bids::new(4.00000000, 431.00000000) + ); } #[test] fn get_all_prices() { let mock_get_all_prices = mock("GET", "/api/v3/ticker/price") - .with_header("content-type", "application/json;charset=UTF-8") - .with_body_from_file("tests/mocks/market/get_all_prices.json") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .with_body_from_file( + "tests/mocks/market/get_all_prices.json", + ) .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let prices: Prices = market.get_all_prices().unwrap(); mock_get_all_prices.assert(); @@ -63,10 +88,20 @@ mod tests { assert!(!symbols.is_empty()); let first_symbol = symbols[0].clone(); assert_eq!(first_symbol.symbol, "LTCBTC"); - assert!(approx_eq!(f64, first_symbol.price, 4.00000200, ulps = 2)); + assert!(approx_eq!( + f64, + first_symbol.price, + 4.00000200, + ulps = 2 + )); let second_symbol = symbols[1].clone(); assert_eq!(second_symbol.symbol, "ETHBTC"); - assert!(approx_eq!(f64, second_symbol.price, 0.07946600, ulps = 2)); + assert!(approx_eq!( + f64, + second_symbol.price, + 0.07946600, + ulps = 2 + )); } } } @@ -74,13 +109,18 @@ mod tests { #[test] fn get_price() { let mock_get_price = mock("GET", "/api/v3/ticker/price") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex("symbol=LTCBTC".into())) .with_body_from_file("tests/mocks/market/get_price.json") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let symbol = market.get_price("LTCBTC").unwrap(); mock_get_price.assert(); @@ -92,13 +132,20 @@ mod tests { #[test] fn get_average_price() { let mock_get_average_price = mock("GET", "/api/v3/avgPrice") - .with_header("content-type", "application/json;charset=UTF-8") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) .match_query(Matcher::Regex("symbol=LTCBTC".into())) - .with_body_from_file("tests/mocks/market/get_average_price.json") + .with_body_from_file( + "tests/mocks/market/get_average_price.json", + ) .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let symbol = market.get_average_price("LTCBTC").unwrap(); mock_get_average_price.assert(); @@ -109,13 +156,21 @@ mod tests { #[test] fn get_all_book_tickers() { - let mock_get_all_book_tickers = mock("GET", "/api/v3/ticker/bookTicker") - .with_header("content-type", "application/json;charset=UTF-8") - .with_body_from_file("tests/mocks/market/get_all_book_tickers.json") - .create(); - - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let mock_get_all_book_tickers = + mock("GET", "/api/v3/ticker/bookTicker") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .with_body_from_file( + "tests/mocks/market/get_all_book_tickers.json", + ) + .create(); + + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let book_tickers = market.get_all_book_tickers().unwrap(); mock_get_all_book_tickers.assert(); @@ -143,7 +198,12 @@ mod tests { 4.00000200, ulps = 2 )); - assert!(approx_eq!(f64, first_ticker.ask_qty, 9.00000000, ulps = 2)); + assert!(approx_eq!( + f64, + first_ticker.ask_qty, + 9.00000000, + ulps = 2 + )); let second_ticker = tickers[1].clone(); assert_eq!(second_ticker.symbol, "ETHBTC"); assert!(approx_eq!( @@ -152,7 +212,12 @@ mod tests { 0.07946700, ulps = 2 )); - assert!(approx_eq!(f64, second_ticker.bid_qty, 9.00000000, ulps = 2)); + assert!(approx_eq!( + f64, + second_ticker.bid_qty, + 9.00000000, + ulps = 2 + )); assert!(approx_eq!( f64, second_ticker.ask_price, @@ -171,35 +236,71 @@ mod tests { #[test] fn get_book_ticker() { - let mock_get_book_ticker = mock("GET", "/api/v3/ticker/bookTicker") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("symbol=LTCBTC".into())) - .with_body_from_file("tests/mocks/market/get_book_ticker.json") - .create(); - - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let mock_get_book_ticker = + mock("GET", "/api/v3/ticker/bookTicker") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex("symbol=LTCBTC".into())) + .with_body_from_file( + "tests/mocks/market/get_book_ticker.json", + ) + .create(); + + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let book_ticker = market.get_book_ticker("LTCBTC").unwrap(); mock_get_book_ticker.assert(); assert_eq!(book_ticker.symbol, "LTCBTC"); - assert!(approx_eq!(f64, book_ticker.bid_price, 4.00000000, ulps = 2)); - assert!(approx_eq!(f64, book_ticker.bid_qty, 431.00000000, ulps = 2)); - assert!(approx_eq!(f64, book_ticker.ask_price, 4.00000200, ulps = 2)); - assert!(approx_eq!(f64, book_ticker.ask_qty, 9.00000000, ulps = 2)); + assert!(approx_eq!( + f64, + book_ticker.bid_price, + 4.00000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + book_ticker.bid_qty, + 431.00000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + book_ticker.ask_price, + 4.00000200, + ulps = 2 + )); + assert!(approx_eq!( + f64, + book_ticker.ask_qty, + 9.00000000, + ulps = 2 + )); } #[test] fn get_24h_price_stats() { - let mock_get_24h_price_stats = mock("GET", "/api/v3/ticker/24hr") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("symbol=BNBBTC".into())) - .with_body_from_file("tests/mocks/market/get_24h_price_stats.json") - .create(); - - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let mock_get_24h_price_stats = + mock("GET", "/api/v3/ticker/24hr") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex("symbol=BNBBTC".into())) + .with_body_from_file( + "tests/mocks/market/get_24h_price_stats.json", + ) + .create(); + + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let price_stats = market.get_24h_price_stats("BNBBTC").unwrap(); mock_get_24h_price_stats.assert(); @@ -220,8 +321,18 @@ mod tests { 4.00000200, ulps = 2 )); - assert!(approx_eq!(f64, price_stats.bid_price, 4.00000000, ulps = 2)); - assert!(approx_eq!(f64, price_stats.ask_price, 4.00000200, ulps = 2)); + assert!(approx_eq!( + f64, + price_stats.bid_price, + 4.00000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + price_stats.ask_price, + 4.00000200, + ulps = 2 + )); assert!(approx_eq!( f64, price_stats.open_price, @@ -234,8 +345,18 @@ mod tests { 100.00000000, ulps = 2 )); - assert!(approx_eq!(f64, price_stats.low_price, 0.10000000, ulps = 2)); - assert!(approx_eq!(f64, price_stats.volume, 8913.30000000, ulps = 2)); + assert!(approx_eq!( + f64, + price_stats.low_price, + 0.10000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + price_stats.volume, + 8913.30000000, + ulps = 2 + )); assert_eq!(price_stats.open_time, 1499783499040); assert_eq!(price_stats.close_time, 1499869899040); assert_eq!(price_stats.first_id, 28385); @@ -245,13 +366,21 @@ mod tests { #[test] fn get_all_24h_price_stats() { - let mock_get_all_24h_price_stats = mock("GET", "/api/v3/ticker/24hr") - .with_header("content-type", "application/json;charset=UTF-8") - .with_body_from_file("tests/mocks/market/get_all_24h_price_stats.json") - .create(); - - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let mock_get_all_24h_price_stats = + mock("GET", "/api/v3/ticker/24hr") + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .with_body_from_file( + "tests/mocks/market/get_all_24h_price_stats.json", + ) + .create(); + + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); let prices_stats = market.get_all_24h_price_stats().unwrap(); mock_get_all_24h_price_stats.assert(); @@ -276,8 +405,18 @@ mod tests { 4.00000200, ulps = 2 )); - assert!(approx_eq!(f64, price_stats.bid_price, 4.00000000, ulps = 2)); - assert!(approx_eq!(f64, price_stats.ask_price, 4.00000200, ulps = 2)); + assert!(approx_eq!( + f64, + price_stats.bid_price, + 4.00000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + price_stats.ask_price, + 4.00000200, + ulps = 2 + )); assert!(approx_eq!( f64, price_stats.open_price, @@ -290,8 +429,18 @@ mod tests { 100.00000000, ulps = 2 )); - assert!(approx_eq!(f64, price_stats.low_price, 0.10000000, ulps = 2)); - assert!(approx_eq!(f64, price_stats.volume, 8913.30000000, ulps = 2)); + assert!(approx_eq!( + f64, + price_stats.low_price, + 0.10000000, + ulps = 2 + )); + assert!(approx_eq!( + f64, + price_stats.volume, + 8913.30000000, + ulps = 2 + )); assert_eq!(price_stats.open_time, 1499783499040); assert_eq!(price_stats.close_time, 1499869899040); assert_eq!(price_stats.first_id, 28385); @@ -302,19 +451,29 @@ mod tests { #[test] fn get_klines() { let mock_get_klines = mock("GET", "/api/v3/klines") - .with_header("content-type", "application/json;charset=UTF-8") - .match_query(Matcher::Regex("interval=5m&limit=10&symbol=LTCBTC".into())) + .with_header( + "content-type", + "application/json;charset=UTF-8", + ) + .match_query(Matcher::Regex( + "interval=5m&limit=10&symbol=LTCBTC".into(), + )) .with_body_from_file("tests/mocks/market/get_klines.json") .create(); - let config = Config::default().set_rest_api_endpoint(mockito::server_url()); - let market: Market = Binance::new_with_config(None, None, &config); + let config = Config::default() + .set_rest_api_endpoint(mockito::server_url()); + let market: Market = + Binance::new_with_config(None, None, &config); - let klines = market.get_klines("LTCBTC", "5m", 10, None, None).unwrap(); + let klines = + market.get_klines("LTCBTC", "5m", 10, None, None).unwrap(); mock_get_klines.assert(); match klines { - binance::model::KlineSummaries::AllKlineSummaries(klines) => { + binance::model::KlineSummaries::AllKlineSummaries( + klines, + ) => { assert!(!klines.is_empty()); let kline: KlineSummary = klines[0].clone(); @@ -327,8 +486,14 @@ mod tests { assert_eq!(kline.close_time, 1499644799999); assert_eq!(kline.quote_asset_volume, "2434.19055334"); assert_eq!(kline.number_of_trades, 308); - assert_eq!(kline.taker_buy_base_asset_volume, "1756.87402397"); - assert_eq!(kline.taker_buy_quote_asset_volume, "28.46694368"); + assert_eq!( + kline.taker_buy_base_asset_volume, + "1756.87402397" + ); + assert_eq!( + kline.taker_buy_quote_asset_volume, + "28.46694368" + ); } } } diff --git a/tests/util_tests.rs b/tests/util_tests.rs index 552992f3..62dc1f33 100644 --- a/tests/util_tests.rs +++ b/tests/util_tests.rs @@ -3,9 +3,9 @@ use binance::util::*; #[cfg(test)] mod tests { use super::*; + use float_cmp::*; use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; - use float_cmp::*; #[test] fn build_request_empty() { @@ -28,16 +28,23 @@ mod tests { let recv_window = 1234; let since_epoch = now.duration_since(UNIX_EPOCH).unwrap(); - let timestamp = - since_epoch.as_secs() * 1000 + u64::from(since_epoch.subsec_nanos()) / 1_000_000; + let timestamp = since_epoch.as_secs() * 1000 + + u64::from(since_epoch.subsec_nanos()) / 1_000_000; let parameters: BTreeMap = BTreeMap::new(); - let result = - binance::util::build_signed_request_custom(parameters, recv_window, now).unwrap(); + let result = binance::util::build_signed_request_custom( + parameters, + recv_window, + now, + ) + .unwrap(); assert_eq!( result, - format!("recvWindow={}×tamp={}", recv_window, timestamp) + format!( + "recvWindow={}×tamp={}", + recv_window, timestamp + ) ); } From 0891f3aa512e86ab582762b10955551baf37ff26 Mon Sep 17 00:00:00 2001 From: Felipe Viana <62855944+ulibte@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:53:30 +0000 Subject: [PATCH 17/17] fix doc test error --- .github/workflows/rust.yml | 9 +- .rustfmt.toml | 4 + Cargo.toml | 2 +- src/account.rs | 369 +++++++++++-------------------------- 4 files changed, 118 insertions(+), 266 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3659a6c7..5d9119bc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,6 +20,11 @@ jobs: override: true components: clippy - run: cargo clippy --all-targets +# - name: Clean Cargo cache and reinstall dependencies +# run: | +# cargo clean +# cargo update -p openssl-src + test: name: test @@ -30,7 +35,7 @@ jobs: include: - build: msrv os: ubuntu-latest - rust: 1.60.0 + rust: 1.56.1 - build: stable os: ubuntu-latest rust: stable @@ -69,4 +74,4 @@ jobs: - uses: actions-rs/cargo@v1 with: command: fmt - args: --all -- --check + args: --all -- --check \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..75872d27 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,4 @@ +binop_separator = "Back" +fn_args_layout = "Compressed" +newline_style = "Unix" +reorder_imports = false \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 77ccf480..d901ce52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ sha2 = "0.10.6" serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0" error-chain = { version = "0.12.4", default-features = false } -reqwest = { version = "0.11.18", features = ["blocking", "json"] } +reqwest = { version = "0.11.4", features = ["blocking", "json"] } tungstenite = { version = "0.19.0", features = ["native-tls"] } url = "2.2.2" diff --git a/src/account.rs b/src/account.rs index bdda537f..e8ff906d 100644 --- a/src/account.rs +++ b/src/account.rs @@ -4,8 +4,8 @@ use crate::api::{Convert, Sapi, Spot, API}; use crate::client::Client; use crate::errors::Result; use crate::model::{ - AccountInformation, AccountSnapshot, Balance, Empty, Order, - OrderCanceled, Quote, QuoteResponse, TradeHistory, Transaction, + AccountInformation, AccountSnapshot, Balance, Empty, Order, OrderCanceled, Quote, + QuoteResponse, TradeHistory, Transaction, }; use crate::util::build_signed_request; use std::collections::BTreeMap; @@ -49,10 +49,10 @@ pub enum ValidTime { TwoMinutes, } -///* "From" When specified, it is the amount you will be debited after the conversion -///* "To" When specified, it is the amount you will be credited after the conversion pub enum QtyType> { + ///* "From" When specified, it is the amount you will be debited after the conversion From(T), + ///* "To" When specified, it is the amount you will be credited after the conversion To(T), } @@ -124,8 +124,7 @@ impl Display for TimeInForce { impl Account { // Account Information pub fn get_account(&self) -> Result { - let request = - build_signed_request(BTreeMap::new(), self.recv_window)?; + let request = build_signed_request(BTreeMap::new(), self.recv_window)?; self.client .get_signed(API::Spot(Spot::Account), Some(request)) } @@ -157,8 +156,7 @@ impl Account { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::OpenOrders), Some(request)) } @@ -167,34 +165,25 @@ impl Account { pub fn get_all_open_orders(&self) -> Result> { let parameters: BTreeMap = BTreeMap::new(); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::OpenOrders), Some(request)) } // Cancel all open orders for a single symbol - pub fn cancel_all_open_orders( - &self, - symbol: S, - ) -> Result> + pub fn cancel_all_open_orders(&self, symbol: S) -> Result> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::OpenOrders), Some(request)) } // Check an order's status - pub fn order_status( - &self, - symbol: S, - order_id: u64, - ) -> Result + pub fn order_status(&self, symbol: S, order_id: u64) -> Result where S: Into, { @@ -202,8 +191,7 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::Order), Some(request)) } @@ -211,11 +199,7 @@ impl Account { /// Place a test status order /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_order_status( - &self, - symbol: S, - order_id: u64, - ) -> Result<()> + pub fn test_order_status(&self, symbol: S, order_id: u64) -> Result<()> where S: Into, { @@ -223,23 +207,14 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client - .get_signed::( - API::Spot(Spot::OrderTest), - Some(request), - ) + .get_signed::(API::Spot(Spot::OrderTest), Some(request)) .map(|_| ()) } // Place a LIMIT order - BUY - pub fn limit_buy( - &self, - symbol: S, - qty: F, - price: f64, - ) -> Result + pub fn limit_buy(&self, symbol: S, qty: F, price: f64) -> Result where S: Into, F: Into, @@ -262,12 +237,7 @@ impl Account { /// Place a test limit order - BUY /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_limit_buy( - &self, - symbol: S, - qty: F, - price: f64, - ) -> Result<()> + pub fn test_limit_buy(&self, symbol: S, qty: F, price: f64) -> Result<()> where S: Into, F: Into, @@ -290,12 +260,7 @@ impl Account { } // Place a LIMIT order - SELL - pub fn limit_sell( - &self, - symbol: S, - qty: F, - price: f64, - ) -> Result + pub fn limit_sell(&self, symbol: S, qty: F, price: f64) -> Result where S: Into, F: Into, @@ -318,12 +283,7 @@ impl Account { /// Place a test LIMIT order - SELL /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_limit_sell( - &self, - symbol: S, - qty: F, - price: f64, - ) -> Result<()> + pub fn test_limit_sell(&self, symbol: S, qty: F, price: f64) -> Result<()> where S: Into, F: Into, @@ -346,11 +306,7 @@ impl Account { } // Place a MARKET order - BUY - pub fn market_buy( - &self, - symbol: S, - qty: F, - ) -> Result + pub fn market_buy(&self, symbol: S, qty: F) -> Result where S: Into, F: Into, @@ -397,9 +353,7 @@ impl Account { // Place a MARKET order with quote quantity - BUY pub fn market_buy_using_quote_quantity( - &self, - symbol: S, - quote_order_qty: F, + &self, symbol: S, quote_order_qty: F, ) -> Result where S: Into, @@ -423,9 +377,7 @@ impl Account { /// /// This order is sandboxed: it is validated, but not sent to the matching engine. pub fn test_market_buy_using_quote_quantity( - &self, - symbol: S, - quote_order_qty: F, + &self, symbol: S, quote_order_qty: F, ) -> Result<()> where S: Into, @@ -448,11 +400,7 @@ impl Account { } // Place a MARKET order - SELL - pub fn market_sell( - &self, - symbol: S, - qty: F, - ) -> Result + pub fn market_sell(&self, symbol: S, qty: F) -> Result where S: Into, F: Into, @@ -475,11 +423,7 @@ impl Account { /// Place a test MARKET order - SELL /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_market_sell( - &self, - symbol: S, - qty: F, - ) -> Result<()> + pub fn test_market_sell(&self, symbol: S, qty: F) -> Result<()> where S: Into, F: Into, @@ -503,9 +447,7 @@ impl Account { // Place a MARKET order with quote quantity - SELL pub fn market_sell_using_quote_quantity( - &self, - symbol: S, - quote_order_qty: F, + &self, symbol: S, quote_order_qty: F, ) -> Result where S: Into, @@ -529,9 +471,7 @@ impl Account { /// /// This order is sandboxed: it is validated, but not sent to the matching engine. pub fn test_market_sell_using_quote_quantity( - &self, - symbol: S, - quote_order_qty: F, + &self, symbol: S, quote_order_qty: F, ) -> Result<()> where S: Into, @@ -568,12 +508,7 @@ impl Account { /// } /// ``` pub fn stop_limit_buy_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: f64, - time_in_force: TimeInForce, + &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, ) -> Result where S: Into, @@ -611,12 +546,7 @@ impl Account { /// } /// ``` pub fn test_stop_limit_buy_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: f64, - time_in_force: TimeInForce, + &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, ) -> Result<()> where S: Into, @@ -654,12 +584,7 @@ impl Account { /// } /// ``` pub fn stop_limit_sell_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: f64, - time_in_force: TimeInForce, + &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, ) -> Result where S: Into, @@ -697,12 +622,7 @@ impl Account { /// } /// ``` pub fn test_stop_limit_sell_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: f64, - time_in_force: TimeInForce, + &self, symbol: S, qty: F, price: f64, stop_price: f64, time_in_force: TimeInForce, ) -> Result<()> where S: Into, @@ -728,15 +648,8 @@ impl Account { /// Place a custom order #[allow(clippy::too_many_arguments)] pub fn custom_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: Option, - order_side: OrderSide, - order_type: OrderType, - time_in_force: TimeInForce, - new_client_order_id: Option, + &self, symbol: S, qty: F, price: f64, stop_price: Option, order_side: OrderSide, + order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option, ) -> Result where S: Into, @@ -762,15 +675,8 @@ impl Account { /// This order is sandboxed: it is validated, but not sent to the matching engine. #[allow(clippy::too_many_arguments)] pub fn test_custom_order( - &self, - symbol: S, - qty: F, - price: f64, - stop_price: Option, - order_side: OrderSide, - order_type: OrderType, - time_in_force: TimeInForce, - new_client_order_id: Option, + &self, symbol: S, qty: F, price: f64, stop_price: Option, order_side: OrderSide, + order_type: OrderType, time_in_force: TimeInForce, new_client_order_id: Option, ) -> Result<()> where S: Into, @@ -794,11 +700,7 @@ impl Account { } // Check an order's status - pub fn cancel_order( - &self, - symbol: S, - order_id: u64, - ) -> Result + pub fn cancel_order(&self, symbol: S, order_id: u64) -> Result where S: Into, { @@ -806,180 +708,126 @@ impl Account { parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::Order), Some(request)) } pub fn cancel_order_with_client_id( - &self, - symbol: S, - orig_client_order_id: String, + &self, symbol: S, orig_client_order_id: String, ) -> Result where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - parameters - .insert("origClientOrderId".into(), orig_client_order_id); + parameters.insert("origClientOrderId".into(), orig_client_order_id); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .delete_signed(API::Spot(Spot::Order), Some(request)) } /// Place a test cancel order /// /// This order is sandboxed: it is validated, but not sent to the matching engine. - pub fn test_cancel_order( - &self, - symbol: S, - order_id: u64, - ) -> Result<()> + pub fn test_cancel_order(&self, symbol: S, order_id: u64) -> Result<()> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); parameters.insert("orderId".into(), order_id.to_string()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client - .delete_signed::( - API::Spot(Spot::OrderTest), - Some(request), - ) + .delete_signed::(API::Spot(Spot::OrderTest), Some(request)) .map(|_| ()) } // Trade history - pub fn trade_history( - &self, - symbol: S, - ) -> Result> + pub fn trade_history(&self, symbol: S) -> Result> where S: Into, { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("symbol".into(), symbol.into()); - let request = - build_signed_request(parameters, self.recv_window)?; + let request = build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Spot(Spot::MyTrades), Some(request)) } - fn build_order( - &self, - order: OrderRequest, - ) -> BTreeMap { - let mut order_parameters: BTreeMap = - BTreeMap::new(); + fn build_order(&self, order: OrderRequest) -> BTreeMap { + let mut order_parameters: BTreeMap = BTreeMap::new(); order_parameters.insert("symbol".into(), order.symbol); - order_parameters - .insert("side".into(), order.order_side.to_string()); - order_parameters - .insert("type".into(), order.order_type.to_string()); - order_parameters - .insert("quantity".into(), order.qty.to_string()); + order_parameters.insert("side".into(), order.order_side.to_string()); + order_parameters.insert("type".into(), order.order_type.to_string()); + order_parameters.insert("quantity".into(), order.qty.to_string()); if let Some(stop_price) = order.stop_price { - order_parameters - .insert("stopPrice".into(), stop_price.to_string()); + order_parameters.insert("stopPrice".into(), stop_price.to_string()); } if order.price != 0.0 { - order_parameters - .insert("price".into(), order.price.to_string()); - order_parameters.insert( - "timeInForce".into(), - order.time_in_force.to_string(), - ); + order_parameters.insert("price".into(), order.price.to_string()); + order_parameters.insert("timeInForce".into(), order.time_in_force.to_string()); } if let Some(client_order_id) = order.new_client_order_id { - order_parameters - .insert("newClientOrderId".into(), client_order_id); + order_parameters.insert("newClientOrderId".into(), client_order_id); } order_parameters } fn build_quote_quantity_order( - &self, - order: OrderQuoteQuantityRequest, + &self, order: OrderQuoteQuantityRequest, ) -> BTreeMap { - let mut order_parameters: BTreeMap = - BTreeMap::new(); + let mut order_parameters: BTreeMap = BTreeMap::new(); order_parameters.insert("symbol".into(), order.symbol); - order_parameters - .insert("side".into(), order.order_side.to_string()); - order_parameters - .insert("type".into(), order.order_type.to_string()); - order_parameters.insert( - "quoteOrderQty".into(), - order.quote_order_qty.to_string(), - ); + order_parameters.insert("side".into(), order.order_side.to_string()); + order_parameters.insert("type".into(), order.order_type.to_string()); + order_parameters.insert("quoteOrderQty".into(), order.quote_order_qty.to_string()); if order.price != 0.0 { - order_parameters - .insert("price".into(), order.price.to_string()); - order_parameters.insert( - "timeInForce".into(), - order.time_in_force.to_string(), - ); + order_parameters.insert("price".into(), order.price.to_string()); + order_parameters.insert("timeInForce".into(), order.time_in_force.to_string()); } if let Some(client_order_id) = order.new_client_order_id { - order_parameters - .insert("newClientOrderId".into(), client_order_id); + order_parameters.insert("newClientOrderId".into(), client_order_id); } order_parameters } fn converter_order_to_btree_map>( - &self, - order: OrderQuoteRequest, + &self, order: OrderQuoteRequest, ) -> BTreeMap { - let mut order_parameters: BTreeMap = - BTreeMap::new(); + let mut order_parameters: BTreeMap = BTreeMap::new(); - order_parameters - .insert("fromAsset".into(), order.from_asset.to_string()); - order_parameters - .insert("toAsset".into(), order.to_asset.to_string()); + order_parameters.insert("fromAsset".into(), order.from_asset.to_string()); + order_parameters.insert("toAsset".into(), order.to_asset.to_string()); match order.from_or_to_amount { QtyType::From(v) => { let qty: f64 = v.into(); - order_parameters - .insert("fromAmount".into(), qty.to_string()); + order_parameters.insert("fromAmount".into(), qty.to_string()); } QtyType::To(v) => { let qty: f64 = v.into(); - order_parameters - .insert("toAmount".into(), qty.to_string()); + order_parameters.insert("toAmount".into(), qty.to_string()); } }; if let Some(wallet_type) = order.wallet_type { match wallet_type { WalletType::SPOT => { - order_parameters.insert( - "walletType".into(), - "SPOT".to_string(), - ); + order_parameters.insert("walletType".into(), "SPOT".to_string()); } WalletType::FUNDING => { - order_parameters.insert( - "walletType".into(), - "FUNDING".to_string(), - ); + order_parameters.insert("walletType".into(), "FUNDING".to_string()); } } } @@ -987,20 +835,16 @@ impl Account { if let Some(time) = order.valid_time { match time { ValidTime::TenSeconds => { - order_parameters - .insert("validTime".into(), "10s".to_string()); + order_parameters.insert("validTime".into(), "10s".to_string()); } ValidTime::ThirtySeconds => { - order_parameters - .insert("validTime".into(), "30s".to_string()); + order_parameters.insert("validTime".into(), "30s".to_string()); } ValidTime::OneMinutes => { - order_parameters - .insert("validTime".into(), "1m".to_string()); + order_parameters.insert("validTime".into(), "1m".to_string()); } ValidTime::TwoMinutes => { - order_parameters - .insert("validTime".into(), "2m".to_string()); + order_parameters.insert("validTime".into(), "2m".to_string()); } } } @@ -1008,13 +852,9 @@ impl Account { order_parameters } - // função que faz o request pra converter + // build the request to convert fn send_quote_request( - &self, - symbol_from: S, - symbol_to: S, - qty: QtyType, - wallet_type: Option, + &self, symbol_from: S, symbol_to: S, qty: QtyType, wallet_type: Option, valid_time: Option, ) -> Result where @@ -1036,10 +876,7 @@ impl Account { } // method que aceita a negociação do convert - fn accept_quote( - &self, - quote: Result, - ) -> Result { + fn accept_quote(&self, quote: Result) -> Result { let quote = quote?; //let quote = quote?; @@ -1051,8 +888,7 @@ impl Account { bail!("Not enough funds") } - let request: String = - build_signed_request(params, self.recv_window)?; + let request: String = build_signed_request(params, self.recv_window)?; self.client .post_signed(API::Convert(Convert::AcceptQuote), request) } @@ -1060,18 +896,24 @@ impl Account { /// # Example /// Convert a currency to another. /// + ///```no_run + /// use binance::api::Binance; + /// use binance::account::*; /// - /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); - /// - /// // QtyType::From reduces the value of the first symbol in this case "BTC" - /// // QtyType::To reduces the value of the second symbol in this case "USDT" - /// let answer = account.convert("BTC", "USDT", QtyType::From(0.0001)).unwrap(); + /// fn main() { + /// let api_key = Some("api_key".into()); + /// let secret_key = Some("secret_key".into()); + /// + /// let account: Account = Binance::new(api_key, secret_key); /// + /// // QtyType::From converts "BTC" to "USDT", amount: 0.0001 BTC + /// let response = account.convert("BTC", "USDT", QtyType::From(0.0001)).unwrap(); + /// // QtyType::To converts "BTC" to "USDT", amount: 2.0 USDT + /// let response = account.convert("BTC", "USDT", QtyType::To(2.0)).unwrap(); + /// } + ///``` pub fn convert( - &self, - symbol_from: S, - symbol_to: S, - qty: QtyType, + &self, symbol_from: S, symbol_to: S, qty: QtyType, ) -> Result where S: Into, @@ -1089,16 +931,14 @@ impl Account { } fn daily_account_snapshot_to_btree_map( - &self, - params: AccountSnapshotRequest, + &self, params: AccountSnapshotRequest, ) -> BTreeMap { let mut parameters: BTreeMap = BTreeMap::new(); parameters.insert("type".into(), params.type_); if let Some(start_time) = params.start_time { - parameters - .insert("startTime".into(), start_time.to_string()); + parameters.insert("startTime".into(), start_time.to_string()); } if let Some(end_time) = params.end_time { @@ -1115,10 +955,17 @@ impl Account { /// # Example /// Get the daily account snapshot. /// + ///```no_run + /// use binance::api::Binance; + /// use binance::account::*; /// - /// let account: Account = Binance::new_with_config("API_KEY", "SECRET_KEY"); + /// fn main() { + /// let api_key = Some("api_key".into()); + /// let secret_key = Some("secret_key".into()); + /// let account: Account = Binance::new(api_key, secret_key); /// let answer = account.daily_account_snapshot().unwrap(); - /// + /// } + ///``` pub fn daily_account_snapshot(&self) -> Result { let params = AccountSnapshotRequest { type_: "SPOT".to_string(), @@ -1126,18 +973,14 @@ impl Account { end_time: None, limit: None, }; - let btree_params = - self.daily_account_snapshot_to_btree_map(params); + let btree_params = self.daily_account_snapshot_to_btree_map(params); // this gets the timestamp and recv_windows to the btreemap - let request = - build_signed_request(btree_params, self.recv_window)?; + let request = build_signed_request(btree_params, self.recv_window)?; eprintln!("{:#?}", request); - self.client.get_signed( - API::Savings(Sapi::AccountSnapshot), - Some(request), - ) + self.client + .get_signed(API::Savings(Sapi::AccountSnapshot), Some(request)) } }