Обработка летнего времени из стороннего API

Я использую CoinbasePro API для извлечения исторических рыночных данных по акциям.

Примечание: API может возвращать не более 300 свечей на запрос.

В приведенном ниже коде создается список задач, которые должны выполняться простым пакетным процессором с ограниченной скоростью В целом этот код не имеет отношения к проблеме, но здесь для получения полной картины.:

var startTime = new DateTimeOffset(DateTime.Today.AddYears(-2)).UtcDateTime;
var totalEndDate = new DateTimeOffset(DateTime.Today).UtcDateTime;
var historicalDataStream = this.GetHistoricalDataStream(
    "BTC-USD", Granularity.One_Hour, startTime, totalEndDate, 250, stoppingToken).ToArray();

while (index < historicalDataStream.Length)
{
    var timer = Task.Delay(TimeSpan.FromSeconds(DELAY_IN_SECONDS)); // ".2" to make sure
    var tasks = historicalDataStream
        .Skip(index)
        .Take(MAX_ITEMS_PER_SECOND)
        .Select(producer => producer(coinbaseClient));

    var tasksAndTimer = tasks.Concat(new[] { timer });
    await Task.WhenAll(tasksAndTimer);

    var product = this.GetProductFromId(cryptoContext, productId);

    //we must await the save since the same ef context cannot be used in parallel
    await this.SaveCandleData(cryptoContext, product, tasks);
    index += MAX_ITEMS_PER_SECOND;
}

Составляю список задач:

public IEnumerable<Func<CoinbaseProClient, Task<IEnumerable<HistoricalCandle>>>> GetHistoricalDataStream(string productId, Granularity granularity, DateTimeOffset totalStartDate, DateTimeOffset totalEndDate, int maxResults, CancellationToken stoppingToken)
{
    var startDate = totalStartDate;
    var maxRangeSeconds = maxResults * (int)granularity;
    while (startDate < totalEndDate)
    {
        var tmpEndDate = startDate.AddSeconds(maxRangeSeconds);
        var endDate = ((tmpEndDate < totalEndDate) ? tmpEndDate : totalEndDate);

        //Create closures so the variables do not change
        var s = startDate;
        var e = endDate;
        var g = granularity;

        yield return (client) =>
        {
            return this.ProcessHistoricalDataAsync(client, productId, s, e, g, stoppingToken);
        };

        startDate = endDate;
    }
}

protected Task<IEnumerable<HistoricalCandle>> ProcessHistoricalDataAsync(CoinbaseProClient client, string productId, DateTimeOffset start, DateTimeOffset end, Granularity granularity, CancellationToken stoppingToken)
{
    var startDateoffset = start.UtcDateTime;
    var endDateoffset = end.UtcDateTime;
    return client.MarketData.GetHistoricRatesAsync(productId, startDateoffset, endDateoffset, (int)granularity, stoppingToken)
        .ContinueWith(task => task.Result.Select(x => new HistoricalCandle { 
            StartDate = x.Time,
            GranularitySeconds = (int)granularity,
            Open = x.Open ?? 0,
            High = x.High ?? 0,
            Low = x.Low ?? 0,
            Close = x.Close ?? 0,
            Volume = x.Volume ?? 0
        })
    );
}

Рядом с конечными точками каждого пакета есть повторяющиеся записи, я предполагаю, что это связано с переходом на летнее время

Id StartDate GranularitySeconds Open High Low Close Volume ProductId 117842 2019-08-22 00:00:00.0000000 +00:00 3600 10133.34000000 10150.00000000 10034.79000000 10074.51000000 272.61765990 1

117848 2019-08-22 00:00:00.0000000 +00:00 3600 10133.34000000 10150.00000000 10034.79000000 10074.51000000 272.61765990 1

В то время как в другие дни меньше записей

Date Count

2020-10-20, 23

2019-10-31, 23

Как видите, я попытался нормализовать все, чтобы использовать дату в формате UTC, но все равно получаю неверные данные. Я не вижу, что я здесь делаю не так, как я могу получить правильный набор результатов?

Вы передаете объекты datetime в GetHistoricRatesAsync, но неясно, ожидает ли он UTC. Вы должны проверить документацию этого API, чтобы узнать, что он ожидает от этих параметров.

JoelFan 30.03.2021 03:06

Кроме того, вы неправильно используете DateTimeOffset. Вы вызываете свойство UtcDate, которое нет возвращает DateTimeOffset. Он возвращает DateTime. Затем вы вставляете этот DateTime в свой собственный метод, который ожидает DateTimeOffset, предположительно выполняя некоторое преобразование. Вы должны просто передать DateTimeOffset в свой метод. При вызове API нет необходимости вызывать UtcDateTime до самого конца. Вся суть DateTimeOffset в том, чтобы сделать преобразование всего в UTC без излишнего труда. Он обрабатывает все это изнутри.

JoelFan 30.03.2021 03:10

Обязательный комментарий, когда OP борется с проблемами DateTime: посмотрите NodaTime (nodatime.org), это может помочь

Flydog57 30.03.2021 04:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
20
0

Другие вопросы по теме