Аватар (Андрей)
Андрей

Оптимизация расчета средних

17 Мая 2017, 21:02, Рубрики: Алготрейдинг, Индикаторы, Исследования, Статьи, Торговая платформа

При разработке торговых алгоритмов и индикаторов, например таких как Simple Moving Average или SMA, зачастую требуется выполнить расчет среднего значения некоторого показателя, например цены, за некоторый период времени. При этом количество значений, которые участвуют в расчете, может варьироваться от нескольких единиц до нескольких сотен или даже тысяч. Более того, часто нужно рассчитывать среднее не по одному такому ряду, а по нескольким, к тому же сразу в нескольких стратегиях. Дело усложняется еще и тем, что эти расчеты чаще всего требуется проводить в режиме реального времени при поступлении новых данных о цене в моменты рыночных сделок или при обновлении данных в стаканах. А уж если говорить о тестировании стратегий, а тем более об их оптимизации, то скорость расчетов становится критически важной.

Давайте рассмотрим варианты, которые можно было бы использовать для расчетов средних по набору значений. Самый простой способ — в цикле пробежаться по всем значениям, посчитать сумму и результат разделить на их количество:

Этот подход вполне прост, понятен и не требует особых знаний в программировании, чтобы его реализовать. Можно даже воспользоваться готовым алгоритмом из стандартной библиотеки:

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

Вывод нашей итоговой программы будет следующим:

Нетрудно видеть, что при небольшом размере буфера с данными и малых значениях count, данный расчет вполне приемлем по скорости выполнения. Однако с ростом размеров буфера и достаточно частом обновлении его значений нагрузка будет повышаться. Как же можно оптимизировать данный алгоритм?

Если присмотреться повнимательнее, то можно заметить, что большинство значений, участвующих в расчете, остаются неизменными, а меняется только последнее значение. А что, если каким-то образом сделать расчет базового значения средней заранее и использовать его результат уже в итоговом вычислении при обновлении последнего значения массива? Ниже показано, как этого можно добиться:

Несколько слов о том, что мы тут сделали. Сначала мы рассчитали среднее по массиву с данными, исключая последнее значение (функция calcPreAverage() и вызов ее в строке 27) и результат запомнили в переменной pre_average. Затем в цикле в строке 32 передаем эту переменную для расчета окончательного среднего значения в функцию calcSmartAverage().

Теперь давайте посмотрим, что мы выиграли от усовершенствованного способа расчета среднего. Для этого немного переделаем код и воспользуемся классом TimingUtil, который был описан в статье о контейнере boost::circular_buffer.

В результате выполнения программы можем наблюдать существенную разницу в скорости выполнения каждого алгоритма:

Итак, имея выигрыш по скорости почти в пять раз, можно значительно повысить общую производительность системы.

В дополнение нужно сказать, что если в массив добавляется новое значение, например, при смене какого-то временного интервала, то, конечно же, базовое среднее значение нужно будет пересчитать. Однако это будет происходить на несколько порядков реже и не будет вносить заметного влияния.

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

Добавить комментарий

Для отправки комментария вы должны авторизоваться.