Example #1
0
        /// <summary>
        /// Get order book (market depth) of a symbol.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="symbol"></param>
        /// <param name="limit">Default 100; max 1000. Valid limits:[5, 10, 20, 50, 100, 500, 1000].</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetOrderBookAsync(this IBinanceHttpClient client, string symbol, int limit = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter
                .DelayAsync(limit >= 1000? 10 : limit >= 500? 5 : 1, token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/api/v1/depth");

            request.AddParameter("symbol", symbol.FormatSymbol());

            if (limit > 0)
            {
                request.AddParameter("limit", limit);
            }

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Example #2
0
        /// <summary>
        /// Get the deposit address for an asset.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="asset"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetDepositAddressAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(asset, nameof(asset));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/wapi/v3/depositAddress.html")
            {
                ApiKey = user.ApiKey
            };

            request.AddParameter("asset", asset.FormatSymbol());

            await client.SignAsync(request, user, token)
            .ConfigureAwait(false);

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Example #3
0
        /// <summary>
        /// Get older (non-compressed) trades.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="apiKey"></param>
        /// <param name="symbol"></param>
        /// <param name="fromId">TradeId to fetch from. Default gets most recent trades.</param>
        /// <param name="limit">Default 500; max 500.</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static Task <string> GetTradesAsync(this IBinanceHttpClient client, string apiKey, string symbol, long fromId = BinanceApi.NullId, int limit = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            var request = new BinanceHttpRequest("/api/v1/historicalTrades", 100)
            {
                ApiKey = apiKey
            };

            request.AddParameter("symbol", symbol.FormatSymbol());

            if (fromId >= 0)
            {
                request.AddParameter("fromId", fromId);
            }

            if (limit > 0)
            {
                request.AddParameter("limit", limit);
            }

            return(client.GetAsync(request, token));
        }
Example #4
0
        /// <summary>
        /// Get the deposit address for an asset.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="asset"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetDepositAddressAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(asset, nameof(asset));

            var request = new BinanceHttpRequest($"/wapi/v3/depositAddress.html")
            {
                ApiKey = user.ApiKey
            };

            request.AddParameter("asset", asset.FormatSymbol());

            var timestamp = await client.GetTimestampAsync(token)
                            .ConfigureAwait(false);

            request.AddParameter("timestamp", timestamp);

            var signature = user.Sign(request.QueryString);

            request.AddParameter("signature", signature);

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
        /// <summary>
        /// Send in a new order.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="symbol"></param>
        /// <param name="side"></param>
        /// <param name="type"></param>
        /// <param name="quantity"></param>
        /// <param name="price"></param>
        /// <param name="newClientOrderId">A unique id for the order. Automatically generated if not sent.</param>
        /// <param name="timeInForce"></param>
        /// <param name="stopPrice">Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.</param>
        /// <param name="icebergQty">Used with iceberg orders.</param>
        /// <param name="recvWindow"></param>
        /// <param name="isTestOnly">If true, test new order creation and signature/recvWindow; creates and validates a new order but does not send it into the matching engine.</param>
        /// <param name="newOrderRespType">Set the response JSON.</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> PlaceOrderAsync(this IBinanceHttpClient client, IBinanceApiUser user, string symbol, OrderSide side, OrderType type, decimal quantity, decimal price, string newClientOrderId = null, TimeInForce?timeInForce = null, decimal stopPrice = 0, decimal icebergQty = 0, long recvWindow = default, bool isTestOnly = false, PlaceOrderResponseType newOrderRespType = PlaceOrderResponseType.Result, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNull(user, nameof(user));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (quantity <= 0)
            {
                throw new ArgumentException("Order quantity must be greater than 0.", nameof(quantity));
            }

            if (recvWindow <= 0)
            {
                recvWindow = client.Options.RecvWindowDefault ?? 0;
            }

            var request = new BinanceHttpRequest($"/api/v3/order{(isTestOnly ? "/test" : string.Empty)}")
            {
                ApiKey = user.ApiKey
            };

            request.AddParameter("symbol", symbol.FormatSymbol());
            request.AddParameter("side", side.ToString().ToUpperInvariant());
            request.AddParameter("type", type.AsString());
            request.AddParameter("newOrderRespType", newOrderRespType.ToString().ToUpperInvariant());
            request.AddParameter("quantity", quantity);

            if (price > 0)
            {
                request.AddParameter("price", price);
            }

            if (timeInForce.HasValue)
            {
                request.AddParameter("timeInForce", timeInForce.ToString().ToUpperInvariant());
            }

            if (!string.IsNullOrWhiteSpace(newClientOrderId))
            {
                request.AddParameter("newClientOrderId", newClientOrderId);
            }

            if (stopPrice > 0)
            {
                request.AddParameter("stopPrice", stopPrice);
            }

            if (icebergQty > 0)
            {
                // Automatically set time-in-force to GTC if not set.
                if (!timeInForce.HasValue)
                {
                    timeInForce = TimeInForce.GTC;
                }

                if (timeInForce != TimeInForce.GTC)
                {
                    throw new BinanceApiException("Any order with an icebergQty MUST have timeInForce set to GTC.");
                }

                request.AddParameter("icebergQty", icebergQty);
            }

            if (recvWindow > 0)
            {
                request.AddParameter("recvWindow", recvWindow);
            }

            await client.SignAsync(request, user, token)
            .ConfigureAwait(false);

            return(await client.PostAsync(request, token, user.RateLimiter)
                   .ConfigureAwait(false));
        }
Example #6
0
        private async Task <string> RequestAsync(HttpMethod method, BinanceHttpRequest request, CancellationToken token = default)
        {
            Throw.IfNull(request, nameof(request));

            token.ThrowIfCancellationRequested();

            var requestMessage = request.CreateMessage(method);

            Logger?.LogDebug($"{nameof(BinanceHttpClient)}.{nameof(RequestAsync)}: [{method.Method}] \"{requestMessage.RequestUri}\"");

            using (var response = await _httpClient.SendAsync(requestMessage, token).ConfigureAwait(false))
            {
                if (response.IsSuccessStatusCode)
                {
                    var json = await response.Content.ReadAsStringAsync()
                               .ConfigureAwait(false);

                    OnMessage(json, requestMessage.RequestUri.AbsolutePath);

                    return(json);
                }

                if (response.StatusCode == HttpStatusCode.GatewayTimeout)
                {
                    throw new BinanceUnknownStatusException();
                }

                var error = await response.Content.ReadAsStringAsync()
                            .ConfigureAwait(false);

                var    errorCode    = 0;
                string errorMessage = null;

                // ReSharper disable once InvertIf
                if (!string.IsNullOrWhiteSpace(error) && error.IsJsonObject())
                {
                    try // to parse server error response.
                    {
                        var jObject = JObject.Parse(error);

                        errorCode    = jObject["code"]?.Value <int>() ?? 0;
                        errorMessage = jObject["msg"]?.Value <string>();
                    }
                    catch (Exception e)
                    {
                        Logger?.LogError(e, $"{nameof(BinanceHttpClient)}.{nameof(RequestAsync)} failed to parse server error response: \"{error}\"");
                    }
                }

                OnMessage(error, requestMessage.RequestUri.AbsolutePath);

                // ReSharper disable once SwitchStatementMissingSomeCases
                switch (response.StatusCode)
                {
                case (HttpStatusCode)429:
                    throw new BinanceRequestRateLimitExceededException(response.ReasonPhrase, errorCode, errorMessage);

                case (HttpStatusCode)418:
                    throw new BinanceRequestRateLimitIpBanException(response.ReasonPhrase, errorCode, errorMessage);

                default:
                    throw new BinanceHttpException(response.StatusCode, response.ReasonPhrase, errorCode, errorMessage);
                }
            }
        }
Example #7
0
 public Task <string> DeleteAsync(BinanceHttpRequest request, CancellationToken token = default)
 {
     return(RequestAsync(HttpMethod.Delete, request, token));
 }
Example #8
0
        /// <summary>
        /// Get the deposit history.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="asset"></param>
        /// <param name="status"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="recvWindow"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetDepositsAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset = null, DepositStatus?status = null, DateTime startTime = default, DateTime endTime = default, long recvWindow = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNull(user, nameof(user));

            if (recvWindow <= 0)
            {
                recvWindow = client.Options.RecvWindowDefault ?? 0;
            }

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/wapi/v3/depositHistory.html")
            {
                ApiKey = user.ApiKey
            };

            if (!string.IsNullOrWhiteSpace(asset))
            {
                request.AddParameter("asset", asset.FormatSymbol());
            }

            if (status.HasValue)
            {
                request.AddParameter("status", (int)status);
            }

            if (startTime != default)
            {
                if (startTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(startTime));
                }

                request.AddParameter("startTime", startTime.ToTimestamp());
            }

            if (endTime != default)
            {
                if (endTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(endTime));
                }

                request.AddParameter("endTime", endTime.ToTimestamp());
            }

            if (recvWindow > 0)
            {
                request.AddParameter("recvWindow", recvWindow);
            }

            await client.SignAsync(request, user, token)
            .ConfigureAwait(false);

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Example #9
0
        /// <summary>
        /// Get compressed, aggregate trades. Trades that fill at the time, from the same order, with the same price will have the quantity aggregated.
        /// If fromdId, startTime, and endTime are not sent, the most recent aggregate trades will be returned.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="symbol"></param>
        /// <param name="fromId">ID to get aggregate trades from INCLUSIVE.</param>
        /// <param name="limit">Default 500; max 500.</param>
        /// <param name="startTime">Timestamp in ms to get aggregate trades from INCLUSIVE.</param>
        /// <param name="endTime">Timestamp in ms to get aggregate trades until INCLUSIVE.</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetAggregateTradesAsync(this IBinanceHttpClient client, string symbol, long fromId = BinanceApi.NullId, int limit = default, DateTime startTime = default, DateTime endTime = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/api/v1/aggTrades");

            request.AddParameter("symbol", symbol.FormatSymbol());

            if (fromId >= 0)
            {
                request.AddParameter("fromId", fromId);
            }

            if (startTime != default)
            {
                if (startTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(startTime));
                }

                request.AddParameter("startTime", startTime.ToTimestamp());
            }

            if (endTime != default)
            {
                if (endTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(endTime));
                }

                request.AddParameter("endTime", endTime.ToTimestamp());
            }

            if (startTime == default || endTime == default)
            {
                if (limit > 0)
                {
                    request.AddParameter("limit", limit);
                }
            }
            else
            {
                if (endTime < startTime)
                {
                    throw new ArgumentException($"Time ({nameof(endTime)}) must not be less than {nameof(startTime)} ({startTime}).", nameof(endTime));
                }

                if ((endTime - startTime).Duration() >= TimeSpan.FromHours(24))
                {
                    throw new ArgumentException($"The interval between {nameof(startTime)} and {nameof(endTime)} must be less than 24 hours.", nameof(endTime));
                }
            }

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Example #10
0
 public Task <string> DeleteAsync(BinanceHttpRequest request, CancellationToken token = default, IApiRateLimiter rateLimiter = null)
 {
     return(RequestAsync(HttpMethod.Delete, request, token, rateLimiter));
 }
Example #11
0
 public Task <string> PutAsync(BinanceHttpRequest request, CancellationToken token = default, IApiRateLimiter rateLimiter = null)
 {
     return(RequestAsync(HttpMethod.Put, request, rateLimiter ?? RateLimiter, token));
 }