/// <summary> /// Подписаться на исторические данные /// </summary> /// <param name="consumer"> /// Потребитель исторических данных /// </param> /// <param name="instrument"> /// Инструмент /// </param> /// <param name="begin"> /// Начало диапазона /// </param> /// <param name="span"> /// Интервал свечей для исторических данных /// </param> /// <returns> /// Подписка на исторические данные /// </returns> /// <remarks> /// Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал, /// если он не в состоянии предоставить запрошенные данные. /// </remarks> public async Task <IHistoryDataSubscription> SubscribeToHistoryDataAsync( IHistoryDataConsumer consumer, Instrument instrument, DateTime begin, HistoryProviderSpan span) { var symbol = await adapter.ResolveSymbolAsync(instrument); if (symbol == null) { consumer.Error($"Unable to resolve symbol for {instrument}"); return(new NullHistoryDataSubscription()); } QLAdapter.Log.Debug().Print($"Candles subscription: {symbol}, span {span}, from {begin}"); var subscriptionMessage = new QLHistoryDataSubscription(symbol, begin, span); var subscription = new HistoryDataSubscription(subscriptionMessage.id, instrument, begin, span, adapter, consumer); using (requestsLock.Lock()) { subscriptions[subscription.Id] = subscription; } adapter.SendMessage(subscriptionMessage); return(subscription); }
/// <summary> /// Получить исторические данные /// </summary> /// <param name="consumer"> /// Потребитель исторических данных /// </param> /// <param name="instrument"> /// Инструмент /// </param> /// <param name="begin"> /// Начало диапазона /// </param> /// <param name="end"> /// Конец диапазона /// </param> /// <param name="span"> /// Интервал свечей для исторических данных /// </param> /// <param name="cancellationToken"> /// Токен отмены /// </param> /// <returns> /// Исторические данные /// </returns> /// <remarks> /// Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал, /// если он не в состоянии предоставить запрошенные данные. /// </remarks> /// <exception cref="NoHistoryDataException"> /// Бросается, если исторические данные за указанный период недоступны /// </exception> public async Task GetHistoryDataAsync( IHistoryDataConsumer consumer, Instrument instrument, DateTime begin, DateTime end, HistoryProviderSpan span, CancellationToken cancellationToken = new CancellationToken()) { var symbol = await adapter.ResolveSymbolAsync(instrument); if (symbol == null) { consumer.Error($"Unable to resolve symbol for {instrument}"); return; } QLAdapter.Log.Debug().Print($"Candles request: {symbol}, span {span}, from {begin} to {end}"); var dataRequestMessage = new QLHistoryDataRequest(symbol, span); var request = new HistoryDataRequest(dataRequestMessage.id, instrument, begin, end, span); using (requestsLock.Lock()) { requests[request.Id] = request; } // Поддержка отмены запроса cancellationToken.RegisterSafe(() => request.TrySetCanceled()); adapter.SendMessage(dataRequestMessage); var data = await request.Task; QLAdapter.Log.Debug().Print("Push candles to consumer. ", LogFields.RequestId(request.Id)); consumer.Update(data, HistoryDataUpdateType.Batch); }
/// <summary> /// Получить исторические данные /// </summary> /// <param name="consumer"> /// Потребитель исторических данных /// </param> /// <param name="instrument"> /// Инструмент /// </param> /// <param name="begin"> /// Начало диапазона /// </param> /// <param name="end"> /// Конец диапазона /// </param> /// <param name="span"> /// Интервал свечей для исторических данных /// </param> /// <param name="cancellationToken"> /// Токен отмены /// </param> /// <returns> /// Исторические данные /// </returns> /// <remarks> /// Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал, /// если он не в состоянии предоставить запрошенные данные. /// </remarks> /// <exception cref="NoHistoryDataException"> /// Бросается, если исторические данные за указанный период недоступны /// </exception> public async Task GetHistoryDataAsync( IHistoryDataConsumer consumer, Instrument instrument, DateTime begin, DateTime end, HistoryProviderSpan span, CancellationToken cancellationToken = new CancellationToken()) { using (LogManager.Scope()) { var instrumentData = await instrumentConverter.ResolveInstrumentAsync(this, instrument); if (instrumentData == null) { consumer.Error($"Unable to resolve symbol for {instrument}"); return; } // Выгружаем список точек var points = await FetchHistoryDataAsync(instrumentData.Symbol, begin, end, span, cancellationToken); // Собираем результат var data = new HistoryData(instrument, begin, end, span); foreach (var p in points.OrderBy(_ => _.Point)) { data.Points.Add(p); } // Передаем результат потребителю consumer.Update(data, HistoryDataUpdateType.Batch); } }
/// <summary> /// Подписаться на исторические данные /// </summary> /// <param name="consumer"> /// Потребитель исторических данных /// </param> /// <param name="instrument"> /// Инструмент /// </param> /// <param name="begin"> /// Начало диапазона /// </param> /// <param name="span"> /// Интервал свечей для исторических данных /// </param> /// <returns> /// Подписка на исторические данные /// </returns> /// <remarks> /// Провайдер вправе переопределить параметры исторических графиков - диапазон, интервал, /// если он не в состоянии предоставить запрошенные данные. /// </remarks> public async Task <IHistoryDataSubscription> SubscribeToHistoryDataAsync( IHistoryDataConsumer consumer, Instrument instrument, DateTime begin, HistoryProviderSpan span) { var instrumentData = await instrumentConverter.ResolveInstrumentAsync(this, instrument); if (instrumentData == null) { consumer.Error($"Unable to resolve symbol for {instrument}"); return(new NullHistoryDataSubscription()); } // Создаем подписку var subscription = new HistorySubscription(this, consumer, instrument, instrumentData.Symbol, span); subscription.StartFetch(begin); return(subscription); }
private void HandleException(Exception exception, string message) { consumer.Error(message); _Log.Error().Print(exception, $"History data subscription failed: {message.Preformatted()}"); Dispose(); }
public void Process(TimeBarReport report, out bool shouldRemoveHandler) { shouldRemoveHandler = false; // Проверяем статус отчета var status = (TimeBarReport.StatusCode)report.status_code; switch (status) { case TimeBarReport.StatusCode.SUCCESS: case TimeBarReport.StatusCode.SUBSCRIBED: case TimeBarReport.StatusCode.UPDATE: break; case TimeBarReport.StatusCode.DISCONNECTED: case TimeBarReport.StatusCode.DROPPED: shouldRemoveHandler = true; return; default: shouldRemoveHandler = true; consumer.Error(report.text_message); return; } // Складываем точки в словарь var dataPoints = provider.PrepareDataPoints(contractId, report) .Where(p => p.High != 0 && p.Low != 0 && p.Open != 0 && p.Close != 0); // иногда нули приходят foreach (var point in dataPoints) { HistoryDataPoint p; if (!points.TryGetValue(point.Point, out p)) { points.Add(point.Point, point); newPoints++; continue; } if (p != point) { // TODO это неоптимально, в data.Points уже есть точка для этой даты, надо ее обновить points[point.Point] = point; updatedPoints++; } } // Если хотя бы одна точка изменилась - перестраиваем набор данных if (newPoints >= 0 || updatedPoints > 0) { var minDate = DateTime.MaxValue; var maxDate = DateTime.MinValue; data.Points.Clear(); foreach (var p in points.OrderBy(_ => _.Key)) { data.Points.Add(p.Value); if (p.Key > maxDate) { maxDate = p.Key; } if (p.Key < minDate) { minDate = p.Key; } } data.Begin = minDate; data.End = maxDate; } // Если еще не все данные пришли - выходим if (!report.is_report_complete) { CQGCAdapter.Log.Debug().Print( $"Got a {nameof(TimeBarReport)} but it's incomplete", LogFields.Instrument(data.Instrument), LogFields.Span(data.Span)); return; } if (newPoints > 0 || updatedPoints > 0) { if (newPoints == 1 && updatedPoints == 0) { // Пришла одна новая свечка consumer.Update(data, HistoryDataUpdateType.OnePointAdded); } else if (newPoints == 0 && updatedPoints == 1) { // Одна свечка обновилась consumer.Update(data, HistoryDataUpdateType.OnePointUpdated); } else { // Свалилась пачка новых данных consumer.Update(data, HistoryDataUpdateType.Batch); } newPoints = 0; updatedPoints = 0; } }