Как я сделал тестер-оптимизатор для нахождения прибыльных стратегий на бирже

в 14:49, , рубрики: .net, Алгоритмы, алготрейдинг, математика, оптимизация, торговые роботы, метки: , , ,

Введение

image

В алгоритмическом трейдинге при создании механических торговых систем (МТС) очень важен вопрос времени жизни торговых алгоритмов. Да, и найти их в принципе достаточно сложно. В условиях постоянно меняющегося рынка рано или поздно наступает момент, когда даже самый совершенный и прибыльный алгоритм начинает приносить убытки. И его нужно, что называется, «подкручивать» или оптимизировать под текущие условия рынка. Одними из самых распространенных являются торговые системы (ТС), работающие со свечными графиками с их многообразием индикаторов для технического анализа.

image

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

Методы оптимизации стратегий

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

Есть уже готовые оптимизаторы стратегий в других программных продуктах типа Wealth-Lab, AmiBroker. Но в них применяются свои скриптовые языки и как правило возникают какие-то другие ограничения, и протестировать стратегии полностью не получается. Как в них перевести свои стратегии? В моём случае это стратегии написанные на C# с помощью библиотек S#.Api. Все ли может этот тестировщик, что нам нужно? Будут ли тесты отражать реальность? И еще много других вопросов возникает, когда начинаешь изучать эту тему подробнее.

К тому же это «черные ящики» и как они делают подсчёты на самом деле, никто не знает. А когда дело касается денег не должно быть места всяким случайностям и неопределенностям. “На слово” создателям такого программного обеспечения я не верю. Сколько раз я сталкивался в самых серьезных продуктах с всякими глюками и багами, письмами и звонками в техподдержку. При этом мы становимся зависимыми от совершенно не нужных нам людей. В общем, доверия у меня к ним никакого. Все эти проблемы сильно замедляют реализацию алгоритмов, и соответственно отнимают наше время и деньги.

И я задался вопросом: «А почему бы не написать свой оптимизатор? Неужели так сложно?» Как оказалось сложно, но вполне реально. К тому же появляется уверенность в результатах и свобода в настройках и модернизациях и модификациях программы. Собственно с этими мыслями я взялся за работу.

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

Ознакомился с основными применяемыми стохастическими способами оптимизации – генетика, монте-карло, рой частиц, их разновидностями и прочими методами. Вообще разновидностей стохастических методов очень много. Например, метод «Роя частиц» или столь популярные «Генетические алгоритмы». Есть также элегантные решения типа алгоритма «Имитации отжига» (красивая гифка справа, советую посмотреть).

Последний способ, например, с высокой степенью гарантирует нахождение глобального экстремума. Так как при этом методе он периодически отклоняется от пути и дополнительно изучает соседние области. Но скорость исследования не самая высокая. Суть методов одна — мы выбираем случайные значения и так или иначе их анализируем. От способа к способу меняются только два параметра – скорость и точность исследования. Причем обратно пропорционально. Чем выше скорость тестирования, тем хуже качество результатов и наоборот. При выборе метода каждый решает сам, чем он готов пожертвовать.

Поиск экстремумов

Например, метод «Имитации отжига» позволяет найти глобальный экстремум. Однако, если подумать, то сам глобальный экстремум нам ни к чему, если к нему нет сходимости. То есть если вокруг экстремума соседние условно равномерно не убывают, то очень вероятно, что этот глобальный экстремум носит случайный характер и пользы нам от него будет мало так как он неадекватный, а расчеты нам испортит. Поэтому так важно изучить параметры вокруг экстремума. Если есть сходимость, значит есть система и эту стратегию можно изучать дальше.

Все стохастические методы оптимизации имеют один общий недостаток – могут упереться в какой-то локальный экстремум, а тот самый оптимальный упустить из вида. Чтобы этого избежать, нужно максимально увеличивать области выборок и количество итераций. Но от этого страдает скорость расчетов. Так что нужно всегда искать золотую середину.
Из-за сложности и не очевидности расчетов, методы «Имитации отжиги» и прочие «Рои частиц» я отложил в сторону. В итоге пришел к выводу, что самый доступный и удобный способ в моем случае — это оптимизация методом «Монте-Карло».

Первая версия тестера-оптимизатора «Монте-Карло»

Классический поиск максимума

За основу своего первого тестера-оптимизатора решил взять логику из статьи «Нелинейная стохастическая оптимизация методом Монте-Карло» из сборника Санкт-Петербургского Государственного Университета. Кого интересует это направление, советую почитать их сборники. Много интересных разноплановых статей про оптимизацию в самых разных областях. Где эти стохастические методы только не применяют!

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

Есть масса способов определения величины уменьшения области выборки. Статистические, где изучают изменение градиента целевой функции или эмпирические, где смотрят на то как быстро меняется сам экстремум от итерации к итерации. И на основании этих данных решают, продолжать исследование дальше или остановить итерации, и принять, что мы уже нашли максимум с заданной погрешностью. Так называемый критерий останова.

Но как я уже выше заметил важно изучить область вокруг экстремума, и поэтому, решил сходиться до конца, и на последней итерации проверить полностью все соседние стратегии. Я не стал мудрить с градиентами и сделал сходимость статичной в процентах от начальной выборки. То есть на сколько резать многомерную матрицу после каждой итерации на 1% или на 20% мы решаем в самом начале. Также, мы сразу, учитывая наши возможности по времени, решаем, сколько стратегий мы будем брать из матрицы на каждой итерации для тестирования. Таким образом, нам вообще не важен размер матрицы, мы точно знаем сколько итераций и в каком объеме проведем! В этом и есть вся прелесть стохастических методов.
Основываясь на вышесказанном, написал программу для поиска лучших параметров стратегий.
Исходные данные для оптимизации:

  • тестируемый торговый инструмент,
  • диапазон истории,
  • таймфреймы свечек (хоть все, от 5 сек до нескольких часов),
  • диапазоны рассматриваемых параметров стратегии,
  • шаг в этих диапазонах,
  • процент уменьшения области выборки после итерации,
  • количество элементов в выборке,
  • количество стратегий отправляемых для тестирования.

Консольные тестеры (их у меня несколько, полностью загружают процессор) получают на вход параметры стратегий, тестируют их и результаты в конце сохраняют в бинарные файлы. Сделано это для промежуточного сохранения данных тестирования на случай ошибки, а также защита от утечек памяти и других глюков. И вообще диверсификация риска выхода чего-то из строя. Программа сама передает все данные, делит нагрузку так чтобы тестировшики работали одновременно на полную мощность и при завершении одного сразу запускался другой. Долго мучился, как все синхронизировать, но все получилось автономно, быстро и удобно (сильно помогли ребята из StockSharp)!
При этом все параметры и результаты выводятся на главное окно программы Монте-Карло. Так что, видно и понятно все что там происходит. Есть окно логирования и окно с итогами тестирования. После каждой итерации программа открывает сериализованные файлы, считает по ним статистику, сортирует и выводит на экран.

Интерфейс тестера-оптимизатора «Монте-Карло»:

image

В работе:

image

Конец, тестирования. Лучший результат стал с матожиданием 88%. Причем из 6060 вариантов протестировали только 778, из них 116 повторилось.

image

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

image

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

Окно результатов тестирования:

image

Все вместе:

image

Однако после реализации первого тестера-оптимизатора «Монте-Карло» и изучения его работы пришел к выводу, что он свою задачу выполняет, но не в том качестве, в каком мне хотелось. В классических методах оптимизации в каждой новой итерации ищется лучшее значение и уже вокруг него проводятся дальнейшие исследования. В моем случае относительно него я обрезал матрицу вариантов стратегий.

Условная схема работы стохастического алгоритма поиска максимума по методу Монте-Карло:
image

Усовершенствованный алгоритм

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

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

Как же быть в этом случае? Я решил отойти от классической схемы и поступить как в трейдинге: «Нельзя контролировать свою прибыль, можно контролировать только свои риски». Поэтому решил не рисковать и принять меры, чтобы не удалить ненароком хорошую стратегию из исследования.

Как тогда обрезать матрицу? Будем обрезать только те области, которые исследовали! То есть мы будем удалять микро области вокруг худших исследованных стратегий. Суть алгоритма сводится к тому, что мы не исследуем хорошие области стратегий, мы не исследуем плохие. А лучшие стратегии мы дополнительно можем исследовать в конце оптимизации.

Здесь показана работа такого алгоритма:

image

На самом деле матрица многомерная (в моем случае максимум 9 измерений), но для объяснения принципа работы воспользуемся всеми нами любимыми тремя измерениями:

  • Ось Х – параметры «длинной» скользящей средней
  • Ось Y – параметры «короткой» скользящей средней
  • Ось Z – результат тестирования стратегии пересечения «короткой» и «длинной» скользящих средних, в данном случае я взял мат ожидание

Точки в этом пространстве это уже протестированные варианты стратегий с разными значениями «длинной» и «короткой» скользящих. Чем светлее точка, тем лучше ее матожидание.

В принципе это можно было изобразить и в двух координатах:

image

Но в трех координатах мне нравится больше – нагляднее.
Так вот, черные точки в пространстве — это худшие стратегии по результатам тестирования. Линии их соединяющие путь движения алгоритма от точки к точке. Серые точки в плоскости – это стратегии, которые мы удаляем из области исследования. Линии между ними – это путь движения алгоритма удаления стратегий из матрицы. Линии между черными точками и серыми – это проекция худшей стратегии на плоскость. Одиночные серые точки на плоскости это проекции уже протестированных стратегий на плоскость.

Здесь видно как алгоритм идет от одной худшей стратегии к другой, начиная с самой плохой:

image

Преимущества алгоритма:

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

В итоге получаем что-то такое:

image

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

«Длинная — матожидание»

image

«Короткая — матожидание»

image

Внешний вид тестера-оптимизатора «Исследователь»:

image

Все приложения были написаны полностью на С#, в основе тестера лежит архитектура S#. Перед запуском оптимизации настраиваем следующие параметры:

  • диапазоны параметров стратегии, а также шаг этих параметров,
  • на каких таймфреймах будем исследовать (можно выбрать сразу несколько),
  • диапазон истории,
  • инструмент,
  • путь для сохранения результатов оптимизации,
  • параметры расчёта комиссии и др.
  • критерий останова исследования в процентах от начального размера матрицы,
  • на сколько % мы будем уменьшать область выборки после каждой итерации. От этого зависит общее число итераций и точность оптимизации.
  • сколько случайных стратегий мы будем исследовать из области выборки на каждой итерации,
  • сколько стратегий будем отправлять в тестеры (возможно несколько заходов). Зависит от мощности ПК, объема оперативной памяти, глубины исторических данных.
  • вокруг скольких худших стратегий будем удалять локальные области,
  • сколько лучших стратегий будем исследовать на сходимость,
  • размер локальной области вокруг лучших стратегий, которые будем исследовать дополнительно.

Можно запустить вместо оптимизации «случайный перебор». Здесь тестирутся стратегии не по сетке, а в случайном порядке. То есть мы можем в любой момент остановить исследование и оценить результат. Конечно, чем больше стратегий протестируем, тем яснее получим представление о пространстве.

image

Допустим мы исследовали пространство и примерно представляем сколько там максимумов. И что нам это дает? Пока почти ничего…
Нам нужно исследовать эти максимумы, разобраться носят они случайный характер или системный. Для этого в тестере-оптимизаторе предусмотрел возможность выбрать лучшие стратегии и дополнительно, более подробно исследовать области вокруг них. Исследовать те стратегии, которые мы пропустили при оптимизации. Теперь об экстремумах пространства мы знаем почти все! Полученные данные можно исследовать дальше на кластеризацию, пере оптимизацию и прочее. Но это уже другая история!

P.S. Не много о себе. Я инженер, проектирую промышленные системы автоматизации. С трейдингом познакомился около года назад, сначала торговал вручную, потом понял, что это не мое. Лучше торговать по чётким правилам и автоматизировано (алготрейдинг). Первый алгоритм написал на скриптовом языке терминала Quik, но этот язык (qpile) оказался невероятно убогим. Меньше полугода назад прошел обучение в StockSharp, тогда я впервые открыл Visual Studio и написал свои первые строчки на С#. StockSharp — это бесплатная платформа/библиотека для создания торговых роботов на языке C#. На ее базе построена архитектура моих приложений.

Всем удачи! С уважением, Алексей.

Автор: Bond_algotrade

Источник



https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js