![](https://habrastorage.org/webt/yo/ur/rc/yourrczo-apctuvumq-jbnt3k-4.png)
Редакция портала QuantStart написала материал о том, что следует знать при начале разработки собственной системы для тестирования торговых стратегий. Некоторые из затронутых в статье вопросов мы разбирали в блоге ранее, поэтому на этот раз подготовили адаптированный пересказ тезисов о том, с какими проблемами сталкиваются разработчики, в чем разница между бэктестерами разных типов, и каковы их плюсы и минусы.
Что такое бэктестер
Бэктест — это применение правил торговой стратегии к набору исторических данных о ценах финансовых инструментов. Суть подхода в том, что если мы разработаем механизм определения момента для входа и выхода из позиции (покупка/продажа), например, акций из некоторого портфолио, и применим получившиеся правила к историческим данным, то это даст представление о продуктивности торговой стратегии «в прошлом».
Однажды кто-то сказал о том, что «все модели ошибочны, но некоторые полезны». Эта фраза отлично подходит к бэктестингу. Системы для исторического тестирования финансовых стратегий помогают определить, стоит ли вообще применять к реальной торговле имеющийся набор правил. Если мы будем знать, как стратегия могла повести себя в прошлом, то это поможет отфильтровать плохие стратегии без необходимости реальных финансовых потерь.
Проблем в том, что результат бэктестинга не имеет ничего общего с результатами реальной торговли на бирже. Это всего лишь модель реальности. Модель, зачастую содержащая множество допущений.
Существуют два основных типа бэктестеров — циклические (for-loop) и ориентированные на события (event-driven).
При разработке таких систем всегда встает необходимость компромисса между точностью и сложностью реализации. Эти два типа бэктестеров представляют весь спектр вариантов такого компромисса.
Сложности бэктестинга
Тестирование на исторических данных несет в себе множество сложностей. Все они связаны с тем фактом, что весь процесс представляет собой лишь моделирование реальности. Вот лишь некоторые из них:
- Тестирование на выборке (in-sample testing) — проблема возникает при использовании одних и тех же данных для тренировки торговых моделей и для их дальнейшего тестирования. В таком случае показанная продуктивность значительно обесценивается — ведь результат достигнут на заранее известных системе данных. В реальности данные будут чаще всего значительно отличаться от тренировочных. По сути, это разновидность переобучения.
- Ошибка выжившего — для фондовых индексов (например, S&P500) характерен процесс листинга и делистинга, когда в них появляются или из них исключаются определенные акции и финансовые инструменты. Если эти изменения в ходе бэктестинга не учитываются, то удачной может быть признана стратегия, которая не учитывает акции компаний, которые были исключены из индекса из-за низкой капитализации. Чтобы избежать таких проблем при запуске бэктестов на длительных временных отрезках нужно использовать данные, не подверженные ошибке выжившего.
- Ошибки прогнозов (look-ahead bias) — на результат бэктеста могут повлиять и данные из будущего. К примеру, рассмотрим случай, когда на определенном временном интервале вычисляется показатель линейной регрессии. Если этот показатель затем используется в той же выборке, то получится, что в нее проникли данные из будущего, а значит получившаяся продуктивность стратегии в значительной степени обесценивается. Решить эту проблему помогают событийно-ориентированные бэктестеры.
- Изменение рыночного режима — параметры финансового рынка не стационарны. Это значит, что процессы, выливающиеся в движения цен акций, не опираются на параметры, которые неизменны во времени. Данный факт усложняет обобщение параметризованных моделей (многие торговые стратегии – это частные случаи таких стратегий), что приводит к тому, что результативность стратегии на исторических данных оказывается значительно лучше чем при реальной торговле.
- Транзакционные издержки — многие циклические бэктестеры не учитывают даже самую базовую информацию о транзакционных издержках, таких как различные комиссии и сборы. Часто этим грешат авторы научных работ, которые предпочитают не опускаться до таких мелочей. Найти стратегию, которая очень прибыльна в идеальных условиях отсутствия издержек, очень легко. Проблема в том, что при торговле в реальных условиях такие стратегии могут оказаться глубоко убыточными. Крайне важно учитывать спред, рыночную ситуацию, различные сборы, проскальзывание (при сделках с высоковолатильными активами реальная цена сделки может немного отличаться от той, которая предполагалась при выставлении заявки – как в выгодную сторону, так и в минус).
Также существуют и другие вопросы, которые не так часто обсуждаются, но тем не менее крайне важны для создания качественно работающего бэктестера. Среди них:
- OHLC-данные — это информация о цене открытия, наивысшей цене финансового инструмента в течение торговой сессии, ее наименьшее значение и цена закрытия торгового периода (open-high-low-close chart, OHLC). Обычно она импортируется из источников вроде Yahoo Finance. В таком случае это может быть объединение различных фидов данных. Это значит, что получить экстремальные значения (включая цены High и Low) трейдинговой системе, работающей в режиме реального времени, будет трудно. Это тоже нужно учитывать в торговой модели.
- Емкостные ограничения — при бэктестинге велик соблазн использовать бесконечный запас денег. В реальности же объем средств, доступных для торговли, всегда ограничен (как и возможный объем заемных средств для маржинальной торговли). Важно также не забывать о среднем лимите дневного объема торговли (Average Daily Volume, ADV), особенно для низколиквидных акций, когда велик риск, что операции торговой системы приведут к реальному изменению цены. Такое влияние на рынок также следует учесть.
- Выбор бенчмарка — необходимо ответить на вопрос о том, верно ли выбран бенчмарк, с которым будет сравниваться тестируемая стратегия. К примеру, если вы торгуете товарными фьючерсами, которые нейтральны к индексу S&P500, стоит ли использовать этот индекс в качестве бенчмарка? Вполне вероятно, что корзина других товарных фондов окажется более корректным выбором.
- Крепкость (robustness) — если изменить время начала работы стратегии в ходе бэктеста, насколько сильно это влияет на результат? Для долгосрочных стратегий время старта их работы не должно серьезно сказываться на продуктивности — неважно, стартовал бэктест в понедельник или в четверг. Если же она слишком чувствительна к начальным условиям, это значит, что предсказать ее возможную продуктивность при старте реальной торговли нет никакой возможности.
- Переобучение и дисперсия — мы уже коснулись вопросов переобучения выше, но это более широкая проблема, присущая всем контролируемым (supervised) методам машинного обучения. Решить эту проблему можно только с помощью тщательного использования техник кросс-валидации. И даже в этом случае следует быть крайне осторожным при адаптации стратегий к шуму в тестовых наборах данных.
- Психологическая толерантность — психологию часто игнорируют при создании автоматизированных торговых систем, ведь ее влияние как раз и должно минимизироваться алгоритмами. Однако люди остаются людьми, даже когда торгуют не руками, а с помощью роботов. В итоге это может сказываться на результатах. К примеру, если в ходе бэктеста просадка депозита на 50% может казаться допустимым риском, то в реальности потеря половины стоимости активов оказывается куда более травмирующим опытом. Удержаться от незапланированных действий в такой ситуации нелегко.
На этом с проблемами тестирования на истории все, теперь перейдем к описанию самих систем для таких тестов.
Два вида бэктестеров
Сначала рассмотрим цикличные системы. Это наиболее простой тип бэктестера, которые чаще всего описываются в различных блог-постах по теме финансов.
Цикличные бэктестеры
Работают они так — система просто итерационно проходит каждый торговый день (или бар OHLC), производит вычисления, связанные с ценой нужного актива (например, вычисляет скользящие средние цены закрытия), а затем совершает соответствующую операцию (вход в длинную или короткую позицию). Далее итерации продолжаются. В процессе сохраняется информация о результативности работы, чтобы по итогу построить график (equity curve).
Вот так выглядит псевдокод подобного алгоритма:
for each trading bar:
do_something_with_prices();
buy_sell_or_hold_something();
next_bar();PythonCopy
Как видно, система крайне проста, что делает такие бэктестеры отличным вариантом для получения первых прикидок по поводу перспективности торговой системы.
Плюсы
Цикличный бэктестер очень просто реализовать с применением практически любого языка программирования, и сделать это быстро. Это полезно, когда требуется протестировать влияние множества различных параметров.
Минусы
Главный минус — нереалистичность получаемых результатов. Часто в циклических бэктестерах нет даже базовой функциональности для учета транзакционных издержек, ее приходится реализовывать отдельно. Также обычно используются только MARKET-приказы.
Также возможность повторного использования кода для тестовой и продуктивной системы минимальна, так что писать его придется заново. Это увеличивает вероятность появления программных ошибок.
Цикличные бэктестеры подвержены появлению ошибок прогнозирования. Использовать их следует исключительно как инструмент фильтрации для отбраковывания явно неудачных стратегий. При этом важно сохранять крайне скептической отношение к стратегиям, показавшим хорошие результаты. Важно помнить, что в реальной жизни стратегии крайне редко показывают себя лучше, чем в ходе бэктеста.
Событийно-ориентированные системы
Системы такого типа находятся на противоположной стороне спектра. Они куда ближе к реальности. Обычно они работают в огромных циклах while, в ходе которых в специальной «очереди событий» постоянно ищутся «события», включающие:
- тики — появление новых рыночных данных;
- сигнальные события – появление торговых сигналов;
- событие приказа – приказ на совершение операции готов к отправке в систему брокера;
- событие сделки – поступление от брокера информации об исполнении заявки.
Когда определенное событие распознано, оно передается соответствующему модулю в инфраструктуре торговой системы для дальнейшей обработки и потенциально генерирует новые события, которые снова попадают в очередь.
Псевдокод такого бэктестера выглядит следующим образом:
while event_queue_isnt_empty():
event = get_latest_event_from_queue();
if event.type == "tick":
strategy.calculate_trading_signals(event);
else if event.type == "signal":
portfolio.handle_signal(event);
else if event.type == "order":
portfolio.handle_order(event);
else if event.type == "fill":
portfolio.handle_fill(event)
sleep(600); # Sleep for, say, 10 minsPythonCopy
Как видно система крайне зависима от модуля обработки портфолио – это настоящее сердце в событийно-ориентированных системах.
Плюсы
У бэктестеров такого типа есть много плюсов:
- Снижение вероятности ошибок прогнозирования — благодаря структуре, предполагающей поэтапную передачу сообщений, в событийно-ориентированные системах реже встречаются ошибки прогнозирования, по крайне мере на торговом уровне. Однако вероятность их появления все равно сохраняется, так как ошибки могут содержаться в самой модели.
- Возможность повторного использования кода – для использования стратегии в реальной торговле необходимо лишь заменить модуль обработки данных и движок исполнения заявок. Описание стратегии, модуль риск-менеджмента и управления позициями, код для оценки продуктивности системы — все это можно использовать без изменений. Это снижает вероятность возникновения новых багов.
- Уровень портфолио — в событийно-ориентированных стратегиях проще думать на уровне портфолио. В итоге это облегчает внесение изменений в стратегию и разработку методов хеджирования.
- «Правильный» риск-менеджмент — в таких системах проще «модуляризовать» риск-менеджмент. Трейдер может без проблем использовать методики вроде Критерия Келли, а также включать различные оповещения, устанавливать лимиты (например, на волатильность).
- Удаленное развертывание и мониторинг — модульный принцип написания кода упрощает его развертывание в облаке или по схеме биржевой колокации.
Минусы
Плюсы событийно-ориентированных систем понятны, но есть и определенные недостатки. Среди них:
- Сложный код — разработка полностью покрытой тестами системы займет недели и месяцы работы в режиме full-time.
- Объектная ориентация — модульный дизайн системы требует объектно-ориентированного подхода к программированию. А значит, нужен язык, который поддерживает эти принципы. Юнит-тестирование от этого легче не станет.
- Высокий порог входа — новичку в программировании не получится создать такую систему. Потребуется достаточно существенный инженерный опыт, который позволит разобраться с написанием кода, реализацией логирования, проведения юнит-тестов, внедрения контроля версий и практик continuous integration.
- Невысокая скорость — подход, при котором сообщения внутри системы последовательно передаются внутри разных ее уровней, замедляет скорость исполнения заявок по сравнению с векторизированным цикличным подходом. Вычисление различных комбинаций параметров может требовать серьезных временных затрат.
Другие материалы по теме финансов и фондового рынка от ITI Capital:
- Аналитика и обзоры рынка
- Назад в будущее: проверка работоспособности торгового робота с помощью исторических данных
- Событийно-ориентированный бэктестинг на Python шаг за шагом (Часть 1, Часть 2, Часть 3, Часть 4, Часть 5)
asdfgh
Нужно знать, что на всей этой хне заработает только брокер.