public LongTaskLaunchResult Filtrate(ICandlesFiltrationRequest request, bool analyzeOnly) { // We should not run filtration multiple times before the first attempt ends. if (Health != null && Health.State == CandlesFiltrationState.InProgress) { return(LongTaskLaunchResult.AlreadyInProgress); } // And also we should check if the specified asset pair is enabled. var storedAssetPair = _assetPairsManager.TryGetEnabledPairAsync(request.AssetPairId).GetAwaiter().GetResult(); if (storedAssetPair == null || !_candlesHistoryRepository.CanStoreAssetPair(request.AssetPairId)) { return(LongTaskLaunchResult.AssetPairNotSupported); } var epsilon = Math.Pow(10, -storedAssetPair.Accuracy); _log.Info(nameof(Filtrate), $"Starting candles with extreme price filtration for {request.AssetPairId}..."); Health = new CandlesFiltrationHealthReport(request.AssetPairId, request.LimitLow, request.LimitHigh, analyzeOnly); var priceTypeTasks = new List <Task>(); if (request.PriceType.HasValue) { priceTypeTasks.Add( DoFiltrateAsync(request.AssetPairId, request.LimitLow, request.LimitHigh, request.PriceType.Value, epsilon, analyzeOnly)); } else { foreach (var priceType in Constants.StoredPriceTypes) { priceTypeTasks.Add( DoFiltrateAsync(request.AssetPairId, request.LimitLow, request.LimitHigh, priceType, epsilon, analyzeOnly)); } } Task.WhenAll(priceTypeTasks.ToArray()).ContinueWith(t => { Health.State = CandlesFiltrationState.Finished; if (analyzeOnly) { _log.Info(nameof(Filtrate), $"Filtration for {request.AssetPairId} finished: analyze only. Total amount of candles to delete: {Health.DeletedCandlesCount.Values.Sum()}, " + $"total amount of candles to replace: {Health.ReplacedCandlesCount.Values.Sum()}. Errors count: {Health.Errors.Count}."); } else { _log.Info(nameof(Filtrate), $"Filtration for {request.AssetPairId} finished. Total amount of deleted Sec candles: {Health.DeletedCandlesCount.Values.Sum()}, " + $"total amount of replaced bigger candles: {Health.ReplacedCandlesCount.Values.Sum()}. Errors count: {Health.Errors.Count}."); } }); return(LongTaskLaunchResult.Started); }
/// <summary> /// Throws an <see cref="InvalidOperationException"/> if the specified asset pair is not supported. /// </summary> /// <param name="assetPairId">Asset pair ID.</param> private void CheckupAssetPairOrFail(string assetPairId) { if (!_candlesHistoryRepository.CanStoreAssetPair(assetPairId)) { throw new InvalidOperationException($"Connection string for asset pair {assetPairId} not configured"); } }
public async Task InitializeCacheAsync() { // Depending on cache invalidation period and on asset pairs amount, there may be a case when // the invalidation timer fires before the cache loading has stopped. This will be a signal // to skip timer-based invalidation. // Below we combine two approaches: // - we're exporting a fast signal for any timer-based routine that we have already been initializing the cache; // - and we additionally block all the cache-related operations for other parts of code. // The first is needed to avoid queueing of timer events. If we simply use blocks, we will finally face a problem // when cache initialization may become infinitly repeated. The second is important for avoidining a multi-threaded // write operations to the cache: if we've got a candle update set, we need to await for cache fill completion and // then proceed. lock (_initializationStateLocker) { if (InitializationState == CacheInitializationState.InProgress) { return; } InitializationState = CacheInitializationState.InProgress; } await _cacheSem.WaitAsync(); try { _log.Info(nameof(InitializeCacheAsync), "Caching candles history..."); SlotType activeSlot = await _candlesCacheService.GetActiveSlotAsync(_marketType); //initialize cache to inactive slot SlotType initSlot = activeSlot == SlotType.Slot0 ? SlotType.Slot1 : SlotType.Slot0; var assetPairs = await _assetPairsManager.GetAllAsync(); var now = _clock.UtcNow; foreach (var pairs in assetPairs.Where(a => _candlesHistoryRepository.CanStoreAssetPair(a.Id)).Batch(6)) { var tasks = pairs.Select(p => CacheAssetPairCandlesAsync(p, now, initSlot)); await Task.WhenAll(tasks); } await _candlesCacheService.InjectCacheValidityToken(); // Initial validation token set. await _candlesCacheService.SetActiveSlotAsync(_marketType, initSlot); //switch slots _log.Info(nameof(InitializeCacheAsync), "All candles history is cached"); } finally { InitializationState = CacheInitializationState.Idle; _cacheSem.Release(); } }
public async Task InitializeCacheAsync() { await _log.WriteInfoAsync(nameof(CandlesCacheInitalizationService), nameof(InitializeCacheAsync), null, "Caching candles history..."); var assetPairs = await _assetPairsManager.GetAllEnabledAsync(); var now = _clock.UtcNow; var cacheAssetPairTasks = assetPairs .Where(a => _candlesHistoryRepository.CanStoreAssetPair(a.Id)) .Select(assetPair => CacheAssetPairCandlesAsync(assetPair, now)); await Task.WhenAll(cacheAssetPairTasks); await _log.WriteInfoAsync(nameof(CandlesCacheInitalizationService), nameof(InitializeCacheAsync), null, "All candles history is cached"); }
public async Task <string> MigrateAsync(string assetPairId, IHistoryProvider historyProvider) { if (!MigrationEnabled) { return(string.Empty); } if (!_candlesHistoryRepository.CanStoreAssetPair(assetPairId)) { return($"Connection string for the asset pair '{assetPairId}' not configuer"); } var assetPair = await _assetPairsManager.TryGetAssetPairAsync(assetPairId); if (assetPair == null) { return($"Asset pair '{assetPairId}' not found"); } lock (_assetManagers) { if (_assetManagers.ContainsKey(assetPairId)) { return($"{assetPairId} already being processed"); } var telemetryService = new AssetPairMigrationTelemetryService(_logFactory, assetPairId); var assetManager = new AssetPairMigrationManager( _healthService, _candlesPersistenceQueue, _candlesGenerator, telemetryService, assetPair, _logFactory, new BidAskHCacheService(), historyProvider, _candlesHistoryMigrationService, OnMigrationStopped, _settings); assetManager.Start(); _assetHealthServices.Add(assetPairId, telemetryService); _assetManagers.Add(assetPairId, assetManager); return($"{assetPairId} processing is started"); } }
public async Task <LongTaskLaunchResult> RunFixMidPricesAsync(string assetPairId, bool analyzeOnly) { // We should not run filtration multiple times before the first attempt ends. if (Health != null && Health.State == CandlesFiltrationState.InProgress) { return(LongTaskLaunchResult.AlreadyInProgress); } // And also we should check if the specified asset pair is enabled. var storedAssetPair = await _assetPairsManager.TryGetEnabledPairAsync(assetPairId); if (storedAssetPair == null || !_candlesHistoryRepository.CanStoreAssetPair(assetPairId)) { return(LongTaskLaunchResult.AssetPairNotSupported); } _log.Info($"Starting mid candles price fix for {assetPairId}..."); Health = new MidPricesFixHealthReport(assetPairId, analyzeOnly); #pragma warning disable 4014 Task.Run(async() => #pragma warning restore 4014 { await FixMidPricesAsync(assetPairId, storedAssetPair.Accuracy, analyzeOnly); Health.State = CandlesFiltrationState.Finished; if (analyzeOnly) { _log.Info($"Mid candle prices fix for {assetPairId} finished: analyze only. Total amount of corrupted candles: {Health.CorruptedCandlesCount}, " + $" errors count: {Health.Errors.Count}."); } else { _log.Info($"Mid candle prices fix for {assetPairId} finished. Total amount of corrupted candles: {Health.CorruptedCandlesCount}, " + $"errors count: {Health.Errors.Count}."); } }); return(LongTaskLaunchResult.Started); }
/// <summary> /// Checks if we can handle/store the given asset pair. /// </summary> /// <param name="assetPairId">Asset pair ID.</param> /// <returns>True if repository is able to store such a pair, and false otherwise.</returns> public virtual bool CanHandleAssetPair(string assetPairId) => _candlesHistoryRepository.CanStoreAssetPair(assetPairId);