Самый быстрый способ перечисления элементов, когда каждому из них требуется информация из предыдущих элементов - PullRequest
0 голосов
/ 26 февраля 2020

Я должен рассчитать вещи для 50 элементов. Каждый из них требует предыдущих 20 элементов. Посмотрите на следующий код:

var klines = _client.GetKlines(bot.Symbol, bot.Interval, limit: 51).Data.SkipLast(1).ToList(); // 50 elements

for (int i = 0; i < klines.Count; i++)
{
    var candles = _client.GetKlines(bot.Symbol, bot.Interval, endTime: klines[i].OpenTime, limit: 20).Data;

    decimal SMA5 = ExponentialMovingAverage.SMA(candles.TakeLast(5).ToList());
    decimal SMA10 = ExponentialMovingAverage.SMA(candles.TakeLast(10).ToList());
    decimal SMA20 = ExponentialMovingAverage.SMA(candles.TakeLast(20).ToList());

    Console.WriteLine($"OpenTime: {klines[i].OpenTime.ToLocalTime()} | MA5: {decimal.Round(SMA5, 6, MidpointRounding.AwayFromZero)} | MA10: {decimal.Round(SMA10, 6, MidpointRounding.AwayFromZero)} | MA20: {decimal.Round(SMA20, 6, MidpointRounding.AwayFromZero)}");
}

Я извлекаю данные с серверов на каждой итерации с помощью _client.GetKlines. Самый быстрый способ - получить данные сразу, а затем обработать их. Например, вместо 50 элементов в начале я получу 70, потому что мне не хватает только первых 20 для первого элемента. Остальные элементы содержатся в списке.

Что-то вроде:

var klines = _client.GetKlines(bot.Symbol, bot.Interval, limit: 51 + 20).Data.SkipLast(1).ToList(); // 50 elements + previous 20 for the first element

for (int i = 0 + 20; i < klines.Count; i++)
{
    // process elements from the existing records, instead of _client.GetKlines everytime
}

Какой самый быстрый способ сделать это? С точки зрения производительности.

Редактировать: Рабочий код, как я и хотел. Можно ли его оптимизировать больше?

int j = 0;
for (int i = 19; i < klines.Count; i++)
{
    var candles = klines.Skip(j).Take(20);

    decimal SMA5 = ExponentialMovingAverage.SMA(candles.TakeLast(5).ToList());
    decimal SMA10 = ExponentialMovingAverage.SMA(candles.TakeLast(10).ToList());
    decimal SMA20 = ExponentialMovingAverage.SMA(candles.TakeLast(20).ToList());

    Console.WriteLine($"OpenTime: {klines[i].OpenTime.ToLocalTime()} | MA5: {decimal.Round(SMA5, 6, MidpointRounding.AwayFromZero)} | MA10: {decimal.Round(SMA10, 6, MidpointRounding.AwayFromZero)} | MA20: {decimal.Round(SMA20, 6, MidpointRounding.AwayFromZero)}");
    j++;
}

1 Ответ

1 голос
/ 26 февраля 2020

Здесь я пытаюсь реализовать ваше поведение, используя Queue<int>, чтобы меньше вызовов Skip и Take. Имя функции ToRanges было лучшим, которое я мог придумать на данный момент. Вы должны проверить код, чтобы убедиться, что он соответствует вашим потребностям.

var klines = _client.GetKlines(bot.Symbol, bot.Interval, limit: 51 + 20).Data.SkipLast(1).ToList(); // 50 elements + previous 20 for the first element

var ranges = ToRanges(klines, 20);
var smaData = ranges.Select(r => new {
    OpenTime = r.First().OpenTime.ToLocalTime(),
    SMA5 = ExponentialMovingAverage.SMA(r.TakeLast(5)), 
    SMA10 = ExponentialMovingAverage.SMA(r.TakeLast(10)),
    SMA20 = ExponentialMovingAverage.SMA(r)
});

foreach(var item in smaData)
{
   Console.WriteLine($"OpenTime: {item.OpenTime.ToLocalTime()} | MA5: {decimal.Round(item.SMA5, 6, MidpointRounding.AwayFromZero)} | MA10: {decimal.Round(item.SMA10, 6, MidpointRounding.AwayFromZero)} | MA20: {decimal.Round(item.SMA20, 6, MidpointRounding.AwayFromZero)}");
}


private IEnumerable<IEnumerable<int>> ToRanges(IEnumerable<int> list, int rangeSize)
{
    var queue = new Queue<int>(splitSize);
    foreach(var item in list)
    {
        queue.Enqueue(item);
        if(queue.Count == rangeSize)
        {
            yield return queue;
            queue.Dequeue();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...