Хабрахабр

Алгоритмы оптимизации торгового робота: эффективный способ наторговать миллион задним числом

тизер

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

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

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

У торгового алгоритма 4 настраиваемых параметра:

  1. Mf период “быстрой” скользящей средней,
  2. Ms период “медленной” скользящей средней
  3. T — TakeProfit, целевой уровень прибыль по каждой отдельной сделке,
  4. S — StopLoss, целевой уровень убытка по каждой отдельной сделке.

Каждому из параметров мы задаем диапазон и фиксированный шаг изменения, всего по 20 значений для каждого из параметров.

Таким образом, мы можем искать максимум прибыли (P) для одного параметра на одном массиве входных данных:

  1. варьируя один параметр, например P = f(Ms), произведя до 20 бэктестов,
  2. варьируя два параметра, например P = f(Ms, T), произведя до 20 * 20 = 400 бэктестов,
  3. варьируя три параметра, например P = f(Mf, Ms, T), произведя до 20 * 20 * 20 = 8 000 бэктестов,
  4. варьируя каждый из параметров, P = f(Mf, Ms, T, S) и произведя до 20^4 = 160 000 бэктестов.

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

подробно о торговле и торговых роботах

Трейдинг на фондовой бирже, беттинг в дилинговом центре Forex, шальные ставки на “бинарные опционы”, спекуляции криптовалютами — своего рода “диагноз”, с несколькими возможными вариантами развития “болезни”. Весьма распространен сценарий, когда игрок, поигравшись в “интуицию”, приходит к автоматической торговле. Не поймите меня неправильно, я не не хочу так расставить акценты, чтобы “технологичные и математически” строгие торговые роботы были противопоставлены наивной и беспомощной “ручной” торговле. К примеру, сам я убежден, что любые мои попытки извлечь из эффективного рынка (читай из любого прозрачного и ликвидного рынка) прибыль путем спекуляций, неважно, дискреционных или полностью автоматизированных, априори обречены на поражение. Если, разве что, не допустить фактор случайного везения.

Одни самостоятельно программируют торговых роботов, другие идут еще дальше и создают собственные платформы для написания, отладки и бэктестинга торговых стратегий, третьи скачивают / покупают готового электронного “эксперта”. Тем не менее, трейдинг, и, в частности, алго(ритмический)трейдинг — популярное хобби для многих. Чтобы не быть голословным, дальнейшие свои наблюдения я привожу на примере простой торговой стратегии: Но даже те, кто не пишут торговые алгоритмы самостоятельно, должны иметь представление о том, как обращаться с этим “черным ящиком”, чтобы извлечь из него прибыль в соответствие с авторской задумкой.

Простой торговый робот

Торговый робот анализирует динамику стоимости золота, в долларах за тройскую унцию, и принимает решение о “покупке” или “продаже” некоторого количества золота. Для простоты примем, что робот всегда торгует одной тройской унцией.

00 USD. К примеру, на момент покупки, стоимость тройской унции золота составляла 1075. 00 USD. На момент последующей продажи (закрытии сделки) цена выросла до 1079. Прибыль по этой сделке составила 4 USD.

00 USD, а впоследствии завершил (закрыл) сделку, “выкупив” золото обратно по цене 1079 USD, прибыль по сделке будет отрицательной величиной — минус 4 USD. Если же робот “продал” золото по 1075. Брокер / дилинговый центр позволяет трейдеру “покупать” и “продавать” актив тем или иным способом, зарабатывая (или, что чаще, теряя), на разнице курсов. Собственно, для нас не имеет значения, каким образом робот продает золото, которым не располагает, чтобы потом “выкупить” его обратно.

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

Более подробно о торговом роботе можно почитать в следующем спойлере:

алгоритм торговли робота

Две тонкие ломаные линии, красная и синяя — усредненные значения цены с периодами усреднения 5 и 10 соответственно. Черная (толстая) кривая на графике — часовые измерения цены XAUUSD. Например, для того, чтобы рассчитать ординату последней (правой) точки красной кривой, я взял среднее из последних 5 значений цены. Иначе говоря, скользящие средние (Moving Average, MA) с периодами 5, 10. Таким образом, каждая скользящая средняя не только “сглажена” относительно ценовой кривой, но и запаздывает относительно нее на половину своего периода.

Правило открытия сделки

Роботу определено простое правило принятие решения о покупке / продаже:
— как только скользящая средняя с коротким периодом (“быстрая” MA) пересекает скользящую среднюю с длинным периодом (“медленную” MA) снизу вверх, робот покупает актив (золото).

На рисунке выше робот совершит 5 сделок: 3 продажи в отметках времени 7, 31 и 50 и две покупки (отметки 16 и 36). Как только “быстрая” MA пересекает “медленную” MA сверху вниз, робот продает актив.

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

Правило закрытия сделки

Робот закрывает сделку, как только:

  • прибыль по сделке превышает указанное в процентах пороговое значение — TakeProfit,
  • либо убыток по сделке, в процентах, превышает соответствующее значение — StopLoss.

Предположим, StopLoss равен 0.2%.
Сделка — “продажа” золота по цене 1061.50.
Как только цена золота вырастет до значения 1061.50 + 1061.50 * 0.2% / 100% = 1063.12%, убыток по сделке, очевидно, будет равен 0.2% и робот закроет сделку автоматически.

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

В то же время, он на 100% соответствует предъявляемым к нему требованиям: Да, робот предельно прост.

  1. алгоритм детерминирован: каждый раз, имитируя работу робота на одних и тех же ценовых данных, мы будем получать один и тот же результат,
  2. имеет достаточное количество настраиваемых параметров, а конкретно: период “быстрой” и период “медленной” скользящей средней (натуральные числа), TakeProfit и StopLoss — положительные вещественные числа,
  3. изменение каждого из 4 параметров, в общем случае, оказывает нелинейное влияние на характеристики торговли робота, в частности, на его доходность,
  4. доходность робота на истории цен считается элементарным программным кодом, а сам расчет занимает доли секунды для вектора из тысячи котировок,
  5. наконец, что, правда, к делу не относится, робот, при всей своей простоте, в реальности проявит себя ничуть не хуже (пусть, вероятно, и не лучше), чем “Грааль”, продаваемый автором в интернет за нескромную сумму.

Быстрый поиск квазиоптимального набора входных параметров

На примере нашего простого робота видно, что полный перебор всех возможных векторов параметров настройки робота слишком затратен даже для 4-х варьируемых параметров. Очевидная альтернатива полному перебору — выбор векторов параметров по определенной стратегии. Рассматриваем лишь часть всех возможных комбинаций в поисках лучшей, в которой ЦФ приближается к наивысшему (либо наименьшему, в зависимости от того, какую ЦФ мы выбрали и какого результата мы добиваемся) значению.

Мы рассмотрим три алгоритма поиска квазиоптимального значения ЦФ. Для каждого алгоритма установим ограничение в 40 тестов (из 400 возможных комбинаций).
или случайный выбор M некоррелированных векторов из числа возможного количества наборов, равного N. Метод, вероятно, самый простой из возможных. Будем использовать его как отправную точку для последующего сравнения с остальными методами оптимизации.

Пример 1

график демонстрирует зависимости прибыли (P) нашего торгового робота, торгующего EURUSD, полученной на годовом отрезки истории часовых измерений цены, от значения параметра — период “медленной” скользящей средней (M). Все остальные параметры фиксированы и не подвергаются оптимизации.

27 в точке M = 12. ЦФ (прибыль) достигает максимума 0. Альтернатива — провести меньшее количество испытаний торгового робота со случайно выбранным значением параметра M на интервале [9, 20]. Чтобы гарантированно найти максимальное значение прибыли, нам потребуется провести 20 итераций тестирования. 18: К примеру, после 5 итераций (20% от общего количества испытаний, мы нашли квазиоптимальный вектор (вектор, очевидно, одномерный) параметров: M = 18 со значением ЦФ (M), равным 0.

Оставшиеся значения на графике от нашего алгоритма оптимизации скрыл “туман войны”.

Возможно, максимум ЦФ, равный 0. Оптимизация одного из четырех параметров нашего торгового робота, при фиксированных значениях остальных параметров, не позволяет нам увидеть всей картины. 27 — не лучшее значение показателя, если варьировать значение других параметров?

2… 0. Вот так изменяется зависимость прибыли от периода скользящей средней при различных значениях параметра TakeProfit на интервале [0. 8].

Метод Монте-Карло: оптимизация двух параметров

Зависимость прибыли торгового робота от двух параметров графически можно изобразить в виде поверхности:

По двум осям отложены значения параметров T (TakeProfit) и M (период скользящей средней), третья ось — значение прибыли.

Для нашего торгового робота, проведя 400 тестов на интервале данных в один год (~6000 часовых котировок евро к доллару США), получим поверхность вида:

или, на плоскости, где значения ЦФ (прибыль, P) представлены цветом:

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

Проведя 1 000 итераций поиска максимума ЦФ на исходных данных из примера выше, я получил следующую статистику: Насколько эффективен метод Монте-Карло в поиске максимума ЦФ?

  • среднее значение максимума ЦФ, найденное в ходе 1 000 итераций оптимизации (40 случайных векторов параметров [M, T] из 400 возможных комбинаций), составило 0.231 или 95.7% от глобального максимума ЦФ (0.279).

Очевидно, в сравнении методов параметрической оптимизации торговых роботов одна выборка — не показатель. Но пока достаточно и этой оценки. Переходим к следующему методу — метод градиентного спуска.
Формально, как следует из названия, метод применяется для поиска минимума ЦФ.
Согласно методу, мы выбираем стартовый точку с координатами [x0, y0, z0, …]. На примере оптимизации одного параметра это может быть случайно выбранная точка:

Далее следуют три простых шага: с координатами [5] и значением ЦФ, равным 148.

  1. проверить значения ЦФ в окрестностях текущей позиции (149 и 144)
  2. переместиться в точку с наименьшим значением ЦФ
  3. если такая отсутствует, локальный экстремум найден, алгоритм завершен

Если раньше мы вычисляли ЦФ в двух соседних точках $[x_], [x_{i+1}]$, теперь мы проверяем 4 точки:
Для оптимизации ЦФ как функции от двух параметров применяем все тот же алгоритм.

$[x_{i-1}, y_i], [x_{i+1},y_i], [x_i, y_{i-1}], [x_i, y_{i+1}].$

Если экстремумов несколько, поиск придется неоднократно повторять, чтобы повысить вероятность нахождения глобального экстремума: Метод, определенно, хорош, когда у ЦФ на тестируем пространстве всего один экстремум.

Чтобы оставаться в рамках определения алгоритма, мы можем считать, что осуществляем поиск минимума “минус ЦФ”. В нашем примере мы ищем максимум ЦФ. Все тот же пример, прибыль торгового робота как функция от периода скользящей средней и значения TakeProfit, одна итерация:

Пример нескольких итераций поиска экстремума ЦФ методом градиентного спуска, значение ЦФ рассчитано 40 раз (40 точек из 400 возможных): В данном случае был найден локальный экстремум, далекий от глобального максимума ЦФ.

В каждом случае проводится 40 испытаний (расчетов ЦФ). Теперь сравним эффективность поиска глобального максимума ЦФ (прибыли) на наших исходных данных алгоритмами Монте-Карло и градиентного спуска. Произведено по 1 000 итераций оптимизации каждым из методов:

Монте-Карло

градиентный спуск

среднее из полученных квазиоптимальное значение ЦФ

0.231

0.200

полученное значение от максимума ЦФ

95.7%

92.1%

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

Параметрическая устойчивость торгового алгоритма

Отыскание координат глобального максимума / минимума ЦФ зачастую не является целью оптимизации. Предположим, на графике встретилась “острая” вершина — глобальный максимум, значение ЦФ в окрестностях которого значительно ниже пикового значения:

Стоит нам незначительно изменить значение хотя бы одного из параметров — период скользящей средней и / или TakeProfit — доходность робота резко упадет (станет отрицательной). Допустим, мы выбрали настройки торгового робота, соответствующие найденному максимуму ЦФ. Применительно к реальной торговле, можно, как минимум, ожидать, что рынок, на котором предстоит торговать нашему роботу, будет заметно отличаться от того периода истории, на котором мы оптимизировали торговый алгоритм.

Следовательно, при выборе “оптимальных” настроек торгового робота, стоит получить представление о том, насколько робот чувствителен к изменениям настроек в окрестностях найденной точки экстремума ЦФ.

Метод Монте-Карло, скорее, бьет по площадям. Очевидно, метод градиентного спуска, как правило, дает нам значения ЦФ в окрестностях экстремума.

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

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

$P = f(m_i, t_i),$

а средневзвешенное значение, учитывающее соседние значения целевой функции, где вес обратно пропорционален расстоянию до соседнего значения (для оптимизации двух параметров x, y и целевой функции P):

$P'(x_i,y_i)=\frac{w_i \times P(x_i,y_i) + w_j \times P(x_j,y_j) + w_k \times P(x_k,y_k) + ...}{w_i + w_j + w_k + ...}$

$w_j = \sqrt{(x_j-x_i)^2 + (y_j-y_i)^2}$

= 1$" data-tex="display"/> <img src="https://habrastorage.org/getpro/habr/formulas/116/561/492/116561492d15cfbaf03f3db9f5fb49a9.svg" alt="$w_i + w_j + w_k + ...

Иначе говоря, при выборе квазиоптимального вектора параметров, алгоритм будет оценивать “сглаженную” целевую функцию:

было

стало

Пытаясь совместить достоинства обоих методов (Монте-Карло и метод градиентного спуска) я попробовал алгоритм, схожий с алгоритмом игры в “морской бой”:

  • сначала я наношу несколько “ударов” по всей площади
  • затем, в местах “попаданий” открываю массированный огонь.

Иначе говоря, первые N тестов проводятся на случайных некоррелированных векторах входных параметров. Из них отбираются M лучших результатов. В окрестностях этих испытаний (плюс — минут 0..L к каждой из координат) проводится еще K испытаний.

Для нашего примера (400 точек, 40 испытаний всего) имеем:

И снова сравним эффективность теперь уже 3-х алгоритмов оптимизации:

Монте-Карло

градиентный спуск

“морской бой”

Среднее значение найденного экстремума ЦФ в процентах от глобального значения.
40 тестов, 1 000 итераций оптимизации

95.7%

92.1%

97.0%

Конечно, сравнение проводились на одной конкретной выборке данных: один торговый алгоритм на одном временном ряду стоимости евро по отношению к доллару США. Результат обнадеживает. Однако статья вышла слишком объемной, и ГА придется отложить на следующую публикацию. Но, прежде чем сравнить алгоритмы на большем количестве выборок исходных данных, я собираюсь рассказать о еще одном, неожиданно (неоправданно?) популярном алгоритме оптимизации торговых стратегий — генетическом алгоритме (ГА) оптимизации.

Теги
Показать больше

Похожие статьи

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть