public virtual IUserDataClient Subscribe <TEventArgs>(string listenKey, IBinanceApiUser user, Action <TEventArgs> callback) where TEventArgs : UserDataEventArgs { Logger?.LogDebug($"{nameof(UserDataClient)}.{nameof(Subscribe)}: \"{listenKey}\" (callback: {(callback == null ? "no" : "yes")}). [thread: {Thread.CurrentThread.ManagedThreadId}]"); var type = typeof(TEventArgs); if (type == typeof(AccountUpdateEventArgs)) { // ReSharper disable once InconsistentlySynchronizedField Subscribe(listenKey, user, callback as Action <AccountUpdateEventArgs>, _accountUpdateSubscribers); } else if (type == typeof(OrderUpdateEventArgs)) { // ReSharper disable once InconsistentlySynchronizedField Subscribe(listenKey, user, callback as Action <OrderUpdateEventArgs>, _orderUpdateSubscribers); } else if (type == typeof(AccountTradeUpdateEventArgs)) { // ReSharper disable once InconsistentlySynchronizedField Subscribe(listenKey, user, callback as Action <AccountTradeUpdateEventArgs>, _accountTradeUpdateSubscribers); } else { Subscribe(listenKey, user, callback as Action <UserDataEventArgs>, null); } return(this); }
public virtual Order Deserialize(string json, IBinanceApiUser user) { Throw.IfNullOrWhiteSpace(json, nameof(json)); Throw.IfNull(user, nameof(user)); return(FillOrder(new Order(user), JObject.Parse(json))); }
public async Task SubscribeAsync(IBinanceApiUser user, Action <UserDataEventArgs> callback, CancellationToken token = default) { Throw.IfNull(user, nameof(user)); if (_listenKeys.ContainsKey(user)) { throw new InvalidOperationException($"{nameof(UserDataWebSocketClient)}: Already subscribed to user."); } try { _listenKeys[user] = await _api.UserStreamStartAsync(user, token) .ConfigureAwait(false); if (string.IsNullOrWhiteSpace(_listenKeys[user])) { throw new Exception($"{nameof(IUserDataWebSocketClient)}: Failed to get listen key from API."); } SubscribeTo(_listenKeys[user], callback); } catch (OperationCanceledException) { } catch (Exception e) { if (!token.IsCancellationRequested) { Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}.{nameof(SubscribeAsync)}"); throw; } } }
private void Subscribe <TEventArgs>(string listenKey, IBinanceApiUser user, Action <TEventArgs> callback, IDictionary <string, IList <Action <TEventArgs> > > subscribers) where TEventArgs : UserDataEventArgs { Throw.IfNullOrWhiteSpace(listenKey, nameof(listenKey)); Throw.IfNull(user, nameof(user)); // Subscribe callback (if provided) to listen key. if (subscribers == null) { SubscribeStream(listenKey, callback as Action <UserDataEventArgs>); } else { lock (_sync) { SubscribeStream(listenKey, callback, subscribers); } } // If listen key is new. // ReSharper disable once InvertIf if (!Users.ContainsKey(listenKey)) { // If a listen key exists with user. if (Users.Any(_ => _.Value.Equals(user))) { throw new InvalidOperationException($"{nameof(UserDataClient)}.{nameof(Subscribe)}: A listen key is already subscribed for this user."); } // Add listen key and user (for stream event handling). Users[listenKey] = user; } }
public Task SubscribeAsync(IBinanceApiUser user, Action<AccountInfoCacheEventArgs> callback, CancellationToken token = default) { Throw.IfNull(user, nameof(user)); base.LinkTo(Client, callback); return Client.SubscribeAsync(user, ClientCallback, token); }
public async Task SubscribeAsync <TEventArgs>(IBinanceApiUser user, Action <TEventArgs> callback, CancellationToken token = default) where TEventArgs : UserDataEventArgs { var listenKey = await _streamControl.OpenStreamAsync(user, token) .ConfigureAwait(false); HandleSubscribe(() => Client.Subscribe(listenKey, user, callback)); }
public virtual IEnumerable <Order> DeserializeMany(string json, IBinanceApiUser user) { Throw.IfNullOrWhiteSpace(json, nameof(json)); Throw.IfNull(user, nameof(user)); return(JArray.Parse(json) .Select(item => FillOrder(new Order(user), item)) .ToArray()); }
/// <summary> /// Constructor. /// </summary> /// <param name="user"></param> /// <param name="oldListenKey"></param> /// <param name="newListenKey"></param> public UserDataListenKeyUpdateEventArgs(IBinanceApiUser user, string oldListenKey, string newListenKey) { Throw.IfNull(user, nameof(user)); Throw.IfNullOrWhiteSpace(oldListenKey, nameof(oldListenKey)); User = user; OldListenKey = oldListenKey; NewListenKey = newListenKey; // can be null. }
public virtual async Task SubscribeAsync(IBinanceApiUser user, Action <UserDataEventArgs> callback, CancellationToken token) { Throw.IfNull(user, nameof(user)); if (!token.CanBeCanceled) { throw new ArgumentException("Token must be capable of being in the canceled state.", nameof(token)); } token.ThrowIfCancellationRequested(); User = user; if (IsSubscribed) { throw new InvalidOperationException($"{nameof(UserDataWebSocketClient)} is already subscribed to a user."); } try { _listenKey = await _api.UserStreamStartAsync(user, token) .ConfigureAwait(false); if (string.IsNullOrWhiteSpace(_listenKey)) { throw new Exception($"{nameof(IUserDataWebSocketClient)}: Failed to get listen key from API."); } var period = _options?.KeepAliveTimerPeriod ?? KeepAliveTimerPeriodDefault; period = Math.Min(Math.Max(period, KeepAliveTimerPeriodMin), KeepAliveTimerPeriodMax); _keepAliveTimer = new Timer(OnKeepAliveTimer, token, period, period); try { await SubscribeToAsync(_listenKey, callback, token) .ConfigureAwait(false); } finally { _keepAliveTimer.Dispose(); await _api.UserStreamCloseAsync(User, _listenKey, CancellationToken.None) .ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception e) { if (!token.IsCancellationRequested) { Logger?.LogError(e, $"{nameof(UserDataWebSocketClient)}.{nameof(SubscribeAsync)}"); throw; } } }
/// <summary> /// /// </summary> /// <param name="cache"></param> /// <param name="user"></param> /// <param name="callback"></param> /// <param name="token"></param> /// <returns></returns> public static async Task StreamAsync(this IAccountInfoCache cache, IBinanceApiUser user, Action <AccountInfoCacheEventArgs> callback, CancellationToken token) { Throw.IfNull(cache, nameof(cache)); await cache.SubscribeAsync(user, callback, token) .ConfigureAwait(false); await cache.Client.WebSocket.StreamAsync(token) .ConfigureAwait(false); }
/// <summary> /// /// </summary> /// <param name="client"></param> /// <param name="user"></param> /// <param name="callback"></param> /// <param name="token"></param> /// <returns></returns> public static async Task StreamAsync(this IUserDataWebSocketClient client, IBinanceApiUser user, Action <UserDataEventArgs> callback, CancellationToken token) { Throw.IfNull(client, nameof(client)); await client.SubscribeAsync(user, callback, token) .ConfigureAwait(false); await client.WebSocket.StreamAsync(token) .ConfigureAwait(false); }
public void Subscribe(string listenKey, IBinanceApiUser user, Action <AccountInfoCacheEventArgs> callback) { Throw.IfNullOrWhiteSpace(listenKey, nameof(listenKey)); Throw.IfNull(user, nameof(user)); _listenKey = listenKey; _user = user; OnSubscribe(callback); SubscribeToClient(); }
public override void Subscribe(string listenKey, IBinanceApiUser user, Action <UserDataEventArgs> callback) { Throw.IfNull(user, nameof(user)); // Ensure only one user is subscribed. if (ListenKeys.Count > 0 && !ListenKeys.Single().Value.Equals(user)) { throw new InvalidOperationException($"{nameof(SingleUserDataWebSocketClient)}: Can only subscribe to a single a user."); } base.Subscribe(listenKey, user, callback); }
/// <summary> /// Constructor. /// </summary> /// <param name="user">The user.</param> /// <param name="commissions">The account commissions.</param> /// <param name="status">The account status.</param> /// <param name="time">The update time.</param> /// <param name="balances">The account balances.</param> public AccountInfo(IBinanceApiUser user, AccountCommissions commissions, AccountStatus status, DateTime time, IEnumerable <AccountBalance> balances = null) { Throw.IfNull(user, nameof(user)); Throw.IfNull(commissions, nameof(commissions)); Throw.IfNull(status, nameof(status)); User = user; Commissions = commissions; Status = status; Time = time; Balances = balances ?? new AccountBalance[] { }; }
public async Task UnsubscribeAsync <TEventArgs>(IBinanceApiUser user, Action <TEventArgs> callback, CancellationToken token = default) where TEventArgs : UserDataEventArgs { var listenKey = await _streamControl.GetStreamNameAsync(user, token) .ConfigureAwait(false); HandleUnsubscribe(() => Client.Unsubscribe(listenKey, callback)); if (callback == null || !Controller.Stream.ProvidedStreams.Contains(listenKey)) { await _streamControl.CloseStreamAsync(user, token) .ConfigureAwait(false); } }
public async Task SignAsync(BinanceHttpRequest request, IBinanceApiUser user, CancellationToken token = default) { Throw.IfNull(request, nameof(request)); Throw.IfNull(user, nameof(user)); var timestamp = TimestampProvider != null ? await TimestampProvider.GetTimestampAsync(this, token).ConfigureAwait(false) : DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); request.AddParameter("timestamp", timestamp); var signature = user.Sign(request.TotalParams); request.AddParameter("signature", signature); }
public void Subscribe(string listenKey, IBinanceApiUser user, Action <AccountInfoCacheEventArgs> callback) { Throw.IfNullOrWhiteSpace(listenKey, nameof(listenKey)); Throw.IfNull(user, nameof(user)); if (_listenKey != null) { throw new InvalidOperationException($"{nameof(AccountInfoCache)}.{nameof(Subscribe)}: Already subscribed to a (user) listen key: \"{_listenKey}\""); } _listenKey = listenKey; _user = user; OnSubscribe(callback); SubscribeToClient(); }
public override IJsonClient Unsubscribe() { if (_listenKey == null) { return(this); } UnsubscribeFromClient(); OnUnsubscribe(); AccountInfo = null; _listenKey = default; _user = default; return(this); }
/// <summary> /// Constructor. /// </summary> /// <param name="user">The user.</param> /// <param name="commissions">The account commissions.</param> /// <param name="status">The account status.</param> /// <param name="updateTime">The update time.</param> /// <param name="balances">The account balances.</param> public AccountInfo(IBinanceApiUser user, AccountCommissions commissions, AccountStatus status, long updateTime, IEnumerable <AccountBalance> balances = null) { Throw.IfNull(user, nameof(user)); Throw.IfNull(commissions, nameof(commissions)); Throw.IfNull(status, nameof(status)); if (updateTime <= 0) { throw new ArgumentException($"{nameof(AccountInfo)}: timestamp must be greater than 0.", nameof(updateTime)); } User = user; Commissions = commissions; Status = status; Timestamp = updateTime; Balances = balances ?? new AccountBalance[] { }; }
public async Task SubscribeAsync(IBinanceApiUser user, Action <AccountInfoCacheEventArgs> callback, CancellationToken token) { Throw.IfNull(user, nameof(user)); if (!token.CanBeCanceled) { throw new ArgumentException("Token must be capable of being in the canceled state.", nameof(token)); } token.ThrowIfCancellationRequested(); Token = token; LinkTo(Client, callback); try { await Client.SubscribeAsync(user, token) .ConfigureAwait(false); } finally { UnLink(); } }
public virtual void Subscribe(string listenKey, IBinanceApiUser user, Action <UserDataEventArgs> callback) { Throw.IfNullOrWhiteSpace(listenKey, nameof(listenKey)); Throw.IfNull(user, nameof(user)); Logger?.LogDebug($"{nameof(UserDataWebSocketClient)}.{nameof(Subscribe)}: \"{listenKey}\" (callback: {(callback == null ? "no" : "yes")}). [thread: {Thread.CurrentThread.ManagedThreadId}]"); // Subscribe callback (if provided) to listen key. SubscribeStream(listenKey, callback); // If listen key is new. // ReSharper disable once InvertIf if (!ListenKeys.ContainsKey(listenKey)) { // If a listen key exists with user. if (ListenKeys.Any(_ => _.Value.Equals(user))) { throw new InvalidOperationException($"{nameof(UserDataWebSocketClient)}.{nameof(Subscribe)}: A listen key is already subscribed for this user."); } // Add listen key and user (for stream event handling). ListenKeys[listenKey] = user; } }
public static async Task Main(string[] args) { // Un-comment to run... //await AccountBalancesExample.ExampleMain(args); //await AccountBalancesExample.AdvancedExampleMain(args); //await MinimalWithDependencyInjection.ExampleMain(args); //await MinimalWithoutDependencyInjection.ExampleMain(args); //await OrderBookCacheAccountBalanceExample.AdvancedExampleMain(args); //await ReadMeExample.ExampleMain(args); var cts = new CancellationTokenSource(); try { // Load configuration. Configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", true, false) .AddUserSecrets <Program>() // for access to API key and secret. .Build(); // Configure services. ServiceProvider = new ServiceCollection() // ReSharper disable once ArgumentsStyleLiteral .AddBinance(useSingleCombinedStream: true) // add default Binance services. // Use alternative, low-level, web socket client implementation. //.AddTransient<IWebSocketClient, WebSocket4NetClient>() //.AddTransient<IWebSocketClient, WebSocketSharpClient>() .AddOptions() .Configure <BinanceApiOptions>(Configuration.GetSection("ApiOptions")) .AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace)) .BuildServiceProvider(); // Configure logging. ServiceProvider .GetService <ILoggerFactory>() .AddConsole(Configuration.GetSection("Logging:Console")) .AddFile(Configuration.GetSection("Logging:File")); // NOTE: Using ":" requires Microsoft.Extensions.Configuration.Binder. var apiKey = Configuration["BinanceApiKey"] // user secrets configuration. ?? Configuration.GetSection("User")["ApiKey"]; // appsettings.json configuration. var apiSecret = Configuration["BinanceApiSecret"] // user secrets configuration. ?? Configuration.GetSection("User")["ApiSecret"]; // appsettings.json configuration. if (string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(apiSecret)) { PrintApiNotice(); } if (!string.IsNullOrEmpty(apiKey)) { User = ServiceProvider .GetService <IBinanceApiUserProvider>() .CreateUser(apiKey, apiSecret); } Api = ServiceProvider.GetService <IBinanceApi>(); ClientManager = ServiceProvider.GetService <IBinanceWebSocketClientManager>(); ClientManager.Error += (s, e) => { lock (ConsoleSync) { Console.WriteLine(); Console.WriteLine($"! Client Manager Error: \"{e.Exception.Message}\""); Console.WriteLine(); } }; UserDataManager = ServiceProvider.GetService <IUserDataWebSocketManager>(); // Instantiate all assembly command handlers. foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { if (typeof(IHandleCommand).IsAssignableFrom(type) && !type.IsAbstract) { CommandHandlers.Add((IHandleCommand)Activator.CreateInstance(type)); } } await SuperLoopAsync(cts.Token); } catch (Exception e) { lock (ConsoleSync) { Console.WriteLine($"! FAIL: \"{e.Message}\""); if (e.InnerException != null) { Console.WriteLine($" -> Exception: \"{e.InnerException.Message}\""); } } } finally { await DisableLiveTaskAsync(); cts.Cancel(); cts.Dispose(); User?.Dispose(); ClientManager?.Dispose(); lock (ConsoleSync) { Console.WriteLine(); Console.WriteLine(" ...press any key to close window."); Console.ReadKey(true); } } }
/// <summary> /// Constructor. /// </summary> /// <param name="user">The user (required).</param> public TakeProfitLimitOrder(IBinanceApiUser user) : base(user) { }
public MarketOrder(IBinanceApiUser user) : base(user) { }
/// <summary> /// /// </summary> /// <param name="cache"></param> /// <param name="user"></param> /// <param name="token"></param> /// <returns></returns> public static Task StreamAsync(this IAccountInfoCache cache, IBinanceApiUser user, CancellationToken token) => StreamAsync(cache, user, null, token);
/// <summary> /// Start a new user data stream. /// </summary> /// <param name="client"></param> /// <param name="user"></param> /// <param name="token"></param> /// <returns></returns> public static Task <string> UserStreamStartAsync(this IBinanceHttpClient client, IBinanceApiUser user, CancellationToken token = default) { Throw.IfNull(user, nameof(user)); return(UserStreamStartAsync(client, user.ApiKey, token)); }
/// <summary> /// Get the account status. /// </summary> /// <param name="client"></param> /// <param name="user"></param> /// <param name="token"></param> /// <returns></returns> public static async Task <string> GetAccountStatusAsync(this IBinanceHttpClient client, IBinanceApiUser user, CancellationToken token = default) { Throw.IfNull(client, nameof(client)); var request = new BinanceHttpRequest("/wapi/v3/accountStatus.html") { ApiKey = user.ApiKey }; await client.SignAsync(request, user, token) .ConfigureAwait(false); 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)); 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 the withdrawal 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> GetWithdrawalsAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset = null, WithdrawalStatus?status = null, long startTime = default, long 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; } var request = new BinanceHttpRequest("/wapi/v3/withdrawHistory.html") { ApiKey = user.ApiKey }; if (!string.IsNullOrWhiteSpace(asset)) { request.AddParameter("asset", asset.FormatSymbol()); } if (status.HasValue) { request.AddParameter("status", (int)status); } if (startTime > 0) { request.AddParameter("startTime", startTime); } if (endTime > 0) { request.AddParameter("endTime", endTime); } if (recvWindow > 0) { request.AddParameter("recvWindow", recvWindow); } await client.SignAsync(request, user, token) .ConfigureAwait(false); return(await client.GetAsync(request, token) .ConfigureAwait(false)); }
/// <summary> /// Submit a withdraw request. /// </summary> /// <param name="client"></param> /// <param name="user"></param> /// <param name="asset"></param> /// <param name="address"></param> /// <param name="addressTag"></param> /// <param name="amount"></param> /// <param name="name">A description of the address (optional).</param> /// <param name="recvWindow"></param> /// <param name="token"></param> /// <returns></returns> public static async Task <string> WithdrawAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset, string address, string addressTag, decimal amount, string name = null, long recvWindow = default, CancellationToken token = default) { Throw.IfNull(client, nameof(client)); Throw.IfNull(user, nameof(user)); Throw.IfNullOrWhiteSpace(asset, nameof(asset)); Throw.IfNullOrWhiteSpace(address, nameof(address)); if (amount <= 0) { throw new ArgumentException("Withdraw amount must be greater than 0.", nameof(amount)); } if (recvWindow <= 0) { recvWindow = client.Options.RecvWindowDefault ?? 0; } var request = new BinanceHttpRequest("/wapi/v3/withdraw.html") { ApiKey = user.ApiKey }; request.AddParameter("asset", asset.FormatSymbol()); request.AddParameter("address", address); request.AddParameter("amount", amount); if (!string.IsNullOrWhiteSpace(addressTag)) { request.AddParameter("addressTag", addressTag); } if (!string.IsNullOrWhiteSpace(name)) { request.AddParameter("name", name); } if (recvWindow > 0) { request.AddParameter("recvWindow", recvWindow); } await client.SignAsync(request, user, token) .ConfigureAwait(false); return(await client.PostAsync(request, token) .ConfigureAwait(false)); }