diff --git a/src/coinnect.rs b/src/coinnect.rs index b21551c..8e36c9d 100644 --- a/src/coinnect.rs +++ b/src/coinnect.rs @@ -1,17 +1,16 @@ //! Use this module to create a generic API. - -#![allow(new_ret_no_self)] +#![allow(clippy::new_ret_no_self)] use std::path::PathBuf; -use exchange::{Exchange, ExchangeApi}; use bitstamp::{BitstampApi, BitstampCreds}; -use kraken::{KrakenApi, KrakenCreds}; -use poloniex::{PoloniexApi, PoloniexCreds}; use bittrex::{BittrexApi, BittrexCreds}; -use gdax::{GdaxApi, GdaxCreds}; use error::*; +use exchange::{Exchange, ExchangeApi}; +use gdax::{GdaxApi, GdaxCreds}; +use kraken::{KrakenApi, KrakenCreds}; +use poloniex::{PoloniexApi, PoloniexCreds}; pub trait Credentials { /// Get an element from the credentials. @@ -27,7 +26,7 @@ pub struct Coinnect; impl Coinnect { /// Create a new CoinnectApi by providing an API key & API secret - pub fn new(exchange: Exchange, creds: C) -> Result> { + pub fn new(exchange: Exchange, creds: C) -> Result> { match exchange { Exchange::Bitstamp => Ok(Box::new(BitstampApi::new(creds)?)), Exchange::Kraken => Ok(Box::new(KrakenApi::new(creds)?)), @@ -42,26 +41,27 @@ impl Coinnect { /// /// For this example, you could use load your Bitstamp account with /// `new_from_file(Exchange::Bitstamp, "account_bitstamp", Path::new("/keys.json"))` - pub fn new_from_file(exchange: Exchange, - name: &str, - path: PathBuf) - -> Result> { + pub fn new_from_file( + exchange: Exchange, + name: &str, + path: PathBuf, + ) -> Result> { match exchange { - Exchange::Bitstamp => { - Ok(Box::new(BitstampApi::new(BitstampCreds::new_from_file(name, path)?)?)) - } - Exchange::Kraken => { - Ok(Box::new(KrakenApi::new(KrakenCreds::new_from_file(name, path)?)?)) - } - Exchange::Poloniex => { - Ok(Box::new(PoloniexApi::new(PoloniexCreds::new_from_file(name, path)?)?)) - } - Exchange::Bittrex => { - Ok(Box::new(BittrexApi::new(BittrexCreds::new_from_file(name, path)?)?)) - } - Exchange::Gdax => { - Ok(Box::new(GdaxApi::new(GdaxCreds::new_from_file(name, path)?)?)) - } + Exchange::Bitstamp => Ok(Box::new(BitstampApi::new(BitstampCreds::new_from_file( + name, path, + )?)?)), + Exchange::Kraken => Ok(Box::new(KrakenApi::new(KrakenCreds::new_from_file( + name, path, + )?)?)), + Exchange::Poloniex => Ok(Box::new(PoloniexApi::new(PoloniexCreds::new_from_file( + name, path, + )?)?)), + Exchange::Bittrex => Ok(Box::new(BittrexApi::new(BittrexCreds::new_from_file( + name, path, + )?)?)), + Exchange::Gdax => Ok(Box::new(GdaxApi::new(GdaxCreds::new_from_file( + name, path, + )?)?)), } } } diff --git a/src/kraken/api.rs b/src/kraken/api.rs index c567581..c0b8ee3 100644 --- a/src/kraken/api.rs +++ b/src/kraken/api.rs @@ -2,32 +2,32 @@ //! It is recommended to use a nonce window setting of 5000 for your API key when sending requests in quick succession in order to avoid nonce errors. //! WARNING: Special attention should be paid to error management: parsing number, etc. -#![allow(too_many_arguments)] +#![allow(clippy::too_many_arguments)] use hmac::{Hmac, Mac}; -use sha2::{Sha256, Sha512, Digest}; +use sha2::{Digest, Sha256, Sha512}; -use hyper_native_tls::NativeTlsClient; -use hyper::Client; use hyper::header; use hyper::net::HttpsConnector; +use hyper::Client; +use hyper_native_tls::NativeTlsClient; use data_encoding::BASE64; -use serde_json::Value; use serde_json::value::Map; +use serde_json::Value; use std::collections::HashMap; use std::io::Read; +use std::str; use std::thread; use std::time::Duration; -use std::str; use error::*; use helpers; -use exchange::Exchange; use coinnect::Credentials; +use exchange::Exchange; use kraken::utils; header! { @@ -50,7 +50,6 @@ pub struct KrakenApi { burst: bool, } - impl KrakenApi { /// Create a new KrakenApi by providing an API key & API secret pub fn new(creds: C) -> Result { @@ -66,16 +65,15 @@ impl KrakenApi { let connector = HttpsConnector::new(ssl); Ok(KrakenApi { - last_request: 0, - api_key: creds.get("api_key").unwrap_or_default(), - api_secret: creds.get("api_secret").unwrap_or_default(), - otp: None, - http_client: Client::with_connector(connector), - burst: false, - }) + last_request: 0, + api_key: creds.get("api_key").unwrap_or_default(), + api_secret: creds.get("api_secret").unwrap_or_default(), + otp: None, + http_client: Client::with_connector(connector), + burst: false, + }) } - /// Use to provide your two-factor password (if two-factor enabled, otherwise not required) pub fn set_two_pass_auth(&mut self, otp: String) { self.otp = Some(otp); @@ -91,7 +89,7 @@ impl KrakenApi { } pub fn block_or_continue(&self) { - if ! self.burst { + if !self.burst { let threshold: u64 = 2000; // 1 request/2sec let offset: u64 = helpers::get_unix_timestamp_ms() as u64 - self.last_request as u64; if offset < threshold { @@ -101,13 +99,16 @@ impl KrakenApi { } } - fn public_query(&mut self, - method: &str, - params: &mut HashMap<&str, &str>) - -> Result> { + fn public_query( + &mut self, + method: &str, + params: &mut HashMap<&str, &str>, + ) -> Result> { helpers::strip_empties(params); - let url = "https://api.kraken.com/0/public/".to_string() + method + "?" + - &helpers::url_encode_hashmap(params); + let url = "https://api.kraken.com/0/public/".to_string() + + method + + "?" + + &helpers::url_encode_hashmap(params); self.block_or_continue(); //TODO: Handle correctly http errors with error_chain. @@ -121,10 +122,11 @@ impl KrakenApi { utils::deserialize_json(&buffer) } - fn private_query(&mut self, - method: &str, - mut params: &mut HashMap<&str, &str>) - -> Result> { + fn private_query( + &mut self, + method: &str, + mut params: &mut HashMap<&str, &str>, + ) -> Result> { let url = "https://api.kraken.com/0/private/".to_string() + method; let urlpath = "/0/private/".to_string() + method; @@ -147,11 +149,13 @@ impl KrakenApi { custom_header.set(KeyHeader(self.api_key.clone())); custom_header.set(SignHeader(signature)); - let mut res = match self.http_client - .post(&url) - .body(&postdata) - .headers(custom_header) - .send() { + let mut res = match self + .http_client + .post(&url) + .body(&postdata) + .headers(custom_header) + .send() + { Ok(res) => res, Err(err) => return Err(ErrorKind::ServiceUnavailable(err.to_string()).into()), }; @@ -211,11 +215,12 @@ impl KrakenApi { /// decimals = scaling decimal places for record keeping /// display_decimals = scaling decimal places for output display /// ``` - pub fn get_asset_info(&mut self, - info: &str, - aclass: &str, - asset: &str) - -> Result> { + pub fn get_asset_info( + &mut self, + info: &str, + aclass: &str, + asset: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("info", info); params.insert("aclass", aclass); @@ -256,10 +261,11 @@ impl KrakenApi { /// margin_call = margin call level /// margin_stop = stop-out/liquidation margin level /// ``` - pub fn get_tradable_asset_pairs(&mut self, - info: &str, - pair: &str) - -> Result> { + pub fn get_tradable_asset_pairs( + &mut self, + info: &str, + pair: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("info", info); params.insert("pair", pair); @@ -312,11 +318,12 @@ impl KrakenApi { /// /// Note: the last entry in the OHLC array is for the current, not-yet-committed frame and will /// always be present, regardless of the value of "since". - pub fn get_ohlc_data(&mut self, - pair: &str, - interval: &str, - since: &str) - -> Result> { + pub fn get_ohlc_data( + &mut self, + pair: &str, + interval: &str, + since: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("pair", pair); params.insert("interval", interval); @@ -344,7 +351,6 @@ impl KrakenApi { self.public_query("Depth", &mut params) } - /// Input: /// /// ```json @@ -382,10 +388,11 @@ impl KrakenApi { /// ``` /// Note: "since" is inclusive so any returned data with the same time as the previous set /// should overwrite all of the previous set's entries at that time - pub fn get_recent_spread_data(&mut self, - pair: &str, - since: &str) - -> Result> { + pub fn get_recent_spread_data( + &mut self, + pair: &str, + since: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("pair", pair); params.insert("since", since); @@ -513,14 +520,15 @@ impl KrakenApi { /// ``` /// Note: Times given by order tx ids are more accurate than unix timestamps. If an order tx id /// is given for the time, the order's open time is used - pub fn get_closed_orders(&mut self, - trades: &str, - userref: &str, - start: &str, - end: &str, - ofs: &str, - closetime: &str) - -> Result> { + pub fn get_closed_orders( + &mut self, + trades: &str, + userref: &str, + start: &str, + end: &str, + ofs: &str, + closetime: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("trades", trades); params.insert("userref", userref); @@ -543,11 +551,12 @@ impl KrakenApi { /// ```json /// = order info. See Get open orders/Get closed orders /// ``` - pub fn query_orders_info(&mut self, - trades: &str, - userref: &str, - txid: &str) - -> Result> { + pub fn query_orders_info( + &mut self, + trades: &str, + userref: &str, + txid: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("trades", trades); params.insert("userref", userref); @@ -605,13 +614,14 @@ impl KrakenApi { /// Unless otherwise stated, costs, fees, prices, and volumes are in the asset pair's scale, /// not the currency's scale. /// Times given by trade tx ids are more accurate than unix timestamps. - pub fn get_trades_history(&mut self, - type_trade: &str, - trades: &str, - start: &str, - end: &str, - ofs: &str) - -> Result> { + pub fn get_trades_history( + &mut self, + type_trade: &str, + trades: &str, + start: &str, + end: &str, + ofs: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("type", type_trade); params.insert("trades", trades); @@ -706,14 +716,15 @@ impl KrakenApi { /// balance = resulting balance /// ``` /// Note: Times given by ledger ids are more accurate than unix timestamps. - pub fn get_ledgers_info(&mut self, - aclass: &str, - asset: &str, - type_ledger: &str, - start: &str, - end: &str, - ofs: &str) - -> Result> { + pub fn get_ledgers_info( + &mut self, + aclass: &str, + asset: &str, + type_ledger: &str, + start: &str, + end: &str, + ofs: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -860,20 +871,22 @@ impl KrakenApi { /// close out your position. /// If you receive the error "EOrder:Trading agreement required", refer to your API key /// management page for further details. - pub fn add_standard_order(&mut self, - pair: &str, - type_order: &str, - ordertype: &str, - price: &str, - price2: &str, - volume: &str, - leverage: &str, - oflags: &str, - starttm: &str, - expiretm: &str, - userref: &str, - validate: &str) - -> Result> { + pub fn add_standard_order( + &mut self, + pair: &str, + type_order: &str, + ordertype: &str, + price: &str, + price2: &str, + volume: &str, + leverage: &str, + oflags: &str, + starttm: &str, + expiretm: &str, + userref: &str, + validate: &str, + trading_agreement: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("pair", pair); params.insert("type", type_order); @@ -887,6 +900,7 @@ impl KrakenApi { params.insert("expiretm", expiretm); params.insert("userref", userref); params.insert("validate", validate); + params.insert("trading_agreement", trading_agreement); self.private_query("AddOrder", &mut params) } @@ -946,12 +960,13 @@ impl KrakenApi { /// expiretm = expiration time in unix timestamp, or 0 if not expiring /// new = whether or not address has ever been used /// ``` - pub fn get_deposit_addresses(&mut self, - aclass: &str, - asset: &str, - method: &str, - new: &str) - -> Result> { + pub fn get_deposit_addresses( + &mut self, + aclass: &str, + asset: &str, + method: &str, + new: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -986,11 +1001,12 @@ impl KrakenApi { /// onhold = deposit is on hold pending review /// ``` /// For information about the status, please refer to the IFEX financial transaction states. - pub fn get_status_of_recent_deposits(&mut self, - aclass: &str, - asset: &str, - method: &str) - -> Result> { + pub fn get_status_of_recent_deposits( + &mut self, + aclass: &str, + asset: &str, + method: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -1014,12 +1030,13 @@ impl KrakenApi { /// limit = maximum net amount that can be withdrawn right now /// fee = amount of fees that will be paid /// ``` - pub fn get_withdrawal_information(&mut self, - aclass: &str, - asset: &str, - key: &str, - amount: &str) - -> Result> { + pub fn get_withdrawal_information( + &mut self, + aclass: &str, + asset: &str, + key: &str, + amount: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -1042,12 +1059,13 @@ impl KrakenApi { /// ```json /// refid = reference id /// ``` - pub fn withdraw_funds(&mut self, - aclass: &str, - asset: &str, - key: &str, - amount: &str) - -> Result> { + pub fn withdraw_funds( + &mut self, + aclass: &str, + asset: &str, + key: &str, + amount: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -1085,11 +1103,12 @@ impl KrakenApi { /// onhold = withdrawal is on hold pending review /// ``` /// For information about the status, please refer to the IFEX financial transaction states. - pub fn get_status_of_recent_withdrawals(&mut self, - aclass: &str, - asset: &str, - method: &str) - -> Result> { + pub fn get_status_of_recent_withdrawals( + &mut self, + aclass: &str, + asset: &str, + method: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -1124,11 +1143,12 @@ impl KrakenApi { /// Note: Cancelation cannot be guaranteed. This will put in a cancelation request. Depending /// upon how far along the withdrawal process is, it may not be possible to cancel the /// withdrawal. - pub fn request_withdrawal_cancelation(&mut self, - aclass: &str, - asset: &str, - refid: &str) - -> Result> { + pub fn request_withdrawal_cancelation( + &mut self, + aclass: &str, + asset: &str, + refid: &str, + ) -> Result> { let mut params = HashMap::new(); params.insert("aclass", aclass); params.insert("asset", asset); @@ -1163,7 +1183,6 @@ mod kraken_api_tests { assert!(difference >= 1999); assert!(difference < 10000); - api.set_burst(true); let start = helpers::get_unix_timestamp_ms(); api.block_or_continue(); @@ -1173,7 +1192,9 @@ mod kraken_api_tests { assert!(difference < 10); counter = counter + 1; - if counter >= 3 { break; } + if counter >= 3 { + break; + } } } } diff --git a/src/kraken/generic_api.rs b/src/kraken/generic_api.rs index 1d7758b..5470086 100644 --- a/src/kraken/generic_api.rs +++ b/src/kraken/generic_api.rs @@ -6,9 +6,9 @@ use exchange::ExchangeApi; use kraken::api::KrakenApi; use error::*; -use types::*; -use kraken::utils; use helpers; +use kraken::utils; +use types::*; impl ExchangeApi for KrakenApi { fn ticker(&mut self, pair: Pair) -> Result { @@ -27,14 +27,13 @@ impl ExchangeApi for KrakenApi { let vol = helpers::from_json_bigdecimal(&result[*pair_name]["v"][0], "v")?; Ok(Ticker { - timestamp: helpers::get_unix_timestamp_ms(), - pair: pair, - last_trade_price: price, - lowest_ask: ask, - highest_bid: bid, - volume: Some(vol), - }) - + timestamp: helpers::get_unix_timestamp_ms(), + pair, + last_trade_price: price, + lowest_ask: ask, + highest_bid: bid, + volume: Some(vol), + }) } fn orderbook(&mut self, pair: Pair) -> Result { @@ -50,20 +49,12 @@ impl ExchangeApi for KrakenApi { let mut ask_offers = Vec::new(); let mut bid_offers = Vec::new(); - let ask_array = - result[*pair_name]["asks"] - .as_array() - .ok_or_else(|| { - ErrorKind::InvalidFieldFormat(format!("{}.asks", - result[*pair_name])) - })?; - let bid_array = - result[*pair_name]["bids"] - .as_array() - .ok_or_else(|| { - ErrorKind::InvalidFieldFormat(format!("{}.bids", - result[*pair_name])) - })?; + let ask_array = result[*pair_name]["asks"] + .as_array() + .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}.asks", result[*pair_name])))?; + let bid_array = result[*pair_name]["bids"] + .as_array() + .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}.bids", result[*pair_name])))?; for ask in ask_array { let price = helpers::from_json_bigdecimal(&ask[0], "ask price")?; @@ -80,19 +71,20 @@ impl ExchangeApi for KrakenApi { } Ok(Orderbook { - timestamp: helpers::get_unix_timestamp_ms(), - pair: pair, - asks: ask_offers, - bids: bid_offers, - }) + timestamp: helpers::get_unix_timestamp_ms(), + pair, + asks: ask_offers, + bids: bid_offers, + }) } - fn add_order(&mut self, - order_type: OrderType, - pair: Pair, - quantity: Volume, - price: Option) - -> Result { + fn add_order( + &mut self, + order_type: OrderType, + pair: Pair, + quantity: Volume, + price: Option, + ) -> Result { let pair_name = match utils::get_pair_string(&pair) { Some(name) => name, None => return Err(ErrorKind::PairUnsupported.into()), @@ -113,38 +105,42 @@ impl ExchangeApi for KrakenApi { price_str = price.unwrap().to_string() }; - let raw_response = self.add_standard_order(pair_name, - direction, - order_type_str, - &price_str, - "", - &quantity.to_string(), - "", - "", - "", - "", - "", - "")?; + let raw_response = self.add_standard_order( + pair_name, + direction, + order_type_str, + &price_str, + "", + &quantity.to_string(), + "", + "", + "", + "", + "", + "", + "", + )?; let result = utils::parse_result(&raw_response)?; let mut txids = Vec::new(); - let list_id = - result["txid"] - .as_array() - .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}", result["txid"])))?; + let list_id = result["txid"] + .as_array() + .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}", result["txid"])))?; for id in list_id { - txids.push(id.as_str() - .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}", id)))? - .to_string()); + txids.push( + id.as_str() + .ok_or_else(|| ErrorKind::InvalidFieldFormat(format!("{}", id)))? + .to_string(), + ); } Ok(OrderInfo { - timestamp: helpers::get_unix_timestamp_ms(), - identifier: txids, - }) + timestamp: helpers::get_unix_timestamp_ms(), + identifier: txids, + }) } fn balances(&mut self) -> Result { @@ -161,8 +157,8 @@ impl ExchangeApi for KrakenApi { let amount = helpers::from_json_bigdecimal(&val, "amount")?; balances.insert(c, amount); - }, - _ => () + } + _ => (), } } diff --git a/src/lib.rs b/src/lib.rs index 65bc291..f709422 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,24 +21,21 @@ //! the library malfunction, which can lead to a loss of money. // error_chain can make a lot of recursions. -#![recursion_limit="128"] - +#![recursion_limit = "128"] // Allow lint customization. #![allow(unknown_lints)] - // Move all the clippy warning in deny. -#![deny(clippy)] - +#![deny(clippy::all)] // Avoid warning for the Crypto-currency about quotes. -#![allow(doc_markdown)] +#![allow(clippy::doc_markdown)] #[macro_use] extern crate hyper; -extern crate sha2; +extern crate chrono; extern crate hmac; extern crate hyper_native_tls; extern crate serde_json; -extern crate chrono; +extern crate sha2; #[macro_use] extern crate lazy_static; extern crate bidir_map; @@ -48,13 +45,13 @@ extern crate error_chain; extern crate bigdecimal; pub mod coinnect; -pub mod exchange; pub mod error; -pub mod types; +pub mod exchange; mod helpers; +pub mod types; pub mod bitstamp; -pub mod poloniex; -pub mod kraken; pub mod bittrex; pub mod gdax; +pub mod kraken; +pub mod poloniex; diff --git a/src/poloniex/generic_api.rs b/src/poloniex/generic_api.rs index 9215a29..6a755ff 100644 --- a/src/poloniex/generic_api.rs +++ b/src/poloniex/generic_api.rs @@ -9,9 +9,9 @@ use bigdecimal::BigDecimal; use std::str::FromStr; use error::*; -use types::*; -use poloniex::utils; use helpers; +use poloniex::utils; +use types::*; impl ExchangeApi for PoloniexApi { fn ticker(&mut self, pair: Pair) -> Result { @@ -30,7 +30,7 @@ impl ExchangeApi for PoloniexApi { Ok(Ticker { timestamp: helpers::get_unix_timestamp_ms(), - pair: pair, + pair, last_trade_price: price, lowest_ask: ask, highest_bid: bid, @@ -75,13 +75,19 @@ impl ExchangeApi for PoloniexApi { Ok(Orderbook { timestamp: helpers::get_unix_timestamp_ms(), - pair: pair, + pair, asks: ask_offers, bids: bid_offers, }) } - fn add_order(&mut self, order_type: OrderType, pair: Pair, quantity: Volume, price: Option) -> Result { + fn add_order( + &mut self, + order_type: OrderType, + pair: Pair, + quantity: Volume, + price: Option, + ) -> Result { let pair_name = match utils::get_pair_string(&pair) { Some(name) => name, None => return Err(ErrorKind::PairUnsupported.into()), @@ -127,12 +133,10 @@ impl ExchangeApi for PoloniexApi { Ok(OrderInfo { timestamp: helpers::get_unix_timestamp_ms(), - identifier: vec![ - result["orderNumber"] - .as_f64() - .ok_or_else(|| ErrorKind::MissingField("orderNumber".to_string()))? - .to_string(), - ], + identifier: vec![result["orderNumber"] + .as_f64() + .ok_or_else(|| ErrorKind::MissingField("orderNumber".to_string()))? + .to_string()], }) }