/// <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)); }
/// <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)); }
/// <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)); }
/// <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)); }
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); } } }
public Task <string> DeleteAsync(BinanceHttpRequest request, CancellationToken token = default) { return(RequestAsync(HttpMethod.Delete, request, token)); }
/// <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)); }
/// <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)); }
public Task <string> DeleteAsync(BinanceHttpRequest request, CancellationToken token = default, IApiRateLimiter rateLimiter = null) { return(RequestAsync(HttpMethod.Delete, request, token, rateLimiter)); }
public Task <string> PutAsync(BinanceHttpRequest request, CancellationToken token = default, IApiRateLimiter rateLimiter = null) { return(RequestAsync(HttpMethod.Put, request, rateLimiter ?? RateLimiter, token)); }