Giter Site home page Giter Site logo

wisespace-io / binance-rs Goto Github PK

View Code? Open in Web Editor NEW
610.0 17.0 284.0 2.93 MB

Rust Library for the Binance API

License: Other

Rust 100.00%
trading binance-api rust cryptocurrency cryptocurrency-exchanges binance binance-exchange binance-futures

binance-rs's Introduction

binance-rs

Unofficial Rust Library for the Binance API and Binance Futures API (Under development with upcoming breaking changes)

Crates.io Build Status CI MIT licensed Apache-2.0 licensed

Documentation on docs.rs

Binance API Telegram

https://t.me/binance_api_english

Risk Warning

It is a personal project, use at your own risk. I will not be responsible for your investment losses. Cryptocurrency investment is subject to high market risk.

Usage

Add this to your Cargo.toml

[dependencies]
binance = { git = "https://github.com/wisespace-io/binance-rs.git" }

Rust >= 1.56.1

rustup install stable

Table of Contents

MARKET DATA

use binance::api::*;
use binance::model::*;
use binance::market::*;

fn main() {
    let market: Market = Binance::new(None, None);

    // Order book at default depth
    match market.get_depth("BNBETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {}", e),
    }

    // Order book at depth 500
    match market.get_custom_depth("BNBETH", 500) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {}", e),
    }

    // Latest price for ALL symbols
    match market.get_all_prices() {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    // Latest price for ONE symbol
    match market.get_price("BNBETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    // Current average price for ONE symbol
    match market.get_average_price("BNBETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    // Best price/qty on the order book for ALL symbols
    match market.get_all_book_tickers() {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    // Best price/qty on the order book for ONE symbol
    match market.get_book_ticker("BNBETH") {
        Ok(answer) => println!(
            "Bid Price: {}, Ask Price: {}",
            answer.bid_price, answer.ask_price
        ),
        Err(e) => println!("Error: {:?}", e),
    }

    // 24hr ticker price change statistics
    match market.get_24h_price_stats("BNBETH") {
        Ok(answer) => println!(
            "Open Price: {}, Higher Price: {}, Lower Price: {:?}",
            answer.open_price, answer.high_price, answer.low_price
        ),
        Err(e) => println!("Error: {:?}", e),
    }

    // last 10 5min klines (candlesticks) for a symbol:
    match market.get_klines("BNBETH", "5m", 10, None, None) {
        Ok(klines) => {   
            match klines {
                binance::model::KlineSummaries::AllKlineSummaries(klines) => {
                    let kline: KlineSummary = klines[0].clone(); // You need to iterate over the klines
                    println!(
                        "Open: {}, High: {}, Low: {}",
                        kline.open, kline.high, kline.low
                    )
                }
            }
        },
        Err(e) => println!("Error: {}", e),
    }
}

ACCOUNT DATA

use binance::api::*;
use binance::account::*;

fn main() {
    let api_key = Some("YOUR_API_KEY".into());
    let secret_key = Some("YOUR_SECRET_KEY".into());

    let account: Account = Binance::new(api_key, secret_key);

    match account.get_account() {
        Ok(answer) => println!("{:?}", answer.balances),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.get_open_orders("WTCETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.limit_buy("WTCETH", 10, 0.014000) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.market_buy("WTCETH", 5) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.limit_sell("WTCETH", 10, 0.035000) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.market_sell("WTCETH", 5) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.custom_order("WTCETH", 9999, 0.0123, "SELL", "LIMIT", "IOC") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    let order_id = 1_957_528;
    match account.order_status("WTCETH", order_id) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.cancel_order("WTCETH", order_id) {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.cancel_all_open_orders("WTCETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.get_balance("KNC") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }

    match account.trade_history("WTCETH") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {:?}", e),
    }
}

ERROR HANDLING

Provides more detailed error information

You can check out the Binance Error Codes

use binance::errors::ErrorKind as BinanceLibErrorKind;

[...]

Err(err) => {
    println!("Can't put an order!");

    match err.0 {
        BinanceLibErrorKind::BinanceError(response) => match response.code {
            -1013_i16 => println!("Filter failure: LOT_SIZE!"),
            -2010_i16 => println!("Funds insufficient! {}", response.msg),
            _ => println!("Non-catched code {}: {}", response.code, response.msg),
        },
        BinanceLibErrorKind::Msg(msg) => {
            println!("Binancelib error msg: {}", msg)
        }
        _ => println!("Other errors: {}.", err.0),
    };
}

TESTNET AND API CLUSTERS

You can overwrite the default binance api urls if there are performance issues with the endpoints.

You can check out the Binance API Clusters.

The same is applicable for Testnet and Binance.US support. See example below:

let general: General = if use_testnet {
    let config = Config::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/api")
                                  // .set_futures_ws_endpoint("https://testnet.binancefuture.com/ws")
    Binance::new_with_config(None, None, &config)
} else {
    Binance::new(None, None)
};

USER STREAM CONFIGURATION

use binance::api::*;
use binance::userstream::*;

fn main() {
    let api_key_user = Some("YOUR_API_KEY".into());
    let user_stream: UserStream = Binance::new(api_key_user.clone(), 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),
            Err(e) => println!("Error: {:?}", e),
        }

        match user_stream.close(&listen_key) {
            Ok(msg) => println!("Close user data stream: {:?}", msg),
            Err(e) => println!("Error: {:?}", e),
        }
    } else {
        println!("Not able to start an User Stream (Check your API_KEY)");
    }
}

USER STREAM

use binance::api::*;
use binance::userstream::*;
use binance::websockets::*;
use std::sync::atomic::{AtomicBool};

fn main() {
    let api_key_user = Some("YOUR_KEY".into());
    let keep_running = AtomicBool::new(true); // Used to control the event loop
    let user_stream: UserStream = Binance::new(api_key_user, None);

    if let Ok(answer) = user_stream.start() {
	let listen_key = answer.listen_key;

	let mut web_socket = WebSockets::new(|event: WebsocketEvent| {
	    match event {
		WebsocketEvent::AccountUpdate(account_update) => {
		    for balance in &account_update.balance {
			println!("Asset: {}, free: {}, locked: {}", balance.asset, balance.free, balance.locked);
		    }
		},
		WebsocketEvent::OrderTrade(trade) => {
		    println!("Symbol: {}, Side: {}, Price: {}, Execution Type: {}", trade.symbol, trade.side, trade.price, trade.execution_type);
		},
		_ => (),
	    };
	    Ok(())
	});

	web_socket.connect(&listen_key).unwrap(); // check error
	    if let Err(e) = web_socket.event_loop(&keep_running) {
		match e {
		    err => {
		        println!("Error: {:?}", err);
		    }
		}
	     }
	} else {
	    println!("Not able to start an User Stream (Check your API_KEY)");
	}
}

TRADES

use binance::websockets::*;
use std::sync::atomic::{AtomicBool};

fn main() {
    let keep_running = AtomicBool::new(true); // Used to control the event loop
    let agg_trade = format!("!ticker@arr"); // All Symbols
    let mut web_socket = WebSockets::new(|event: WebsocketEvent| {
	match event {
        // 24hr rolling window ticker statistics for all symbols that changed in an array.
	    WebsocketEvent::DayTickerAll(ticker_events) => {
	        for tick_event in ticker_events {
		    if tick_event.symbol == "BTCUSDT" {
			let btcusdt: f32 = tick_event.average_price.parse().unwrap();
			let btcusdt_close: f32 = tick_event.current_close.parse().unwrap();
			println!("{} - {}", btcusdt, btcusdt_close);
		    }
		}
	    },
	    _ => (),
        };

        Ok(())
    });

    web_socket.connect(&agg_trade).unwrap(); // check error
    if let Err(e) = web_socket.event_loop(&keep_running) {
	match e {
	    err => {
	        println!("Error: {:?}", err);
	    }
	}
     }
}

KLINE

use binance::websockets::*;
use std::sync::atomic::{AtomicBool};

fn main() {
    let keep_running = AtomicBool::new(true); // Used to control the event loop
    let kline = format!("{}", "ethbtc@kline_1m");
    let mut web_socket = WebSockets::new(|event: WebsocketEvent| {
        match event {
            WebsocketEvent::Kline(kline_event) => {
                println!("Symbol: {}, high: {}, low: {}", kline_event.kline.symbol, kline_event.kline.low, kline_event.kline.high);
            },
            _ => (),
        };
        Ok(())
    });
 
    web_socket.connect(&kline).unwrap(); // check error
    if let Err(e) = web_socket.event_loop(&keep_running) {
        match e {
          err => {
             println!("Error: {:?}", err);
          }
        }
     }
     web_socket.disconnect().unwrap();
}

MULTIPLE STREAMS

use binance::websockets::*;
use std::sync::atomic::{AtomicBool};

fn main() {
    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 {
            println!("{:?}", depth_order_book);
        }

        Ok(())
    });

    web_socket.connect_multiple_streams(&endpoints).unwrap(); // check error
    if let Err(e) = web_socket.event_loop(&keep_running) {
        println!("Error: {:?}", e);
    }
    web_socket.disconnect().unwrap();
}

Other Exchanges

If you use Bitfinex check out my Rust library for bitfinex API

binance-rs's People

Contributors

agodnic avatar alexapps99 avatar chipshort avatar clementpl avatar danieleades avatar dependabot[bot] avatar dizda avatar dorak88783 avatar g1mv avatar gabriel-milan avatar gleize avatar hightop0924 avatar jhnsmth avatar jpmediadev avatar julesyoungberg avatar kalitaalexey avatar kkrittipong avatar nopbit avatar ppamorim avatar qrayven avatar sergshabal avatar siegfried avatar timvanscherpenzeel avatar unv-annihilator avatar vikulikov avatar vmark-dev avatar wisespace-io avatar y0h4n3s avatar yovanoc avatar ytskuh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

binance-rs's Issues

Support for /api/v3/order/test endpoint

Hi!

Are you interested in supporting the Test new order endpoint?

From an interface point of view, two options come to my mind:

  1. Adding a test version for each existing function related to new orders. Example: now we have Account::limit_buy, Account::limit_sell, etc. and we could add Account::test_limit_buy, Account::test_limit_sell, etc.
  2. Adding a boolean parameter to the existing functions to indicate whether to use the test endpoint or not.

I could help with a pull request.

Testnet API

Hi, would like to include testnet API there. It is only necessary to create client with https://testnet.binance.vision instead of https://api.binance.com. As I'm new to Rust, what would be the ideal implementation? Should I make parameter to new(...) function or create new trait function for example new_testnet(...)?

Futures account API

Hello, great lib, but when are you planning to add account apis for futures? to place order, etc all methods needed for real futures trading

Thanks

websocket receive MarketData error

thread 'main' panicked at 'called Result::unwrap() on an Err value: Error(Msg("Error during handshake URL error: Unable to connect to wss://stream.binance.com:9443/stream?streams=ethbtc@depth@100ms/bnbeth@depth@100ms"), State { next_error: None, backtrace: InternalBacktrace })'

Rust Version 1.53

Bad Request errors.

Today, binance started returning Bad Request errors using this library. Other libraries seem to work as do curl requests, so I assume it's something about the signature generation.

Every request will return an error.

Error: Bad Request: Response { url: "https://www.binance.com/api/v3/account?recvWindow=5000&timestamp=XXX&signature=XXX", status: BadRequest, headers: {"Content-Type": "application/json", "Transfer-Encoding": "chunked", "Connection": "keep-alive", "Date": "Mon, 18 Jun 2018 06:15:38 GMT", "Server": "nginx", "Strict-Transport-Security": "max-age=31536000; includeSubdomains", "X-Frame-Options": "SAMEORIGIN", "X-Xss-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "Content-Security-Policy": "default-src 'self'", "X-Content-Security-Policy": "default-src 'self'", "X-WebKit-CSP": "default-src 'self'", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0", "X-Cache": "Error from cloudfront", "Via": "1.1 38afecd326e6fb8546e520d8470bb9f6.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "XXX=="} }

How to save websocket stream to file? File object is out of scope in callback function

The csv crate requires a file object to write to CSV. How can this fileobject be passed/accessed from within a method such as day_ticker_handler? From my understanding the callback parameters are set to: &self, events: &[DayTickerEvent] which means that wtr is out of scope inside day_ticker_handler.

extern crate csv;

let file_path = std::path::Path::new("test.csv");
let mut wtr = csv::Writer::from_path(file_path).unwrap();

Here is a link to day_ticker_handler example in the repository:
https://github.com/wisespace-io/binance-rs/blob/master/examples/src/binance_websockets.rs#L115-L118

fn all_trades_websocket() {
    struct WebSocketHandler;

    impl DayTickerEventHandler for WebSocketHandler {
        fn day_ticker_handler(&self, events: &[DayTickerEvent]) {
            for event in events {
                println!(
                    "Symbol: {}, price: {}, qty: {}",
                    event.symbol, event.best_bid, event.best_bid_qty
                );
            }
        }
}

Step Size

With this API, there does not seem to be a way of determining the step size for certain tokens. Sometimes i recieve LOT_SIZE error, and from my research this has to do with binance only allowing certain purchase amounts for certain tokens. At the moment i am rounding off to a fixed 6 decimals but i know that this will not apply for everything and i will come across issues eventually.

This is the API reference for the feature i am referring to
https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#lot_size

Stop limits on buy and sell

Super cool crate, works perfectly.

However is is possible when summitting a buy or sell order to use a stop-limit?

I've take a quick look and can't see the feature in your library. Looking at Binance's api it does show it's possible.

If this features does not exist would it be best to expand on custom_order or create a new stop_loss_custom_order?

Keep WS User Stream Alive

I'm subscribing to the WS user stream as per the "WEBSOCKETS - USER STREAM" section of the README. I noticed that it seems to disconnect (no longer receiving updates) after several hours. I also subscribe to the !ticker@arr stream in the same connection (like this:format!("{}/!ticker@arr", answer.listen_key)) but the ticker stream stays connected.

Where do you suggest I begin troubleshooting this?

Future WebSocket Support

Hey,

first of all: great project - thanks!

I am already using the DayTickerAll WebSocket as shown in the README, and would now like to use the Future WebSockets.
I have seen that there are already models and code for it, but it seems that it is not ready yet.

My question now is whether it is already being developed further, or if I should develop it further myself and then submit a pull request.

Thanks a lot for your help.

keep getting "Unauthorized" when call "get_account"

I tried sample code on README :

use binance::api::*;
use binance::account::*;

fn main() {
    let api_key = Some("YOUR_API_KEY".into());
    let secret_key = Some("YOUR_SECRET_KEY".into());

    let account: Account = Binance::new(api_key, secret_key);

    match account.get_account() {
        Ok(answer) => println!("{:?}", answer.balances),
        Err(e) => println!("Error: {:?}", e),
    }

and result didn't change :

    Error(Msg("Unauthorized"), State { next_error: None, backtrace: InternalBacktrace })
    Error(Msg("Unauthorized"), State { next_error: None, backtrace: InternalBacktrace })

I already enabled Spot Trading + Reading in API management but nothing change.

panic on losing internet connection in websockets

When running websockets event_loop() and any errors occur the program panics immediately, it would be nice to be able to recover from such an error and try connecting again to the websockets api.
Here everything is being unwrapped, instead of actually returning an error in the event_loop such that any external caller could recover from them.
websockets.rs:
let msg: String = socket.0.read_message().unwrap().into_text().unwrap();

Get better informations return by market buy request

Hello,

I'm actually using the market_buy/market_sell api for a trading bot.
But the return of those function is not very helpfull.
I first get a transaction object (after "market_buy").
With this transaction object i can then make a request "order_status" but unfortunately the return of order status don't give me info about the price or commission.

Here is the actual return of market_buy (transaction) and order_status (order)

RETURN MARKET_BUY
Transaction { symbol: "BTCUSDT", order_id: ..an_id_here.., client_order_id: "..an_id_here..",
transact_time: 1588629578067 }

RETURN ORDER STATUS
Order { symbol: "BTCUSDT", order_id: ..an_id_here.., client_order_id: "..an_id_here..",
price: 0.0, orig_qty: "0.00219300", executed_qty: "0.00219300", status: "FILLED",
time_in_force: "GTC", type_name: "MARKET", side: "BUY", stop_price: 0.0,
iceberg_qty: "0.00000000", time: 1588629578067 }

As we can see the price is equal to 0 in the return of order status.
Maybe there is another request to use to get more usefull information about the order but i didn't find which request.

I manage to look at the data fetch before the deserialization to see where I can get more information
Here is the raw data fetch before deserialization

RAW DATA AFTER MARKET BUY (before deserialization):
 "{\"symbol\":\"BTCUSDT\",\"orderId\":..an_id_here..,\"orderListId\":..an_id_here..,
\"clientOrderId\":\"..an_id_here..\",\"transactTime\":1588629578067,
\"price\":\"0.00000000\",\"origQty\":\"0.00219300\",\"executedQty\":\"0.00219300\",
\"cummulativeQuoteQty\":\"19.59425763\",\"status\":\"FILLED\",\"timeInForce\":\"GTC\",
\"type\":\"MARKET\",\"side\":\"BUY\",
\"fills\":[{\"price\":\"8934.91000000\",\"qty\":\"0.00219300\",\"commission\":\"0.00086106\",\"commissionAsset\":\"BNB\",\"tradeId\":309149253}]}"

RAW DATA AFTER ORDER STATUS (before deserialization):
"{\"symbol\":\"BTCUSDT\",\"orderId\":..an_id_here..,\"orderListId\":-1,
\"clientOrderId\":\"..an_id_here..\",\"price\":\"0.00000000\",\"origQty\":\"0.00219300\",
\"executedQty\":\"0.00219300\",\"cummulativeQuoteQty\":\"19.59425763\",
\"status\":\"FILLED\",\"timeInForce\":\"GTC\",\"type\":\"MARKET\",\"side\":\"BUY\",
\"stopPrice\":\"0.00000000\",\"icebergQty\":\"0.00000000\",\"time\":1588629578067,
\"updateTime\":1588629578067,\"isWorking\":true,\"origQuoteOrderQty\":\"0.00000000\"}"

We can see that the "raw data" fetch after the market buy request have usefull information which are not parsed. It could be very usefull to get the "fills" property to let the user calculate the average price + sum of comission.

I don't know how it can be done because I know it's not possible to get the same kind of return with the limit_order request (not directly executed).

Maybe the market_buy request could return an Order directly and we can add the "fills property" in an Option so it doesn't affect the return of order_status.

KlineSummary Serialization

Hi I don't know if this was asked before, but i noticed that KlineSummary struct doesn't implement Serialize/Deserialize macro. Was that intentional by design?

The use case for me would be getting historical data and saving it locally to test against.

Would this be something that I can make a PR for?

Get klines for all symbols asynchronously

I am trying to get klines for all symbols, I have created this function but having some problems as I am new to the Rust language. Do you have any solutions or suggestions, I also realized that there is Igosuki/binance-rs-async, but it lacks of futures API?

My approach to get klines for all symbols is:

  1. fetch exchangeinfo
  2. fetch klines for each symbol with a custom async reqwest client

Cargo.toml

[dependencies]
# ...
futures = "0.3"

./src/futures/market.rs

// ...

use futures::future;
use crate::api::*;
use crate::futures::general::*;

// ...
impl FuturesMarket {

    // ...

    pub async fn get_all_klines<S1, S2, S3, S4>(
        &self, interval: S1, limit: S2, start_time: S3, end_time: S4,
    ) -> Result<Vec<Result<KlineSummaries>>>
    where
        S1: Into<String>,
        S2: Into<Option<u16>>,
        S3: Into<Option<u64>>,
        S4: Into<Option<u64>>,
    {

        let general: FuturesGeneral = Binance::new(None, None);
        match general.exchange_info() {
            Err(e) => {
                eprintln!("error: {}", e);
                bail!("can't get all symbol names")
            },
            Ok(exchangeinfo) => {

                let client = reqwest::Client::new();
                let mut parameters: BTreeMap<String, String>;
                let urls: Vec<String>;

                // fill urls[]
                for symbol in exchangeinfo.symbols {
                    parameters = BTreeMap::new();
                    parameters.insert("symbol".into(), symbol.symbol.into());
                    parameters.insert("interval".into(), interval.into());
                    // Add three optional parameters
                    if let Some(lt) = limit.into() {
                        parameters.insert("limit".into(), format!("{}", lt));
                    }
                    if let Some(st) = start_time.into() {
                        parameters.insert("startTime".into(), format!("{}", st));
                    }
                    if let Some(et) = end_time.into() {
                        parameters.insert("endTime".into(), format!("{}", et));
                    }
                    urls.push(build_request(parameters));
                }

                let responses = future::join_all(urls.into_iter().map(|url| {
                    let client = &client;
                    let mut url: String = format!("{}{}", "https://fapi.binance.com".into(), String::from(API::Futures(Futures::Klines)));
                    url.push_str(format!("?{}", url).as_str());
                    async move {
                        println!("-> get {:?}", url);
                        let resp = client.get(url).send().await?;
                        resp.bytes().await
                    }
                })).await;

                let mut all_klines: Vec<Result<KlineSummaries>>;

                for response in responses {
                    response.iter().map(|r|
                        all_klines.push(
                            Ok(KlineSummaries::AllKlineSummaries(
                                r.iter()
                                    .map(|row| KlineSummary {
                                        open_time: to_i64(&row[0]),
                                        open: to_f64(&row[1]),
                                        high: to_f64(&row[2]),
                                        low: to_f64(&row[3]),
                                        close: to_f64(&row[4]),
                                        volume: to_f64(&row[5]),
                                        close_time: to_i64(&row[6]),
                                        quote_asset_volume: to_f64(&row[7]),
                                        number_of_trades: to_i64(&row[8]),
                                        taker_buy_base_asset_volume: to_f64(&row[9]),
                                        taker_buy_quote_asset_volume: to_f64(&row[10]),
                                    })
                                    .collect(),
                            ))
                        )
                    );
                }

                Ok(all_klines)

            }
        }
    }

    // ...

Error I got

   Compiling binance v0.16.1 (/Users/ihsan/Sync/code/github.com/ihsanturk/binance-rs)
error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:244:60
    |
244 | ...                   open_time: to_i64(&row[0]),
    |                                          ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:245:55
    |
245 | ...                   open: to_f64(&row[1]),
    |                                     ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:246:55
    |
246 | ...                   high: to_f64(&row[2]),
    |                                     ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:247:54
    |
247 | ...                   low: to_f64(&row[3]),
    |                                    ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:248:56
    |
248 | ...                   close: to_f64(&row[4]),
    |                                      ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:249:57
    |
249 | ...                   volume: to_f64(&row[5]),
    |                                       ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:250:61
    |
250 | ...                   close_time: to_i64(&row[6]),
    |                                           ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:251:69
    |
251 | ...                   quote_asset_volume: to_f64(&row[7]),
    |                                                   ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:252:67
    |
252 | ...                   number_of_trades: to_i64(&row[8]),
    |                                                 ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:253:78
    |
253 | ...                   taker_buy_base_asset_volume: to_f64(&row[9]),
    |                                                            ^^^^^^

error[E0608]: cannot index into a value of type `&u8`
   --> src/futures/market.rs:254:79
    |
254 | ...                   taker_buy_quote_asset_volume: to_f64(&row[10]),
    |                                                             ^^^^^^^

error: aborting due to 11 previous errors

For more information about this error, try `rustc --explain E0608`.
error: could not compile `binance`

To learn more, run the command again with --verbose.

Using orderQuoteQty in market orders

If you look at the API docs (https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#new-order--trade) for the MARKET order, one can specify either quantity or quoteOrderQty. The latter seems not to be implemented.

How can I do this in a nice way? My only way was to duplicate pub fn market_buy into a pub fn market_buy_using_quote_quantity which uses a OrderQuoteQuantityRequest iso a OrderRequest and a duplicated self.build_quote_quantity_order(buy) iso build_order(buy). This works but duplicates a lot of code... I guess there must be a nicer way in Rust to make this.

Websocket, DayTickerEvent is no longer a vector in Single Symbol Tickers.

Line 103 in websockets.rs

                        } else if msg.find(DAYTICKER) != None {
                            let trades: Vec<DayTickerEvent> = from_str(msg.as_str())?;
                            (self.handler)(WebsocketEvent::DayTicker(trades))?;

according to my debugging, msg.as_str() returns a json structure that isn't an array

(gdb) p msg.as_str()
$4 = "{\"e\":\"24hrTicker\",\"E\":1600800387836,\"s\":\"BCHUSDT\",\"p\":\"1.36000000\",\"P\":\"0.639\",\"w\":\"213.85752497\",\"x\":\"212.96000000\",\"c\":\"214.34000000\",\"Q\":\"0.23330000\",\"b\":\"214.23000000\",\"B\":\"30.39787000\",\"a\":\"214.28000000\",\"A\":\"1.71200000\",\"o\":\"212.98000000\",\"h\":\"215.99000000\",\"l\":\"210.48000000\",\"v\":\"72448.37057000\",\"q\":\"15493629.21838270\",\"O\":1600713979263,\"C\":1600800379263,\"F\":31831182,\"L\":31867465,\"n\":36284}"

The way to fix this would be

To change the type to not be a Vec in that code, and in the Enum WebsocketEvents, for individual tickers this is the case

WebSocket @trade not supported

Thank you for your work, overall.

But ;)
I couldn't receive any Trade events.

I found this part in websocket processing(stream to events):

} else if msg.find(AGGREGATED_TRADE) != None {
            let trade: TradesEvent = from_str(msg)?;
            (self.handler)(WebsocketEvent::Trade(trade))?

Its wrong, according to this:
https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md#trade-streams

aggrTrade only happens when you subscribe to @aggrTrade.
'trade' is the one it should respect - @trade.

EDIT: Upon review... the @trade just wasn't supported and Trade meant AggrTrades. This PR enables the @trade stream.

PR: #70

Kline summary

the trait bound binance::model::KlineSummaries: serde::Deserializer<'_> is not satisfied

the trait serde::Deserializer<'_> is not implemented for binance::model::KlineSummaries

note: required by serde::Deserialize::deserializerustc(E0277)
main.rs(255, 58): the trait serde::Deserializer<'_> is not implemented for binance::model::KlineSummaries

any can help me please?

Futures API

From what I can tell, this crate does not support the Binance's Futures API.

Are there any branches/alternate crates that support the API?

Alternatively, are there any plans to add the API to this crate?

Thanks

Stop orders

Hi, would like to use stop orders but I'm a bit confused, does this crate implement stop orders? It seems like it doesn't but not sure. Official API documentation contains:

  • LIMIT
  • MARKET
  • STOP_LOSS
  • STOP_LOSS_LIMIT
  • TAKE_PROFIT
  • TAKE_PROFIT_LIMIT
  • LIMIT_MAKER

Breaking the event_loop

I want to subscribe to a user stream up until a certain point (e.g. all open orders filled), then close the stream. I'm blocking a thread like this to watch the stream: ws.event_loop(). I don't see any method for this in the WebSockets struct. Is there a recommended way to break the loop?

If not, I can submit a PR. Any guidance on that would be appreciated.

Precision is over the maximum defined for this asset.

Hello,

Is there an easy way, to handle f64's for account.market_buy?

I get numbers like this qty: 119.96000000000001 , so no surprise, the server gives me this error message.

As I see now, it is not possible with f64, but market_buy need f64.

I consider to make a Decimal version of your asset.rs.

Typo in api.rs Spot::Ticker24hr

Hello,

Thanks for sharing this wonderful library. I believe there is a typo in api.rs, we need to add "r" at the end of the address. It is causing 404 error.

currently:
Spot::Ticker24hr => "/api/v3/ticker/24h",

correct:
Spot::Ticker24hr => "/api/v3/ticker/24hr",

Best regards,
Mustafa

Getting prices that match the UI

This is a great project and very useful in a project I'm working on.

However I can't seem to connect to the websocket correctly to get prices that match the web UI's price

I've followed the example code on the README at WEBSOCKETS - TRADES

let agg_trade: String = format!("!ticker@arr");
let mut btcusdt: f32 = "0".parse().unwrap();

let mut web_socket: WebSockets = WebSockets::new(|event: WebsocketEvent| {
    match event {
        WebsocketEvent::DayTicker(ticker_events) => {
            for tick_event in ticker_events {
                if tick_event.symbol == "BTCUSDT" {
                    btcusdt = tick_event.average_price.parse().unwrap();
                }
            }
        },
        _ => return,
    }
});

But for example - as I'm writing:

BTCUSDT @ $7967 on Binance.com 
BTCUSDT @ $7855.4326 from the websocket (above)

How should I change the websocket to get prices that match the UI?

Unified approach for all future markets

The current implementation of futures endpoints is geared mainly to the USD-M futures (link): e.g. https://github.com/wisespace-io/binance-rs/blob/master/src/config.rs#L18 and https://github.com/wisespace-io/binance-rs/blob/master/src/api.rs#L129 all have fapi hardcoded, but there is also dapi for the COIN-M futures (link) and vapi for the Vanilla options (link). I guess this is historical, since the USD-M futures first were the only type offered and the others are added laters (leading to confusing urls "futures" and "delivery" for USD-M and COIN-M respectively).

To properly support those futures types I see two options:

  1. add new methods for all other future types. This is what python-binance did: you can see at https://python-binance.readthedocs.io/en/latest/binance.html there's methods like futures_account and futures_coin_account_balance: each of the *_coin_* methods is a duplicate to serve the COIN-M futures.
  2. create new methods that require an extra future market type (e.g. COINM, USDM, VANILLA).

My preference is the 2nd method as it makes the API much simpler and code can be re-used, but this would be a major breaking change to the current API. What do you think?

Get the price of a symbol at a specific date

Hello,

Nice job on this code ! You have really cool feature on your api
Did I miss something about the api to get price symbol ?

I would like to be able to get the prince of a specific symbol at a specific date ? Is it possible with this api ?

Thanks,
Eva.

Unable to connect to more than one markets orderbooks

Hi sorry to bother you again @wisespace-io ... i've been pulling my hair out this morning trying to connect to more that one markets orderbook stream.

A small example is something like this

    web_socket.add_market_handler(stream.clone());
    let partial_depth1: String = format!("{}@depth", "ethbtc");
    let partial_depth2: String = format!("{}@depth", "xrpbtc");

    web_socket.connect(&partial_depth1).unwrap(); // check error
    web_socket.connect(&partial_depth2).unwrap(); // check error
    web_socket.event_loop();

I event tried copying the market_websocket() method in the binance_websocket example, gave the two methods different names, e.g market_websocket1() & market_websocket2(). Inside each one I tried to spawn a new market. However only one market only ever gets printed to the console.

My end goal is to have a websocket connection to every market on binance and receive their orderbooks. Am I missing something super obvious or currently can this crate only connect and stream orderbooks from only ever a single market?

Appreciate any advice. Cheers.

Error on sample get_depth

Fail to make sample get_depth api call.

extern crate binance;

use binance::api::*;
use binance::market::*;

fn main() {
    let market: Market = Binance::new(None, None);

    // Order book
    match market.get_depth("ETHBTC") {
        Ok(answer) => println!("{:?}", answer),
        Err(e) => println!("Error: {}", e),
    }
}

Result

Error: invalid length 2, expected struct Bids with 3 elements at line 1 column 61

Cargo version

cargo 1.35.0 (6f3e9c367 2019-04-04)

Using sessions in the reqwest handling

The current code will open a new HTTPS sessions for each request.
The underlying reqwest library seems to be capable using the session capability to re-use connections (see https://docs.rs/reqwest/0.11.1/reqwest/, "NOTE: If you plan to perform multiple requests, it is best to create a Client and reuse it, taking advantage of keep-alive connection pooling.").

It seems to make sense anyway to refactor the current, repeated let client = reqwest::blocking::Client::new(); from src\client.rs into a field of pub struct Client. This might already directly allow for connection pooling. I didn't try it yet though, I'll try somewhere in the upcoming weeks, but maybe somebody already did this?

Build failed with tungstenite and ring

Excuse if this is a naive question, as I'm new to Rust -

With the version bumps, I'm unable to build. Specifically, I get the following errors:

  1. ring 0.16.7: error[E0658]: use of unstable library feature 'alloc': this library is unlikely to be stabilized in its current form or name (see issue #27783)
  2. tungstenite 0.9.1: error[E0505]: cannot move out of request because it is borrowed

Error with kindle websocket

Hi, I follow your exemple code for websocket kindle and have an error

  extern crate binance;
  
 use binance::api::*;
   use binance::websockets::*;
  
   fn main() {
   let keep_running = AtomicBool::new(true); // Used to control the event loop
   let kline: String = format!("{}", "ethbtc@kline_1m");
   let mut web_socket: WebSockets = WebSockets::new(|event: WebsocketEvent| {                                                                                                                                           match event {
          WebsocketEvent::Kline(kline_event) => {
              println!(
                  "Symbol: {}, high: {}, low: {}",
                  kline_event.kline.symbol, kline_event.kline.low, kline_event.kline.high
              );
          },
          _ => (),
      };                                                                                                                                                                                                               Ok(())
  });
 
  web_socket.connect(&kline).unwrap(); // check error
  if let Err(e) = web_socket.event_loop(&keep_running) {
      match e {
          err => {
             println!("Error: {}", err);
          }
      }
  }
  web_socket.disconnect().unwrap();
  }

Error :

error[E0433]: failed to resolve: use of undeclared type or module `AtomicBool`
 --> src/main.rs:7:20
  |
7 | let keep_running = AtomicBool::new(true); // Used to control the event loop
  |                    ^^^^^^^^^^ use of undeclared type or module `AtomicBool`

warning: unused import: `binance::api`
 --> src/main.rs:3:5
  |
3 | use binance::api::*;
  |     ^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0433`.
error: could not compile `rsbot`.

To learn more, run the command again with --verbose.

[BUG] Exchange info deserializer not working

Hello,

First, thank you for your work.
I'm moving a project from nodejs which is using ccxt to rust and I was really happy to found this library to going faster.
But I found something weird when using the General api.
It looks like there is a problem while deserializing exchange information

let general: General = Binance::new(None, None);
let info = match general.exchange_info() {
                Ok(info) => info,
                Err(e) => panic!("Cannot get exchange info [{:?}]", e),
};

I'm getting this error

Error(Json(Error("missing field `limit`", line: 1, column: 1399)), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })

I look at the 1399 column of the exchange info result
Which after "maxNumOrders":200
{"filterType":"MAX_NUM_ORDERS","maxNumOrders":200}

And it looks like there is no limit field but in your model in the enum filter it is define with

    #[serde(rename = "MAX_NUM_ORDERS")]
    #[serde(rename_all = "camelCase")]
    MaxNumOrders { limit: u16 },

I replace in my local build with

    MaxNumOrders { },

And it works.

If you want I can manage to do a PR.
Or if you can handle the correction.
Thank you.

async/await compatible -- reqwest 0.10 alpha

This API would be phenomenal if it were completely async/await ready. reqwest 0.10 now supports it, so it is a natural upgrade to this to start decorating the whole api with async/await.

Messages from websocket stream are queued

Hi, thank you for developing this project, it's really great! I'm interested in how to deal with queuing websockets responses.
Here is a code example to explain, what I'm interested in.

let mut web_socket: WebSockets = WebSockets::new(|event: WebsocketEvent| {
    if let WebsocketEvent::BookTicker(tick_event) = event {
        if tick_event.symbol == "ADAUSDT" { 
            keep_running.swap(false, Ordering::Relaxed);
            println!("Event {:?}\nTime: {}\n{:?}\n", tick_event, Utc::now().format("%Y-%m-%d %H:%M:%S.%f"), keep_running);

            thread::sleep(time::Duration::from_secs(5)); // Delay
            keep_running.swap(true, Ordering::Relaxed);
        }
    }

    return Ok(());
});

For example, if we received "ADA/USDT" event at 01:00:00, after 5 seconds delay we will receive the rest of the data from 01:00:00 to 01:00:05, which I`m not interested in. Is there a way to check if the event is relevant or just stop queuing messages?

cancel_all_open_orders should return OrderCanceled type

The function account.cancel_all_open_orders aims to return Vec<Order>, but this doesn't work:

Error: Error(Json(Error("missing field `stopPrice`", line: 1, column: 313)), State { next_error: None, backtrace: IntenalBacktrace })

It should be OrderCanceled.

From the API https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#cancel-all-open-orders-on-a-symbol-trade I see that the response on a canceled OCO is even more complicated; I didn't test this but I think this is still broken.

Add Binance.US support

Can this library be modified to make it easier to use with Binance.US? In most cases, this involves using endpoints like api.binance.us instead of api.binance.com. As far as I'm aware, the APIs are identical.

In most places, static constants seem to be used for defining hosts, particularly when it comes to websockets. It might be nice to create an Enum of supported hosts and then allow that to be passed into the Binance::new and WebSockets::connect functions.

no TAKE_PROFIT_LIMIT

hi, there is no TAKE_PROFIT_LIMIT order implemented
I was trying to implement it by myself but have no idea where build_signed_request is implemented

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.