Skip to content
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ Cargo.lock

*.fmt
*.iml
.env
.env
.idea
9 changes: 6 additions & 3 deletions examples/ws.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::binance::{model::websocket::Subscription, Binance, BinanceWebsocket};
use crate::binance::{
model::websocket::{Subscription, UpdateSpeed::Default},
Binance, BinanceWebsocket,
};
use binance_async as binance;
use failure::Fallible;
use std::env::var;
Expand All @@ -23,9 +26,9 @@ async fn main() -> Fallible<()> {
Subscription::Ticker("ethbtc".to_string()),
Subscription::AggregateTrade("eosbtc".to_string()),
Subscription::Candlestick("ethbtc".to_string(), "1m".to_string()),
Subscription::Depth("xrpbtc".to_string()),
Subscription::Depth("xrpbtc".to_string(), Default),
Subscription::MiniTicker("zrxbtc".to_string()),
Subscription::OrderBook("trxbtc".to_string(), 5),
Subscription::OrderBook("trxbtc".to_string(), 5, Default),
Subscription::Trade("adabtc".to_string()),
Subscription::UserData(listen_key),
Subscription::MiniTickerAll,
Expand Down
45 changes: 43 additions & 2 deletions src/client/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const API_V3_ORDER: &str = "/api/v3/order";
struct OrderRequest {
pub symbol: String,
pub qty: f64,
pub quote_qty: bool,
pub price: f64,
pub order_side: String,
pub order_type: String,
Expand Down Expand Up @@ -53,6 +54,20 @@ impl Binance {
Ok(balance)
}

pub fn get_all_balances(
&self,
) -> Fallible<impl Future<Output = Fallible<HashMap<String, Balance>>>> {
let to_map = move |account: AccountInformation| {
let map: HashMap<String, Balance> = account
.balances
.into_iter()
.map(|balance| (balance.asset.clone(), balance))
.collect();
future::ready(Ok(map))
};
Ok(self.get_account()?.and_then(to_map))
}

// Current open orders for ONE symbol
pub fn get_open_orders(
&self,
Expand Down Expand Up @@ -95,6 +110,7 @@ impl Binance {
let order = OrderRequest {
symbol: symbol.into(),
qty,
quote_qty: false,
price,
order_side: ORDER_SIDE_BUY.to_string(),
order_type: ORDER_TYPE_LIMIT.to_string(),
Expand All @@ -117,6 +133,7 @@ impl Binance {
let order = OrderRequest {
symbol: symbol.into(),
qty,
quote_qty: false,
price,
order_side: ORDER_SIDE_SELL.to_string(),
order_type: ORDER_TYPE_LIMIT.to_string(),
Expand All @@ -129,14 +146,16 @@ impl Binance {
}

// Place a MARKET order - BUY
pub fn market_buy(
fn market_buy_generic(
&self,
symbol: &str,
qty: f64,
quote_qty: bool,
) -> Fallible<impl Future<Output = Fallible<Transaction>>> {
let order = OrderRequest {
symbol: symbol.into(),
qty,
quote_qty,
price: 0.0,
order_side: ORDER_SIDE_BUY.to_string(),
order_type: ORDER_TYPE_MARKET.to_string(),
Expand All @@ -148,6 +167,22 @@ impl Binance {
Ok(transaction)
}

pub fn market_buy(
&self,
symbol: &str,
qty: f64,
) -> Fallible<impl Future<Output = Fallible<Transaction>>> {
self.market_buy_generic(symbol, qty, false)
}

pub fn market_buy_quote(
&self,
symbol: &str,
qty: f64,
) -> Fallible<impl Future<Output = Fallible<Transaction>>> {
self.market_buy_generic(symbol, qty, true)
}

// Place a MARKET order - SELL
pub fn market_sell(
&self,
Expand All @@ -157,6 +192,7 @@ impl Binance {
let order = OrderRequest {
symbol: symbol.into(),
qty,
quote_qty: false,
price: 0.0,
order_side: ORDER_SIDE_SELL.to_string(),
order_type: ORDER_TYPE_MARKET.to_string(),
Expand Down Expand Up @@ -226,11 +262,16 @@ impl Binance {
}

fn build_order(order: OrderRequest) -> HashMap<&'static str, String> {
let quantity_key = if order.quote_qty {
"quoteOrderQty"
} else {
"quantity"
};
let mut params: HashMap<&str, String> = maplit::hashmap! {
"symbol" => order.symbol,
"side" => order.order_side,
"type" => order.order_type,
"quantity" => order.qty.to_string(),
quantity_key => order.qty.to_string(),
};

if order.price != 0.0 {
Expand Down
4 changes: 2 additions & 2 deletions src/client/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ impl Binance {
}

pub fn get_exchange_info(&self) -> Fallible<impl Future<Output = Fallible<ExchangeInfo>>> {
Ok(self.transport.get::<_, ()>("/api/v1/exchangeInfo", None)?)
Ok(self.transport.get::<_, ()>("/api/v3/exchangeInfo", None)?)
}

// Obtain exchange information (rate limits, symbol metadata etc)
pub fn exchange_info(&self) -> Fallible<impl Future<Output = Fallible<ExchangeInformation>>> {
let info = self.transport.get::<_, ()>("/api/v1/exchangeInfo", None)?;
let info = self.transport.get::<_, ()>("/api/v3/exchangeInfo", None)?;
Ok(info)
}
}
10 changes: 7 additions & 3 deletions src/client/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::{
use streamunordered::{StreamUnordered, StreamYield};
use tokio::net::TcpStream;
use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
use tracing::*;
use tracing::{callsite, trace};
use tungstenite::Message;
use url::Url;

Expand All @@ -40,10 +40,14 @@ impl BinanceWebsocket {
Subscription::Candlestick(ref symbol, ref interval) => {
format!("{}@kline_{}", symbol, interval)
}
Subscription::Depth(ref symbol) => format!("{}@depth", symbol),
Subscription::Depth(ref symbol, ref update_speed) => {
format!("{}@depth{}", symbol, update_speed)
}
Subscription::MiniTicker(ref symbol) => format!("{}@miniTicker", symbol),
Subscription::MiniTickerAll => "!miniTicker@arr".to_string(),
Subscription::OrderBook(ref symbol, depth) => format!("{}@depth{}", symbol, depth),
Subscription::OrderBook(ref symbol, depth, ref update_speed) => {
format!("{}@depth{}{}", symbol, depth, update_speed)
}
Subscription::Ticker(ref symbol) => format!("{}@ticker", symbol),
Subscription::TickerAll => "!ticker@arr".to_string(),
Subscription::Trade(ref symbol) => format!("{}@trade", symbol),
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use snafu::*;
use snafu::Snafu;

#[allow(clippy::pub_enum_variant_names)]
#[derive(Deserialize, Serialize, Debug, Clone, Snafu)]
Expand Down
44 changes: 35 additions & 9 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,36 @@ pub struct Transaction {
pub order_id: u64,
pub client_order_id: String,
pub transact_time: u64,
#[serde(with = "string_or_float")]
pub price: f64,
#[serde(with = "string_or_float")]
pub orig_qty: f64,
#[serde(with = "string_or_float")]
pub executed_qty: f64,
#[serde(with = "string_or_float")]
pub cummulative_quote_qty: f64,
pub status: String,
pub fills: Vec<Fill>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Bids {
#[serde(rename_all = "camelCase")]
pub struct Fill {
#[serde(with = "string_or_float")]
pub price: f64,
#[serde(with = "string_or_float")]
pub qty: f64,
#[serde(with = "string_or_float")]
pub commission: f64,
pub commission_asset: String,
}

// Never serialized.
#[serde(skip_serializing)]
ignore: Vec<String>,
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Bids {
#[serde(with = "string_or_float")]
pub price: f64,
#[serde(with = "string_or_float")]
pub qty: f64,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand All @@ -97,10 +115,6 @@ pub struct Asks {
pub price: f64,
#[serde(with = "string_or_float")]
pub qty: f64,

// Never serialized.
#[serde(skip_serializing)]
ignore: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -366,17 +380,29 @@ pub enum SymbolFilter {
step_size: String,
},
#[serde(rename_all = "camelCase")]
MarketLotSize {
min_qty: String,
max_qty: String,
step_size: String,
},
#[serde(rename_all = "camelCase")]
PriceFilter {
min_price: String,
max_price: String,
tick_size: String,
},
#[serde(rename_all = "camelCase")]
PercentPrice {
multiplier_up: String,
multiplier_down: String,
avg_price_mins: u64,
},
#[serde(rename_all = "camelCase")]
MinNotional { min_notional: String },
#[serde(rename_all = "camelCase")]
MaxNumAlgoOrders { max_num_algo_orders: u64 },
#[serde(rename_all = "camelCase")]
MaxNumOrders { limit: u64 },
MaxNumOrders { max_num_orders: u64 },
#[serde(rename_all = "camelCase")]
IcebergParts { limit: u64 },
}
Expand Down
26 changes: 24 additions & 2 deletions src/model/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::{
string_or_float, Asks, Bids, Kline, OrderBook, OrderExecType, OrderRejectReason, OrderStatus,
OrderType, Side, TimeInForce,
};
use failure::_core::fmt::Formatter;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand All @@ -14,8 +15,29 @@ pub enum Subscription {
MiniTickerAll,
Ticker(String), // symbol
TickerAll,
OrderBook(String, i64), //symbol, depth
Depth(String), //symbol
OrderBook(String, i64, UpdateSpeed), //symbol, depth, update speed
Depth(String, UpdateSpeed), //symbol, update speed
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UpdateSpeed {
Default,
Slow,
Fast,
}

impl std::fmt::Display for UpdateSpeed {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Default => "",
Self::Slow => "@1000ms",
Self::Fast => "@100ms",
}
)
}
}

#[derive(Debug, Clone, Serialize)]
Expand Down
6 changes: 3 additions & 3 deletions src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ use crate::error::{BinanceResponse, Error};
use chrono::Utc;
use failure::Fallible;
use futures::prelude::*;
use headers::*;
use headers::{HeaderName, HeaderValue};
use hex::encode as hexify;
use hmac::{Hmac, Mac};
use http::Method;
use once_cell::sync::OnceCell;
use reqwest_ext::*;
use reqwest_ext::TypedHeaderExt;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{to_string, to_value, Value};
use sha2::Sha256;
use std::str::FromStr;
use tracing::*;
use tracing::{callsite, trace};
use url::Url;

const BASE: &str = "https://www.binance.com";
Expand Down