elektroyar
elektroyar личный блог
30 октября 2019, 21:37

Тестирование стратегий для бинарных опционов на истории. Библиотека для С++ и пример с "граалем".

В данной статье будет рассмотрен только технический аспект тестирования стратегий для бинарных опционов. Если вы считаете, что бинарные опционы не предсказуемы, или что брокеры «разводят» трейдеров, то данный пост будет не об этом и просьба не обращать на него внимания. Здесь будет рассмотрен только технический аспект для тех, кто хочет сам тестировать стратегии и проводить эксперименты на БО. Впрочем, используемый код можно адаптировать при желании и под форекс.

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

Получаем исторические данные


Раннее я уже разрабатывал библиотеки для упрощения разработки алгоритмов торговли. В данной статье мы будем использовать библиотеку для хранения и удобного доступа к сжатым данным xquotes_history https://github.com/NewYaroslav/xquotes_history , при помощи нее можно хранить не только котировки, хотя основное предназначение именно для этого. Библиотека для хранения котировок использует библиотеку сжатия zstd, которая после предварительного обучения на данных может сжимать эффективнее и быстрее, чем например gzip. Для сравнения, котировки из файлов csv получилось упаковать в файлы, где они занимают в 12 раз меньше места. При этом можно сразу получать доступ к необходимым данным. 

Сначала инициализируем класс, укажем путь к файлу с котировками. Котировки можно скачать здесь: https://github.com/NewYaroslav/finam_history_quotes
std::string path = "..//..//..//fxcm_history_quotes//storage//";
std::string symbol_path = path + "EURUSD.qhs4";
xquotes_history::QuotesHistory<> iQuotesHistory(symbol_path, xquotes_common::PRICE_OHLC, xquotes_common::USE_COMPRESSION);
Можно получить минимальную и максимальную метку времени начала дня для котировок, которые содержатся в файле.
/* найдем минимальную и максимальную даты (время GMT)
 * начнем торговать с 2012 года
 */
xtime::timestamp_t timestamp_min, timestamp_max;
iQuotesHistory.get_min_max_day_timestamp(timestamp_min, timestamp_max);
timestamp_min = std::max(timestamp_min, xtime::get_timestamp(1,1,2012));
std::cout << "date: " << xtime::get_str_date(timestamp_min) << " - " << xtime::get_str_date(timestamp_max) << std::endl;
Далее можно пройтись по всем меткам времени с шагом в одну минуту и получать значения цен.
/* начинаем торговать на минутном таймфрейме */
for(xtime::timestamp_t t = timestamp_min; t <= timestamp_max; t += xtime::SECONDS_IN_MINUTE) { 
    xquotes_common::Candle candle; // свеча
    /* получаем минутную свечу */
    int err_candle = iQuotesHistory.get_candle(candle, t);
    if(err_candle != xquotes_common::OK) {
        /* если данные по свече отсутствуют, пропустим шаг */
        continue;
    }
}
Проверить бинарный опцион не сложно, класс QuotesHistory содержит метод check_binary_option который может проверить исход сделки.
int state_bo = xquotes_common::NEUTRAL; // состояние сделки, может иметь три состояния (WIN, LOSS, NEUTRAL)
/* получаем результат бинарного опциона. С этим надо быть поаккуратнее, т.к. можно "подсмотреть в будущее" */
int err_bo = iQuotesHistory.check_binary_option(state_bo, xquotes_common::SELL, duration, t);
if(err_bo != xquotes_common::OK) {}; // если данные по бинарному опциону не доступны, решаем что делать


Обрабатываем данные


Для работы с котировками нужны индикаторы. Можно использовать библиотеку https://www.ta-lib.org/ , лично мне не нужно было большое разнообразие индикаторов, поэтому я написал свою библиотеку для С++ https://github.com/NewYaroslav/xtechnical_analysis

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

Для примера мы будем использовать Bollinger Bands, а торговлю будем вести с 0:00 до 3:00 часов ночи по МСК. В это время брокеры бинарных опционов либо вовсе запрещают торговать, либо значительно уменьшают проценты выплат. Так как цена не предсказуема, то нам нет смысла волноваться, это видимо просто «технический перерыв»… Или все же нет? Скоро узнаем.

Создать индикатор не сложно:
/* инициализируем Боллинджер с периодом 20 и множителем 2 */
xtechnical_indicators::BollingerBands<double> iBB(20,2);

Далее просто вызываем метод update чтобы обновить состояние индикатора и заодно получить его рассчитанные значения:
/* получаем полосы Боллинджера и его среднюю линию, используя цену закрытия свечи */
double tl, ml, bl;
int err_bb = iBB.update(candle.close, tl, ml, bl);
if(err_bb != xtechnical_common::OK) continue; // если индикатор еще не готов нам дать результат, пропускаем ход
Далее уже ничего не мешаем нам создать простенькую торговую систему

/* настраиваем параметры торговли */
const uint32_t duration = 180; // Время экспирации опицона 3 минуты
const double broker_payout = 0.8; // Выплата брокера
const double risk = 0.01; // Ставка в процентах от депозита, ставим 1%

/* Фильтруем по времени, мы будем торговать только с 0:00 до 3:00 часов по МСК
* Так как у нас время в GMT, не забываем отнять 3 часа.
* В это время мы будем торговать, так как почему то все брокеры делают "технический перерыв" в это время
* Вдруг брокеры нам мешают заработать свои миллионы? Ща выясним!
*/
if(xtime::get_hour_day(t) < 21) continue;

/* Проверяем сигнал простой ТС,
 * если цена закрытяи свечи выша за линию, открываем сделку в противоположную сторону
 */

int state_bo = xquotes_common::NEUTRAL; // состояние сделки, может иметь три состояния (WIN, LOSS, NEUTRAL)
if(candle.close > tl) {
    /* получаем результат бинарного опциона. С этим надо быть поаккуратнее, т.к. можно "подсмотреть в будущее" 
     */
    int err_bo = iQuotesHistory.check_binary_option(state_bo, xquotes_common::SELL, duration, t);
    if(err_bo != xquotes_common::OK) continue; // если данные по бинарному опциону не доступны, пропусакем ход

    /* добавляем сделку в тестер */
    iStandardTester.add_deal(state_bo, duration,  broker_payout, risk);
}
if(candle.close < bl) {
    /* получаем результат бинарного опциона. С этим надо быть поаккуратнее, т.к. можно "подсмотреть в будущее" 
    */
    int err_bo = iQuotesHistory.check_binary_option(state_bo, xquotes_common::BUY, duration, t);
    if(err_bo != xquotes_common::OK) continue; // если данные по бинарному опциону не доступны, пропусакем ход

    /* добавляем сделку в тестер */
    iStandardTester.add_deal(state_bo, duration,  broker_payout, risk);
}

Заметкаxtime — пространство имен библиотеки для работы с меткой времени. https://github.com/NewYaroslav/xtime_cpp


Тестер стратегии


В коде вверху есть тестер стратегии iStandardTester, он может обрабатывать бинарные опционы с разными экспирациями и процентами выплат. Чтобы он показывал во время теста реалистичные значения, тестер имитирует «задержку времени» для исхода бинарного опциона. Поэтому, если пытаться получить винрейт или значение депозита, мы не будем «подсматривать в будущее». Задержка реализуется при помощи методов update_delay и update_timestamp на выбор, в зависимости от того, удобно ли нам использовать метки времени или мы проводим тест без их использования.

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

Также я решил, что именно тестер пусть и содержит функции для небольших расчетов, такие как ставка по критерию Келли и пр.

Исходный код тестера расположен здесь: https://github.com/NewYaroslav/easy_bo_tester


Результаты теста


Скажу сразу, EURUSD не смог стать «граалем», после 2012 года он вдруг начал показывать плохие результаты. Поэтому было решение выбрать AUDCAD, просто потому что  это не EURUSD. Смотрим результат с 2013 года по сентябрь 2019:


график роста депозита

Какой красивый график. Теперь смотрим статистические значения:
статистика тестера

Итог:
  • винрейт: 59%
  • сделок: 27541
  • депозит увеличился в: 48552400 раз
Граалль!!!.. Но, увы, нет. В это время торговать нельзя. Исходники программы для тестирования: https://github.com/NewYaroslav/easy_bo_tester , конкретно сам код расположен здесь. Тестируйте на здоровье)

Полезные ссылки:

Почти все рассматриваемые библиотеки почти полностью header-only (если не считать зависимостей).
Спасибо за внимание! 
5 Комментариев
  • Savin
    30 октября 2019, 22:38
    Ну наконец то в топах про бинарики на даунлабе, пора видимо…
  • Бил Денбро
    31 октября 2019, 08:14
    бинарные опционы
  • Cat_in_heaven
    31 октября 2019, 18:51
    ТС, если замените «бинарные опционы» на «торговля опционами» — не будет снисходительных смешков, хотя смысл и результат «изысканий» не поменяется...
  • Astronomer
    31 октября 2019, 19:59
    Отличная статья!
  • Александр
    31 октября 2019, 21:29
    Грааль в продаже опционов) ВЕбинарных...)

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн