/// <summary> /// Cancels a pending order /// </summary> /// <param name="symbol">The symbol the order is for</param> /// <param name="orderId">The order id of the order</param> /// <param name="origClientOrderId">The client order id of the order</param> /// <param name="newClientOrderId">Unique identifier for this cancel</param> /// <param name="recvWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>Id's for canceled order</returns> public async Task <BinanceApiResult <BinanceCanceledOrder> > CancelOrderAsync(string symbol, long?orderId = null, string origClientOrderId = null, string newClientOrderId = null, long?recvWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceCanceledOrder>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "symbol", symbol }, { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "orderId", orderId?.ToString()); AddOptionalParameter(parameters, "origClientOrderId", origClientOrderId); AddOptionalParameter(parameters, "newClientOrderId", newClientOrderId); AddOptionalParameter(parameters, "recvWindow", recvWindow?.ToString()); return(await ExecuteRequest <BinanceCanceledOrder>(GetUrl(CancelOrderEndpoint, Api, SignedVersion, parameters), true, DeleteMethod)); }
private BinanceApiResult <BinanceStream> CreateSocket(string url) { try { var socket = SocketFactory.CreateWebsocket(url); var socketObject = new BinanceStream() { Socket = socket, StreamId = NextStreamId() }; socket.SetEnabledSslProtocols(protocols); socket.OnClose += (obj, args) => Socket_OnClose(socketObject, args); socket.OnError += (obj, args) => Socket_OnError(socketObject, args); socket.OnOpen += (obj, args) => Socket_OnOpen(socketObject, args); socket.Connect(); lock (sockets) sockets.Add(socketObject); return(new BinanceApiResult <BinanceStream>() { Data = socketObject, Success = true }); } catch (Exception e) { var errorMessage = $"Couldn't open socket stream: {e.Message}"; log.Write(LogVerbosity.Error, errorMessage); return(ThrowErrorMessage <BinanceStream>(BinanceErrors.GetError(BinanceErrorKey.CantConnectToServer), errorMessage)); } }
/// <summary> /// Places a new test order. Test orders are not actually being executed and just test the functionality. /// </summary> /// <param name="symbol">The symbol the order is for</param> /// <param name="side">The order side (buy/sell)</param> /// <param name="type">The order type (limit/market)</param> /// <param name="timeInForce">Lifetime of the order (GoodTillCancel/ImmediateOrCancel)</param> /// <param name="quantity">The amount of the symbol</param> /// <param name="price">The price to use</param> /// <param name="newClientOrderId">Unique id for order</param> /// <param name="stopPrice">Used for stop orders</param> /// <param name="icebergQty">User for iceberg orders</param> /// <returns>Id's for the placed test order</returns> public async Task <BinanceApiResult <BinancePlacedOrder> > PlaceTestOrderAsync(string symbol, OrderSide side, OrderType type, TimeInForce timeInForce, decimal quantity, decimal price, string newClientOrderId = null, decimal?stopPrice = null, decimal?icebergQty = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinancePlacedOrder>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "symbol", symbol }, { "side", JsonConvert.SerializeObject(side, new OrderSideConverter(false)) }, { "type", JsonConvert.SerializeObject(type, new OrderTypeConverter(false)) }, { "timeInForce", JsonConvert.SerializeObject(timeInForce, new TimeInForceConverter(false)) }, { "quantity", quantity.ToString(CultureInfo.InvariantCulture) }, { "price", price.ToString(CultureInfo.InvariantCulture) }, { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "newClientOrderId", newClientOrderId); AddOptionalParameter(parameters, "stopPrice", stopPrice?.ToString()); AddOptionalParameter(parameters, "icebergQty", icebergQty?.ToString()); return(await ExecuteRequest <BinancePlacedOrder>(GetUrl(NewTestOrderEndpoint, Api, SignedVersion, parameters), true, PostMethod)); }
/// <summary> /// Subscribes to the account update stream. Prior to using this, the <see cref="BinanceClient.StartUserStream"/> method should be called. /// </summary> /// <param name="listenKey">Listen key retrieved by the StartUserStream method</param> /// <param name="onAccountInfoMessage">The event handler for whenever an account info update is received</param> /// <param name="OnOrderUpdateMessage">The event handler for whenever an order status update is received</param> /// <returns>A stream subscription. This stream subscription can be used to be notified when the socket is closed and can close this specific stream /// using the <see cref="UnsubscribeFromStream(BinanceStreamSubscription)"/> method</returns> public BinanceApiResult <BinanceStreamSubscription> SubscribeToUserStream(string listenKey, Action <BinanceStreamAccountInfo> onAccountInfoMessage, Action <BinanceStreamOrderUpdate> OnOrderUpdateMessage) { if (string.IsNullOrEmpty(listenKey)) { return(ThrowErrorMessage <BinanceStreamSubscription>(BinanceErrors.GetError(BinanceErrorKey.NoListenKey))); } return(CreateUserStream(listenKey, onAccountInfoMessage, OnOrderUpdateMessage)); }
/// <summary> /// Subscribes to the order update stream. Prior to using this, the <see cref="BinanceClient.StartUserStream"/> method should be called. /// </summary> /// <param name="listenKey">Listen key retrieved by the StartUserStream method</param> /// <param name="onMessage">The event handler for the data received</param> /// <returns>bool indicating success</returns> public BinanceApiResult <bool> SubscribeToOrderUpdateStream(string listenKey, Action <BinanceStreamOrderUpdate> onMessage) { if (string.IsNullOrEmpty(listenKey)) { return(ThrowErrorMessage <bool>(BinanceErrors.GetError(BinanceErrorKey.NoListenKey))); } orderUpdateCallback = onMessage; return(CreateUserStream(listenKey)); }
/// <summary> /// Starts a user stream by requesting a listen key. This listen key can be used in subsequent requests to <see cref="BinanceSocketClient.SubscribeToAccountUpdateStream"/> and <see cref="BinanceSocketClient.SubscribeToOrderUpdateStream"/> /// </summary> /// <returns>Listen key</returns> public async Task <BinanceApiResult <BinanceListenKey> > StartUserStreamAsync() { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceListenKey>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } return(await ExecuteRequest <BinanceListenKey>(GetUrl(GetListenKeyEndpoint, Api, UserDataStreamVersion), false, PostMethod)); }
/// <summary> /// Stops the current user stream /// </summary> /// <returns></returns> public async Task <BinanceApiResult <object> > StopUserStreamAsync(string listenKey) { if (key == null || encryptor == null) { return(ThrowErrorMessage <object>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "listenKey", listenKey }, }; return(await ExecuteRequest <object>(GetUrl(CloseListenKeyEndpoint, Api, UserDataStreamVersion, parameters), false, DeleteMethod)); }
/// <summary> /// Gets the withdrawal history /// </summary> /// <param name="asset">Filter by asset</param> /// <param name="status">Filter by status</param> /// <param name="startTime">Filter start time from</param> /// <param name="endTime">Filter end time till</param> /// <param name="recvWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>List of withdrawals</returns> public async Task <BinanceApiResult <BinanceWithdrawalList> > GetWithdrawHistoryAsync(string asset = null, WithdrawalStatus?status = null, DateTime?startTime = null, DateTime?endTime = null, long?recvWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceWithdrawalList>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "asset", asset); AddOptionalParameter(parameters, "status", status != null ? JsonConvert.SerializeObject(status, new WithdrawalStatusConverter(false)): null); AddOptionalParameter(parameters, "startTime", startTime != null ? ToUnixTimestamp(startTime.Value).ToString(CultureInfo.InvariantCulture) : null); AddOptionalParameter(parameters, "endTime", endTime != null ? ToUnixTimestamp(endTime.Value).ToString(CultureInfo.InvariantCulture) : null); AddOptionalParameter(parameters, "recvWindow", recvWindow?.ToString()); var result = await ExecuteRequest <BinanceWithdrawalList>(GetUrl(WithdrawHistoryEndpoint, WithdrawalApi, WithdrawalVersion, parameters), true, PostMethod); if (!result.Success || result.Data == null) { return(result); } result.Success = result.Data.Success; if (!result.Success) { result.Error = new BinanceError() { Message = result.Data.Message } } ; return(result); }
/// <summary> /// Withdraw assets from Binance to an address /// </summary> /// <param name="asset">The asset to withdraw</param> /// <param name="address">The address to send the funds to</param> /// <param name="amount">The amount to withdraw</param> /// <param name="name">Name for the transaction</param> /// <param name="recvWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>Withdrawal confirmation</returns> public async Task <BinanceApiResult <BinanceWithdrawalPlaced> > WithdrawAsync(string asset, string address, decimal amount, string name = null, long?recvWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceWithdrawalPlaced>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "asset", asset }, { "address", address }, { "amount", amount.ToString(CultureInfo.InvariantCulture) }, { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "name", name); AddOptionalParameter(parameters, "recvWindow", recvWindow?.ToString()); var result = await ExecuteRequest <BinanceWithdrawalPlaced>(GetUrl(WithdrawEndpoint, WithdrawalApi, WithdrawalVersion, parameters), true, PostMethod); if (!result.Success || result.Data == null) { return(result); } result.Success = result.Data.Success; if (!result.Success) { result.Error = new BinanceError() { Message = result.Data.Message } } ; return(result); }
/// <summary> /// Gets account information, including balances /// </summary> /// <param name="recvWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>The account information</returns> public async Task <BinanceApiResult <BinanceAccountInfo> > GetAccountInfoAsync(long?recvWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceAccountInfo>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "recvWindow", recvWindow?.ToString()); return(await ExecuteRequest <BinanceAccountInfo>(GetUrl(AccountInfoEndpoint, Api, SignedVersion, parameters), true, GetMethod)); }
/// <summary> /// Gets a list of open orders for the provided symbol /// </summary> /// <param name="symbol">The symbol to get open orders for</param> /// <param name="receiveWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>List of open orders</returns> public async Task <BinanceApiResult <BinanceOrder[]> > GetOpenOrdersAsync(string symbol, int?receiveWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceOrder[]>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "symbol", symbol }, { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "recvWindow", receiveWindow?.ToString()); return(await ExecuteRequest <BinanceOrder[]>(GetUrl(OpenOrdersEndpoint, Api, SignedVersion, parameters), true)); }
/// <summary> /// Gets all user trades for provided symbol /// </summary> /// <param name="symbol">Symbol to get trades for</param> /// <param name="limit">The max number of results</param> /// <param name="fromId">TradeId to fetch from. Default gets most recent trades</param> /// <param name="recvWindow">The receive window for which this request is active. When the request takes longer than this to complete the server will reject the request</param> /// <returns>List of trades</returns> public async Task <BinanceApiResult <BinanceTrade[]> > GetMyTradesAsync(string symbol, int?limit = null, long?fromId = null, long?recvWindow = null) { if (key == null || encryptor == null) { return(ThrowErrorMessage <BinanceTrade[]>(BinanceErrors.GetError(BinanceErrorKey.NoApiCredentialsProvided))); } if (AutoTimestamp && !timeSynced) { await GetServerTimeAsync(); } var parameters = new Dictionary <string, string>() { { "symbol", symbol }, { "timestamp", GetTimestamp() } }; AddOptionalParameter(parameters, "limit", limit?.ToString(CultureInfo.InvariantCulture)); AddOptionalParameter(parameters, "fromId", fromId?.ToString(CultureInfo.InvariantCulture)); AddOptionalParameter(parameters, "recvWindow", recvWindow?.ToString(CultureInfo.InvariantCulture)); return(await ExecuteRequest <BinanceTrade[]>(GetUrl(MyTradesEndpoint, Api, SignedVersion, parameters), true, GetMethod)); }
private async Task <BinanceApiResult <T> > ExecuteRequest <T>(Uri uri, bool signed = false, string method = GetMethod, int currentTry = 0) { var apiResult = (BinanceApiResult <T>)Activator.CreateInstance(typeof(BinanceApiResult <T>)); string returnedData = ""; try { var uriString = uri.ToString(); if (signed) { uriString += $"&signature={ByteToString(encryptor.ComputeHash(Encoding.UTF8.GetBytes(uri.Query.Replace("?", ""))))}"; } var request = RequestFactory.Create(uriString); request.Headers.Add("X-MBX-APIKEY", key); request.Method = method; log.Write(LogVerbosity.Debug, $"Sending {method} request to {uriString}"); var response = request.GetResponse(); using (var reader = new StreamReader(response.GetResponseStream())) { returnedData = await reader.ReadToEndAsync(); var result = JsonConvert.DeserializeObject <T>(returnedData); apiResult.Success = true; apiResult.Data = result; return(apiResult); } } catch (WebException we) { var response = (HttpWebResponse)we.Response; if ((int)response.StatusCode >= 400) { try { using (var reader = new StreamReader(response.GetResponseStream())) { var error = JsonConvert.DeserializeObject <BinanceError>(await reader.ReadToEndAsync()); apiResult.Success = false; apiResult.Error = error; log.Write(LogVerbosity.Warning, $"Request to {uri} returned an error: {apiResult.Error?.Code} - {apiResult.Error?.Message}"); return(apiResult); } } catch (Exception) { log.Write(LogVerbosity.Warning, $"Couldn't parse error response for status code {response.StatusCode}"); } } if (currentTry < MaxRetries) { return(await ExecuteRequest <T>(uri, signed, method, ++currentTry)); } return(ThrowErrorMessage <T>(BinanceErrors.GetError(BinanceErrorKey.ErrorWeb), $"Status: {response.StatusCode}-{response.StatusDescription}, Message: {we.Message}")); } catch (JsonSerializationException jse) { return(ThrowErrorMessage <T>(BinanceErrors.GetError(BinanceErrorKey.ParseErrorSerialization), $"Message: {jse.Message}. Received data: {returnedData}")); } catch (JsonReaderException jre) { return(ThrowErrorMessage <T>(BinanceErrors.GetError(BinanceErrorKey.ParseErrorReader), $"Error occured at Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {returnedData}")); } catch (Exception e) { return(ThrowErrorMessage <T>(BinanceErrors.GetError(BinanceErrorKey.UnknownError), $"Message: {e.Message}")); } }