public static void FillGapsByTickerFast(string ticker, DateTime?startTime, DateTime?endTime, List <GapInfo> gaps, string quoteCacheFolder, BackgroundWorker worker, Action <string, List <GapInfo> > gapsUpdatedAction) { if (gaps == null || gaps.Count == 0) { return; } // старые свечи var candlesOld = AtomCandleStorage.Instance.GetAllMinuteCandles(ticker) ?? new List <CandleData>(); var visualGaps = new List <GapInfo>(); if (worker != null && worker.CancellationPending) { return; } // корректируем интервал запроса var requestGaps = new List <GapInfo>(); var histStart = GapMap.Instance.GetServerTickerHistoryStart(ticker); var startFillTime = startTime.HasValue ? (startTime.Value < histStart ? histStart : startTime.Value) : histStart; var endFillTime = endTime.HasValue ? endTime.Value : DateTime.Now; if (startFillTime > endFillTime) { return; } // отсекаем части гэпов или гэпы целиком, находящиеся за пределами интервала запроса foreach (var gap in gaps) { if (gap.end < startFillTime || gap.start > endFillTime) { visualGaps.Add(new GapInfo { start = gap.start, end = gap.end, status = GapInfo.GapStatus.FailedToFill }); continue; } var start = gap.start; var end = gap.end; if (start < startFillTime) { visualGaps.Add(new GapInfo { start = gap.start, end = startFillTime.AddMinutes(-1), status = GapInfo.GapStatus.FailedToFill }); start = startFillTime; } if (end > endFillTime) { visualGaps.Add(new GapInfo { start = endFillTime.AddMinutes(1), end = gap.end, status = GapInfo.GapStatus.FailedToFill }); end = endFillTime; } requestGaps.Add(new GapInfo { start = start, end = end }); visualGaps.Add(new GapInfo { start = start, end = end }); } // группируем - составляем запросы var gapLists = GapList.CreateGapList(requestGaps, daysInQuoteServerRequest); // обновляем контрол gapsUpdatedAction(ticker, visualGaps); if (gapLists.Count == 0 || gapLists.First().Gaps.Count == 0) { return; } // в результат добавляем старые начальные свечи, не вошедшие в запрос var candlesOldIndex = 0; var candlesNew = new List <CandleData>(); // результат - обновленные свечи var nextGap = gapLists.First().Gaps.First(); for (; candlesOldIndex < candlesOld.Count; candlesOldIndex++) { if (candlesOld[candlesOldIndex].timeOpen > nextGap.start) { break; } candlesNew.Add(candlesOld[candlesOldIndex]); } // отправляем запросы, параллельно обновляя контрол for (var gapListIndex = 0; gapListIndex < gapLists.Count; gapListIndex++) { // проверяем на завершение операции if (worker != null && worker.CancellationPending) { return; } if (currentTickerCancelled) { currentTickerCancelled = false; return; } var gapList = gapLists[gapListIndex]; if (gapList.Gaps.Count == 0) { continue; } var ok = LoadQuotesFromServerFast(ticker, gapList, candlesNew); // добавляем старые свечи, встретившиеся между гэпами (после gapIndex до gapIndex + 1) DateTime lastTime; for (var gapIndex = 0; gapIndex < gapList.Gaps.Count - 1; gapIndex++) { lastTime = gapList.Gaps[gapIndex].end; var nextTime = gapList.Gaps[gapIndex + 1].start; var candlesOldFoundIndexForGap = candlesOld.FindIndex(candlesOldIndex, c => c.timeOpen > lastTime); if (candlesOldFoundIndexForGap == -1) { continue; } var insertPosition = candlesNew.FindIndex(c => c.timeOpen >= nextTime); if (insertPosition == -1) { continue; } var candlesOldForGap = new List <CandleData>(); for (; candlesOldFoundIndexForGap < candlesOld.Count; candlesOldFoundIndexForGap++) { if (candlesOld[candlesOldFoundIndexForGap].timeOpen >= nextTime) { break; } candlesOldForGap.Add(candlesOld[candlesOldFoundIndexForGap]); } candlesOldIndex = candlesOldFoundIndexForGap; candlesNew.InsertRange(insertPosition, candlesOldForGap); } // обновляем контрол foreach (var gap in gapList.Gaps) { var thisGap = gap; visualGaps.RemoveByPredicate(g => g.start == thisGap.start && g.end == thisGap.end, true); visualGaps.Add(new GapInfo { start = gap.start, end = gap.end, status = ok ? GapInfo.GapStatus.Filled : GapInfo.GapStatus.FailedToFill }); } gapsUpdatedAction(ticker, visualGaps); // добавляем старые свечи, встретившиеся между запросами lastTime = gapList.Gaps.Last().end; var candlesOldFoundIndex = candlesOld.FindIndex(candlesOldIndex, c => c.timeOpen > lastTime); if (candlesOldFoundIndex == -1) { continue; } candlesOldIndex = candlesOldFoundIndex; // если есть еще гэп, то добавляем свечки до него if (gapListIndex + 1 >= gapLists.Count) { continue; } nextGap = gapLists[gapListIndex + 1].Gaps.First(); for (; candlesOldIndex < candlesOld.Count; candlesOldIndex++) { if (candlesOld[candlesOldIndex].timeOpen >= nextGap.start) { break; } candlesNew.Add(candlesOld[candlesOldIndex]); } } // сохраняем результат // сохраняем карту гэпов // дырки, оставшиеся в истории, и есть серверные гэпы if (candlesNew.Count > 0) { var updatedGapsInfo = QuoteCacheManager.GetGaps(candlesNew, startFillTime, endFillTime); GapMap.Instance.UpdateGaps(ticker, updatedGapsInfo); GapMap.Instance.SaveToFile(); } // сохранить свечи в хранилище и в файл AtomCandleStorage.Instance.RewriteCandles(ticker, candlesNew); AtomCandleStorage.Instance.FlushInFile(quoteCacheFolder, ticker); }
private static bool LoadQuotesFromServerFast(string ticker, GapList gaps, List<CandleData> candles) { // запросить данные на сервере var intervals = gaps.Gaps.Select(g => new Cortege2<DateTime, DateTime>(g.start, g.end)).ToList(); IQuoteStorage storage; try { storage = Contract.Util.Proxy.QuoteStorage.Instance.proxy; } catch (Exception) { Logger.Error("LoadQuotesFromServerFast: Связь с сервером (IQuoteStorageFastBinding) не установлена"); return false; } try { var packedQuoteStream = storage.GetMinuteCandlesPackedFast(ticker, intervals); if (packedQuoteStream == null || packedQuoteStream.count == 0) return true; var packedCandles = packedQuoteStream.GetCandles(); candles.AddRange(packedCandles.Select(c => new CandleData(c, DalSpot.Instance.GetPrecision10(ticker)))); return true; } catch (Exception ex) { Logger.ErrorFormat("LoadQuotesFromServerFast: Ошибка закачки котировок {0}: {1}", ticker, ex.Message); return false; } }