Pull to refresh

Тестирование торговой системы на PHP

Reading time 6 min
Views 11K

Вступление



Наверное каждый, кто сталкивался с торговлей акциями, валютами или каким-либо другим биржевым инструментом часто встречал совет о необходимости построения и тестирования своей торговой системы.

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





При этом я решил совместить приятное с полезным, и реализовать тестирование торговой стратегии на PHP. Почему я не стал использовать готовые программные продукты наподобие TSLab, WealthLab и т.п.? Во-первых, сама задача реализации торгового робота довольно интересна в качестве саморазвития. Во-вторых, большинство продуктов для тестирования торговых стратегий платные, а гарантировать, что бесплатные аналоги в скором времени не изменят свою политику распространения никто не может. Также у меня нет уверенности, что возможностей сторонних продуктов хватит для моих будущих торговых стратегий. В-третьих, PHP — веб-ориентированный язык, и свою разработку можно потом будет использовать в качестве основного модуля для веб-сервиса. Уже этих трех причин мне было достаточно для начала работ над tradeSystem.

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

Формализация стратегии



Наша стратегия будет по сути «hello world» стратегией. Она будет основываться на MACD гистограмме.



Считается, что когда график этого индикатора пересекает значение «0» сверху или снизу, это сигнализирует о смене тренда. Если пересечение происходит сверху вниз — поступает сигнал на продажу, если снизу вверх — сигнал на покупку.

Наша стратегия будет работать на часовом графике и в конце каждого часа будет проверять, не появился ли сигнал на продажу или покупку. Сигналом будет служить разница в знаках между текущим и предыдущим значеним индикатора, либо равенство нулю предыдущего значения индикатора. В случае наличия сигнала и отсутствия бумаги в портфеле, мы будем открывать позицию на все денежные средства, которые имеем в наличии. При открытии позиции мы будем выставлять stop loss приказ на уровень 1% от цены открытия позиции и take profit приказ на уровень 2% от цены открытия, со смещением от максимума на 0.4 пункта.

Так же нам бы хотелось бы учитывать комиссию, которую будет брать брокер в начале каждого дня.

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

Реализация торговой системы и скрипта тестирования



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

Непосредственно для проведения тестов нам потребуется три составляющих: данные котировок, реализация торговой системы, и скрипт, который организует среду тестирования и вывод информации о работе системы.

Что касается данных котировок, то я воспользовался экспортом котировок с сайта брокера Финам. Я решил протестировать систему на котировках Сбербанка за 2011 год. Несмотря на то что торговая стратегия принимает решения на часовом графике, нам надо будет на вход подать данные минутного графика. Чтобы сберечь сайт брокера, я положил файл котировок в директорию robots/testing/input проекта под именем: SBER_110101_111231_1min.txt.gz. Просто распакуйте этот файл, желательно в ту же директорию где он лежит.

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

Для нашей стратегии нам понадобится сам индикатор, на основе которого она работает, бумага, которую мы тестируем и счетчик серий выигрышей и проигрышей. Далее у стратегии должен быть метод handleBar, аргументом которого является свеча. Для удобства я выделил еще два метода: canOpenPosition и openPosition, которые отвечают непосредственно за проверку возможности открытия позиции и непосредственно об открытии позиции.
В функции simpleHandle мы проверяем настало ли время принимать решение, если настало — проверяем появился ли сигнал к покупке или продаже. Если появился — открываем позицию. В функции openPosition мы непосредственно открываем позицию, выставляем stop loss и take profit приказы. Так как эти приказы тоже являются по сути стратегией сопровождения позиции, мы вклиниваемся в цепочку вызова стратегий методом insertUp. Вот собственно и все, осталось только написать скрипт, который будет считывать данные котировок, и подавать их на вход стратегии.

Скрипт тестирования



Листинг скрипта можно увидеть в файле MACDHistogrammReverse.php. Код опять же должен быть достаточно понятным, я остановлюсь на основных моментах.

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

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

Расчет новых значений индикаторов мы поручим классу Chart. Для этого создадим его и добавим все наши индикаторы в него. Теперь при вызове handleBar Chart будет вызывать у всех индикаторов рассчет нового значения индикатора. Некоторые вопросы могут возникнуть относительно надобности EndStrategy и BeginStrategy, в которые заворачивается наша стратегия. Основное назначение этих стратегий — обеспечить корректную обработку вновь генерируемых стратегий стратегиями, которые находятся в самой цепочке. Например при методе insertUp который использует наша стратегия стратегии TakeProfit и StopLoss вставляются выше по цепочке, но после BeginStrategy. При отсутствии BeginStrategy может возникнуть ситуация, когда мы будем вызывать handleBar нашей стратегии, а стратегии выше по цепочке вообще бы остались за бортом тестирования.

Далее все совсем просто, мы создаем нашу стратегию, создаем объект который будет вычитать комиссию, начинаем считывать FinamBarReader'ом входные данные и подавать их на вход комиссии, графику и стратегии. Также не забываем обновлять текущее время в DateTimeManager, чтобы наша стратегия корректно обрабатывала момент времени, в который надо принимать решение.
В конце, если у нас есть открытая позиция — закрываем ее принудительно по цене последней сделки и финальным аккордом выводим информацию, которая нас интересует.

Тестируем



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

Итак, тестируем:

php robots/testing/MACDHistogrammReverse.php robots/testing/input/SBER_110101_111231_1min.txt > /tmp/result.txt

Смотрим файл result.txt и видим, что по результатам тестирования наша стратегия показала четыре выигрыша подряд, шесть проигрышей, и в конце концов у нас на счете осталось 9143 рубля, при начальном балансе 10000 рублей. Всего торговая система провела операций на сумму 2 миллиона рублей.
Для наглядности я составил график распределения баланса во времени.



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

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

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

Tags:
Hubs:
+35
Comments 65
Comments Comments 65

Articles