diff --git a/CryptoGramBot/EventBus/Handlers/TradeNotificationHandler.cs b/CryptoGramBot/EventBus/Handlers/TradeNotificationHandler.cs index 1ce3c4d..ff46aab 100644 --- a/CryptoGramBot/EventBus/Handlers/TradeNotificationHandler.cs +++ b/CryptoGramBot/EventBus/Handlers/TradeNotificationHandler.cs @@ -44,7 +44,7 @@ public async Task Handle(TradeNotificationCommand command) profitPercentage = tradesProfitResponse.ProfitPercentage; btcProfit = tradesProfitResponse.BtcProfit; dollarProfit = tradesProfitResponse.DollarProfit; - lastBought = tradesProfitResponse.LastBoughtTime + TimeSpan.FromHours(_config.TimeOffset); + lastBought = tradesProfitResponse.LastBoughtTime; } var sb = new StringBuffer(); diff --git a/CryptoGramBot/Services/Exchanges/BinanceService.cs b/CryptoGramBot/Services/Exchanges/BinanceService.cs index fd48589..f6174f4 100644 --- a/CryptoGramBot/Services/Exchanges/BinanceService.cs +++ b/CryptoGramBot/Services/Exchanges/BinanceService.cs @@ -46,6 +46,11 @@ public async Task GetBalance() { var accountInfo = await _binanceWebsocketService.GetAccountInfoAsync(); + if(accountInfo == null) + { + throw new Exception("Account info not had get from binance"); + } + balances = BinanceConverter.BinanceToWalletBalances(accountInfo.Balances); } catch (Exception e) diff --git a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceCacheService.cs b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceCacheService.cs index db16d28..229d749 100644 --- a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceCacheService.cs +++ b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceCacheService.cs @@ -23,6 +23,7 @@ public class BinanceCacheService : IBinanceCacheService private readonly string SYMBOL_PRICE = "price_"; private readonly int CACHE_TIME_IN_MINUTES = 60; + private readonly int USER_CACHE_TIME_IN_HOURS = 23; #endregion @@ -50,7 +51,7 @@ public AccountInfo GetAccountInfo() public void SetAccountInfo(AccountInfo accountInfo) { - _memoryCache.Set(ACCOUNT_INFO_KEY, accountInfo, TimeSpan.FromMinutes(CACHE_TIME_IN_MINUTES)); + _memoryCache.Set(ACCOUNT_INFO_KEY, accountInfo, TimeSpan.FromHours(USER_CACHE_TIME_IN_HOURS)); } public ImmutableList GetOrders(string symbol) @@ -60,7 +61,7 @@ public ImmutableList GetOrders(string symbol) public void SetOrders(string symbol, ImmutableList orders) { - _memoryCache.Set($"{symbol}{ORDERS_BY_SYMBOL_KEY}", orders, TimeSpan.FromMinutes(CACHE_TIME_IN_MINUTES)); + _memoryCache.Set($"{symbol}{ORDERS_BY_SYMBOL_KEY}", orders, TimeSpan.FromHours(USER_CACHE_TIME_IN_HOURS)); } public ImmutableList GetAccountTrades(string symbol) @@ -70,7 +71,7 @@ public ImmutableList GetAccountTrades(string symbol) public void SetAccountTrades(string symbol, ImmutableList trades) { - _memoryCache.Set($"{symbol}{ACCOUNT_TRADES_BY_SYMBOL_KEY}", trades, TimeSpan.FromMinutes(CACHE_TIME_IN_MINUTES)); + _memoryCache.Set($"{symbol}{ACCOUNT_TRADES_BY_SYMBOL_KEY}", trades, TimeSpan.FromHours(USER_CACHE_TIME_IN_HOURS)); } public List GetSymbols() @@ -83,6 +84,11 @@ public void SetSymbols(List symbols) _memoryCache.Set(SYMBOLS, symbols, TimeSpan.FromMinutes(CACHE_TIME_IN_MINUTES)); } + public void ClearSymbols() + { + _memoryCache.Remove(SYMBOLS); + } + public ImmutableList GetCandlesticks(string symbol, CandlestickInterval interval) { return _memoryCache.Get>($"{symbol}{SYMBOL_CANDLESTICK}{interval.AsString()}"); diff --git a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceSubscriberService.cs b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceSubscriberService.cs index b407418..8b8dfed 100644 --- a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceSubscriberService.cs +++ b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceSubscriberService.cs @@ -22,7 +22,7 @@ private class CandlestickSubscriber public CandlestickInterval Interval { get; set; } - public Timer CandlestickReConnectionTimer { get; set; } + public Timer CandlestickDisconnectionTimer { get; set; } public CancellationTokenSource TokenSource { get; set; } @@ -57,8 +57,8 @@ private class CandlestickSubscriber private ISymbolStatisticsWebSocketClient _symbolStatisticsWebSocketClient; private IUserDataWebSocketClient _userDataWebSocketClient; - private Timer _symbolsReConnectionTimer; - private Timer _userDataReConnectionTimer; + private Timer _symbolsDisconnectionTimer; + private Timer _userDataDisconnectionTimer; private Task _userDataSubscribeTask; private Task _symbolsSubscribeTask; @@ -70,12 +70,16 @@ private class CandlestickSubscriber private Action _onCandlestickUpdate; - private Action _onSymbolStatisticError; - private Action _onCandlestickError; - private Func _onUserDataError; + private Action _onSymbolStatisticErrorOrDisconnect; + private Action _onCandlestickErrorOrDisconnect; + private Func _onUserDataErrorOrDisconnect; private BinanceApiUser _user; + private SemaphoreSlim _symbolsStatisticSemaphore; + private SemaphoreSlim _userDataSemaphore; + private SemaphoreSlim _candlestickSemaphore; + #endregion #region Dependecies @@ -95,6 +99,10 @@ private class CandlestickSubscriber _config = config; _serviceProvider = serviceProvider; _log = log; + + _symbolsStatisticSemaphore = new SemaphoreSlim(1, 1); + _userDataSemaphore = new SemaphoreSlim(1, 1); + _candlestickSemaphore = new SemaphoreSlim(1, 1); } #endregion @@ -106,7 +114,7 @@ public async Task SymbolsStatistics(Action onUpdate, if (_symbolsSubscribeTask == null) { _onSymbolStatisticUpdate = onUpdate ?? throw new ArgumentException(nameof(onUpdate)); - _onSymbolStatisticError = onError ?? throw new ArgumentException(nameof(onError)); + _onSymbolStatisticErrorOrDisconnect = onError ?? throw new ArgumentException(nameof(onError)); await SubscribeToSymbols(); } @@ -122,9 +130,7 @@ public async Task SymbolsStatistics(Action onUpdate, _onOrderUpdate = onOrderUpdate ?? throw new ArgumentException(nameof(onOrderUpdate)); _onAccountUpdate = onAccountUpdate ?? throw new ArgumentException(nameof(onAccountUpdate)); _onAccountTradeUpdate = onAccountTradeUpdate ?? throw new ArgumentException(nameof(onAccountTradeUpdate)); - _onUserDataError = onError ?? throw new ArgumentException(nameof(onError)); - - _log.LogInformation($"Subscribe user data"); + _onUserDataErrorOrDisconnect = onError ?? throw new ArgumentException(nameof(onError)); await SubscribeToUserData(); } @@ -137,7 +143,7 @@ public async Task SymbolsStatistics(Action onUpdate, if (_candlestickSubscribers == null || !_candlestickSubscribers.ContainsKey($"{symbol}{interval.AsString()}")) { _onCandlestickUpdate = onUpdate ?? throw new ArgumentException(nameof(onUpdate)); - _onCandlestickError = onError ?? throw new ArgumentException(nameof(onError)); + _onCandlestickErrorOrDisconnect = onError ?? throw new ArgumentException(nameof(onError)); if (_candlestickSubscribers == null) { @@ -156,9 +162,12 @@ public void Dispose() { if (!_isDisposed) { + _symbolsStatisticSemaphore?.Dispose(); + _userDataSemaphore?.Dispose(); + _candlestickSemaphore?.Dispose(); - _symbolsReConnectionTimer?.Dispose(); - _userDataReConnectionTimer?.Dispose(); + _symbolsDisconnectionTimer?.Dispose(); + _userDataDisconnectionTimer?.Dispose(); _symbolStatisticCancellationTokenSource?.Cancel(); _userDataCancellationTokenSource?.Cancel(); @@ -170,7 +179,7 @@ public void Dispose() { foreach (var item in _candlestickSubscribers) { - item.Value.CandlestickReConnectionTimer?.Dispose(); + item.Value.CandlestickDisconnectionTimer?.Dispose(); item.Value.TokenSource?.Cancel(); item.Value.TokenSource?.Dispose(); } @@ -184,152 +193,168 @@ public void Dispose() #region Private methods - private async Task SubscribeToSymbols(bool reConnect = false) + private async Task SubscribeToSymbols() { - _log.LogInformation($"Subscribe to symbols"); - - if (_symbolsSubscribeTask != null && !reConnect) + if (_symbolsSubscribeTask != null) { return; } try { - _symbolStatisticCancellationTokenSource?.Cancel(); - _symbolStatisticCancellationTokenSource?.Dispose(); + try + { + await _symbolsStatisticSemaphore.WaitAsync(); + + if(_symbolsSubscribeTask != null) + { + return; + } + + _log.LogInformation($"Subscribe to symbols"); - _symbolStatisticCancellationTokenSource = new CancellationTokenSource(); + _symbolStatisticCancellationTokenSource?.Cancel(); + _symbolStatisticCancellationTokenSource?.Dispose(); - _symbolStatisticsWebSocketClient = _serviceProvider.GetService(); + _symbolStatisticCancellationTokenSource = new CancellationTokenSource(); - _symbolsSubscribeTask = _symbolStatisticsWebSocketClient.SubscribeAsync(_onSymbolStatisticUpdate, _symbolStatisticCancellationTokenSource.Token); + _symbolStatisticsWebSocketClient = _serviceProvider.GetService(); - if (!reConnect) + _symbolsSubscribeTask = _symbolStatisticsWebSocketClient.SubscribeAsync(_onSymbolStatisticUpdate, _symbolStatisticCancellationTokenSource.Token); + + SymbolsDisconnectionTimerInitialize(); + } + finally { - SymbolsReConnectionTimerInitialize(); + _symbolsStatisticSemaphore.Release(); } await _symbolsSubscribeTask; + + _symbolsSubscribeTask = null; } - catch (Exception ex) + catch (Exception) { _symbolsSubscribeTask = null; - _log.LogError($"Error with symbols statistic websocket {ex.Message}", ex); - - _onSymbolStatisticError?.Invoke(); + OnSymbolStatisticDisconnect(true); } } - private async Task SubscribeToUserData(bool reConnect = false) + private async Task SubscribeToUserData() { - if (_userDataSubscribeTask != null && !reConnect) + if (_userDataSubscribeTask != null) { return; } try { - _user?.Dispose(); + try + { + await _userDataSemaphore.WaitAsync(); - _userDataCancellationTokenSource?.Cancel(); - _userDataCancellationTokenSource?.Dispose(); + if(_userDataSubscribeTask != null) + { + return; + } - _userDataCancellationTokenSource = new CancellationTokenSource(); + _log.LogInformation("Subscribe to user data"); - if(_userDataWebSocketClient != null) - { - _userDataWebSocketClient.TradeUpdate -= OnAccountTradeUpdate; - _userDataWebSocketClient.AccountUpdate -= OnAccountUpdate; - _userDataWebSocketClient.OrderUpdate -= OnOrderUpdate; - } + _user?.Dispose(); + _user = null; - _userDataWebSocketClient = _serviceProvider.GetService(); + _userDataCancellationTokenSource?.Cancel(); + _userDataCancellationTokenSource?.Dispose(); - _user = new BinanceApiUser(_config.Key, _config.Secret); + _userDataCancellationTokenSource = new CancellationTokenSource(); - _userDataWebSocketClient.TradeUpdate += OnAccountTradeUpdate; - _userDataWebSocketClient.AccountUpdate += OnAccountUpdate; - _userDataWebSocketClient.OrderUpdate += OnOrderUpdate; + if (_userDataWebSocketClient != null) + { + _userDataWebSocketClient.TradeUpdate -= OnAccountTradeUpdate; + _userDataWebSocketClient.AccountUpdate -= OnAccountUpdate; + _userDataWebSocketClient.OrderUpdate -= OnOrderUpdate; + } + + _userDataWebSocketClient = _serviceProvider.GetService(); - _userDataSubscribeTask = _userDataWebSocketClient.SubscribeAsync(_user, _userDataCancellationTokenSource.Token); + _user = new BinanceApiUser(_config.Key, _config.Secret); - if (!reConnect) + _userDataWebSocketClient.TradeUpdate += OnAccountTradeUpdate; + _userDataWebSocketClient.AccountUpdate += OnAccountUpdate; + _userDataWebSocketClient.OrderUpdate += OnOrderUpdate; + + _userDataSubscribeTask = _userDataWebSocketClient.SubscribeAsync(_user, _userDataCancellationTokenSource.Token); + + UserDataDisconnectionTimerInitialize(); + } + finally { - UserDataReConnectionTimerInitialize(); + _userDataSemaphore.Release(); } await _userDataSubscribeTask; + + _userDataSubscribeTask = null; } - catch (Exception ex) + catch (Exception) { _userDataSubscribeTask = null; - _log.LogError($"Error with user data websocket {ex.Message}", ex); - - await _onUserDataError?.Invoke(); + await OnUserDataDisconnect(true); } } - private async Task SubscribeToCandlestick(string symbol, CandlestickInterval interval, bool reConnect = false) + private async Task SubscribeToCandlestick(string symbol, CandlestickInterval interval) { - _log.LogInformation($"Subscribe to candlestick {symbol} {interval.AsString()}"); - var key = GetKey(symbol, interval); - if (_candlestickSubscribers.ContainsKey(key) && !reConnect) + if (_candlestickSubscribers.ContainsKey(key)) { return; } try { - CandlestickSubscriber subscriber = _candlestickSubscribers.ContainsKey(key) ? _candlestickSubscribers[key] : null; - - if(subscriber != null) + var subscriber = new CandlestickSubscriber() { - subscriber.TokenSource.Cancel(); - subscriber.TokenSource.Dispose(); - } - else + Symbol = symbol, + Interval = interval + }; + + try { - subscriber = new CandlestickSubscriber() + await _candlestickSemaphore.WaitAsync(); + + if (_candlestickSubscribers.ContainsKey(key)) { - Symbol = symbol, - Interval = interval - }; + return; + } - CandlestickReConnectionTimerInitialize(subscriber); + _log.LogInformation($"Subscribe to candlestick {symbol} {interval.AsString()}"); _candlestickSubscribers[key] = subscriber; - } - subscriber.TokenSource = new CancellationTokenSource(); - subscriber.CandlestickWebSocketClient = _serviceProvider.GetService(); - subscriber.SubscribeTask = subscriber.CandlestickWebSocketClient.SubscribeAsync(symbol, interval, _onCandlestickUpdate, subscriber.TokenSource.Token); + subscriber.TokenSource = new CancellationTokenSource(); + subscriber.CandlestickWebSocketClient = _serviceProvider.GetService(); + subscriber.SubscribeTask = subscriber.CandlestickWebSocketClient.SubscribeAsync(symbol, interval, _onCandlestickUpdate, subscriber.TokenSource.Token); - await subscriber.SubscribeTask; - } - catch (Exception ex) - { - if (_candlestickSubscribers.ContainsKey(key)) + CandlestickDisconnectionTimerInitialize(subscriber); + } + finally { - var subscriber = _candlestickSubscribers[key]; - - subscriber.CandlestickReConnectionTimer?.Dispose(); - subscriber.TokenSource?.Dispose(); - - CandlestickSubscriber removedSubscriber = null; - - if(_candlestickSubscribers.TryRemove(key, out removedSubscriber)) - { - _log.LogInformation($"subscriber removed for {symbol} {interval.AsString()}"); - } + _candlestickSemaphore.Release(); } - _log.LogError($"Error with candlestick websocket {ex.Message}", ex); + await subscriber.SubscribeTask; - _onCandlestickError?.Invoke(symbol, interval); + RemoveSubscriber(symbol, interval); + } + catch (Exception) + { + OnCandlesticDisconnect(symbol, interval, true); + + RemoveSubscriber(symbol, interval); } } @@ -353,36 +378,96 @@ private void OnOrderUpdate(object sender, OrderUpdateEventArgs args) _onOrderUpdate?.Invoke(args); } - private void SymbolsReConnectionTimerInitialize() + private void OnSymbolStatisticDisconnect(bool error = false) + { + _symbolStatisticCancellationTokenSource?.Cancel(); + + var logMessage = error ? "Error with symbol statistic websocket" : "Symbol statistic websocket disconnected!"; + + _log.LogInformation($"{logMessage} Cache will be clear"); + + _onSymbolStatisticErrorOrDisconnect?.Invoke(); + } + + private async Task OnUserDataDisconnect(bool error = false) + { + _userDataCancellationTokenSource?.Cancel(); + + var logMessage = error ? "Error with user data websocket" : "User data websocket disconnected!"; + + _log.LogInformation($"{logMessage} Cache will be clear"); + + await _onUserDataErrorOrDisconnect?.Invoke(); + } + + private void OnCandlesticDisconnect(string symbol, CandlestickInterval interval, bool error = false) + { + var key = GetKey(symbol, interval); + + if (_candlestickSubscribers.ContainsKey(key)) + { + var subscriber = _candlestickSubscribers[key]; + subscriber.TokenSource?.Cancel(); + } + + var logMessage = error ? "Error with candlestick websocket" : "Candlestick websocket disconnected"; + + _log.LogInformation($"{logMessage} {symbol} {interval.AsString()}. Cache will be cleared"); + + _onCandlestickErrorOrDisconnect(symbol, interval); + } + + private void RemoveSubscriber(string symbol, CandlestickInterval interval) { - _symbolsReConnectionTimer?.Dispose(); + var key = GetKey(symbol, interval); + + if (_candlestickSubscribers.ContainsKey(key)) + { + var subscriber = _candlestickSubscribers[key]; - _symbolsReConnectionTimer = new Timer(async s => await SubscribeToSymbols(reConnect: true), null, + subscriber.CandlestickDisconnectionTimer?.Dispose(); + subscriber.TokenSource?.Dispose(); + } + + CandlestickSubscriber removedSubscriber = null; + + if (_candlestickSubscribers.TryRemove(key, out removedSubscriber)) + { + _log.LogInformation($"subscriber removed for {symbol} {interval.AsString()}"); + } + } + + private void SymbolsDisconnectionTimerInitialize() + { + _symbolsDisconnectionTimer?.Dispose(); + + _symbolsDisconnectionTimer = new Timer(s => OnSymbolStatisticDisconnect(), null, TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES), - TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES)); + TimeSpan.FromMilliseconds(-1)); } - private void UserDataReConnectionTimerInitialize() + private void UserDataDisconnectionTimerInitialize() { - _userDataReConnectionTimer?.Dispose(); + _userDataDisconnectionTimer?.Dispose(); - _userDataReConnectionTimer = new Timer(async s => await SubscribeToUserData(reConnect: true), null, + _userDataDisconnectionTimer = new Timer(async s => await OnUserDataDisconnect(), null, TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES), - TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES)); + TimeSpan.FromMilliseconds(-1)); } - private void CandlestickReConnectionTimerInitialize(CandlestickSubscriber subscriber) + private void CandlestickDisconnectionTimerInitialize(CandlestickSubscriber subscriber) { - subscriber.CandlestickReConnectionTimer?.Dispose(); + subscriber.CandlestickDisconnectionTimer?.Dispose(); - subscriber.CandlestickReConnectionTimer = new Timer(async(object state) => + subscriber.CandlestickDisconnectionTimer = new Timer((object state) => { var _subscriber = state as CandlestickSubscriber; - await SubscribeToCandlestick(_subscriber.Symbol, _subscriber.Interval, reConnect: true); + + OnCandlesticDisconnect(_subscriber.Symbol, _subscriber.Interval); }, subscriber, TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES), - TimeSpan.FromMinutes(WEBSOCKET_LIFE_TIME_IN_MINUTES)); + TimeSpan.FromMilliseconds(-1)); } #endregion diff --git a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceWebsocketService.cs b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceWebsocketService.cs index 1bdbc26..d01e2c0 100644 --- a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceWebsocketService.cs +++ b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/BinanceWebsocketService.cs @@ -217,17 +217,17 @@ private async Task> GetSymbolStatistics() private void SubscribeSymbols() { - _subscriber.SymbolsStatistics(OnStatisticsUpdate, OnStatisticError); + _subscriber.SymbolsStatistics(OnStatisticsUpdate, OnStatisticErrorOrDisconnect); } private void SubscribeUserData() { - _subscriber.UserData(OnOrderUpdate, OnAccountUpdate, OnAccountTradeUpdate, OnUserDataError); + _subscriber.UserData(OnOrderUpdate, OnAccountUpdate, OnAccountTradeUpdate, OnUserDataErrorOrDisconnect); } private void SubscribeCandlestick(string symbol, CandlestickInterval interval) { - _subscriber.Candlestick(symbol, interval, OnCandletickUpdate, OnCandlestickError); + _subscriber.Candlestick(symbol, interval, OnCandletickUpdate, OnCandlestickErrorOrDisconnect); } #endregion @@ -252,7 +252,10 @@ private void SubscribeCandlestick(string symbol, CandlestickInterval interval) cacheValue = await initialize(); } - subscribeTo?.Invoke(); + if(cacheValue != null) + { + subscribeTo?.Invoke(); + } return cacheValue; } @@ -264,91 +267,166 @@ private void SubscribeCandlestick(string symbol, CandlestickInterval interval) private async Task InitializeAccountInfo() { - using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + try + { + using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + { + var accountInfo = await _binanceApi.GetAccountInfoAsync(user, 10000000); + + _cache.SetAccountInfo(accountInfo); + + return accountInfo; + } + } + catch (Exception ex) { - var accountInfo = await _binanceApi.GetAccountInfoAsync(user, 10000000); + _cache.ClearAccountInfo(); - _cache.SetAccountInfo(accountInfo); + _log.LogError($"Account info initialization error {ex.Message}"); - return accountInfo; + return null; } } private async Task> InitializeOpenOrders(string symbol) { - using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + try { - var openOrders = await _binanceApi.GetOpenOrdersAsync(user, symbol, 10000000); + using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + { + var openOrders = await _binanceApi.GetOpenOrdersAsync(user, symbol, 10000000); - var immutableOrders = openOrders.ToImmutableList(); + var immutableOrders = openOrders.ToImmutableList(); + + _cache.SetOrders(symbol, immutableOrders); + + return immutableOrders; + } + } + catch (Exception ex) + { + _cache.ClearOrders(symbol); - _cache.SetOrders(symbol, immutableOrders); + _log.LogError($"Orders initialization error {ex.Message}"); - return immutableOrders; + return new List().ToImmutableList(); } } private async Task> InitializeAccountTrades(string symbol) { - using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + try { - var accountTrades = await _binanceApi.GetAccountTradesAsync(user, symbol, -1L, 0, 10000000); + using (var user = new BinanceApiUser(_config.Key, _config.Secret)) + { + var accountTrades = await _binanceApi.GetAccountTradesAsync(user, symbol, -1L, 0, 10000000); - var immutableAccountTrades = accountTrades.ToImmutableList(); + var immutableAccountTrades = accountTrades.ToImmutableList(); + + _cache.SetAccountTrades(symbol, immutableAccountTrades); + + return immutableAccountTrades; + } + } + catch (Exception ex) + { + _cache.ClearAccountTrades(symbol); - _cache.SetAccountTrades(symbol, immutableAccountTrades); + _log.LogError($"Account trades initialization error {ex.Message}"); - return immutableAccountTrades; + return new List().ToImmutableList(); } } private async Task> InitializeCandleticks(string symbol, CandlestickInterval interval) { - var candlesticks = await _binanceApi.GetCandlesticksAsync(symbol, interval); + try + { + var candlesticks = await _binanceApi.GetCandlesticksAsync(symbol, interval); - var immutableCandlesticks = candlesticks.ToImmutableList(); + var immutableCandlesticks = candlesticks.ToImmutableList(); - _cache.SetCandlestick(symbol, interval, immutableCandlesticks); + _cache.SetCandlestick(symbol, interval, immutableCandlesticks); - return immutableCandlesticks; + return immutableCandlesticks; + } + catch (Exception ex) + { + _cache.ClearCandlestick(symbol, interval); + + _log.LogError($"Canlestick initialization error {ex.Message}"); + + return new List().ToImmutableList(); + } } private async Task> InitializeSymbols() { - var symbols = await _binanceApi.GetSymbolsAsync(); + try + { + var symbols = await _binanceApi.GetSymbolsAsync(); + + _cache.SetSymbols(symbols.ToList()); + + return symbols.ToList(); + } + catch (Exception ex) + { + _cache.ClearSymbols(); - _cache.SetSymbols(symbols.ToList()); + _log.LogError($"Symbols initialization error {ex.Message}"); - return symbols.ToList(); + return new List(); + } } private async Task> InitializeSymbolPrices() { - var symbolPrices = await _binanceApi.GetPricesAsync(); + try + { + var symbolPrices = await _binanceApi.GetPricesAsync(); - var immutableSymbolPrices = symbolPrices.ToImmutableList(); + var immutableSymbolPrices = symbolPrices.ToImmutableList(); - var immutablePrices = symbolPrices.ToImmutableDictionary(s => s.Symbol, s => s.Value); + var immutablePrices = symbolPrices.ToImmutableDictionary(s => s.Symbol, s => s.Value); - _cache.SetSymbolPrices(immutablePrices); + _cache.SetSymbolPrices(immutablePrices); - foreach(var symbolPrice in immutableSymbolPrices) - { - _cache.SetSymbolPrice(symbolPrice.Symbol, symbolPrice.Value); + foreach (var symbolPrice in immutableSymbolPrices) + { + _cache.SetSymbolPrice(symbolPrice.Symbol, symbolPrice.Value); + } + + return immutablePrices; } + catch (Exception ex) + { + _cache.ClearSymbolPrices(); - return immutablePrices; + _log.LogError($"Symbol Prices initialization error {ex.Message}"); + + return new Dictionary().ToImmutableDictionary(); + } } private async Task> InitializeSymbolStatistics() { - var symbolStatistics = await _binanceApi.Get24HourStatisticsAsync(); + try + { + var symbolStatistics = await _binanceApi.Get24HourStatisticsAsync(); + + var immutableStatistics = symbolStatistics.ToImmutableDictionary(s => s.Symbol, s => s); - var immutableStatistics = symbolStatistics.ToImmutableDictionary(s => s.Symbol, s => s); + _cache.SetSymbolStatistics(immutableStatistics); - _cache.SetSymbolStatistics(immutableStatistics); + return immutableStatistics; + } + catch (Exception ex) + { + _log.LogError($"Symbol statistics initialization error {ex.Message}"); - return immutableStatistics; + return new Dictionary().ToImmutableDictionary(); + } } #endregion @@ -428,16 +506,16 @@ private void UpdateOrders(Order updatedOrder, string symbol) } } - private async Task OnUserDataError() + private async Task OnUserDataErrorOrDisconnect() { _cache.ClearAccountInfo(); - var symbols = await GetSymbols(); + var symbols = await GetSymbolStringsAsync(); foreach(var symbol in symbols) { - _cache.ClearAccountTrades(symbol.BaseAsset.Symbol); - _cache.ClearOrders(symbol.BaseAsset.Symbol); + _cache.ClearAccountTrades(symbol); + _cache.ClearOrders(symbol); } } @@ -475,7 +553,7 @@ private void OnStatisticsUpdate(SymbolStatisticsEventArgs e) } } - private void OnStatisticError() + private void OnStatisticErrorOrDisconnect() { var symbolPrices = _cache.GetSymbolPrices(); @@ -518,7 +596,7 @@ private void OnCandletickUpdate(CandlestickEventArgs args) } } - private void OnCandlestickError(string symbol, CandlestickInterval interval) + private void OnCandlestickErrorOrDisconnect(string symbol, CandlestickInterval interval) { if (_cache.GetCandlesticks(symbol, interval) != null) { diff --git a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/IBinanceCacheService.cs b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/IBinanceCacheService.cs index f19be38..36ba2d1 100644 --- a/CryptoGramBot/Services/Exchanges/WebSockets/Binance/IBinanceCacheService.cs +++ b/CryptoGramBot/Services/Exchanges/WebSockets/Binance/IBinanceCacheService.cs @@ -31,6 +31,8 @@ public interface IBinanceCacheService void SetSymbols(List symbols); + void ClearSymbols(); + ImmutableDictionary GetSymbolPrices(); void SetSymbolPrices(ImmutableDictionary prices); diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3aa1334 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 mehtadone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6189e94..d359a7e 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,6 @@ This app needs to be run all the time to have the bot running. I might look at c * Profit calculations are wrong on a sell if we don't have the data in the database. ## Support -* Send me a [telegram](https://t.me/mehtadone) * Join the [telegram group](https://t.me/joinchat/AYGQfg7ZauzhAxe5QyU4Tg) ## Screenshots