Секреты TSLab | Торговые роботы | События
11 Апр

Торговый робот “Краб”

Очередной интересный торговый робот, написанный нами на заказ уже в 2016 году. Он даже более интересен чем анонсированный ранее торговый робот “Хомяк”. Самой крутой особенностью данного робота является то, что вся торговая логика обсчитывается во внешней программе программе R, про которую я уже много раз упоминал в статьях и вебинарах. Ну а Краб он потому, что параллельно всегда тащит две однонаправленные позиции по опционам обоими клешнями :). Итак, давайте-ка посмотрим что из себя представляет торговый робот Краб.

Торговый робот “Краб”. Техническое задание.

Торговый робот реализован на заказ.

Итоговая цена разработки: 24000 рублей (фактически цена 35000 из-за вылезших проблем с TSLab, которые пришлось решать через поддержку. Но мы работаем в соответствии с нашим Коммерческим предложением поэтому для заказчика цена другая)

Находясь под впечатлением от простоты работы с кубиками в TSLab, я попробовал создать скрипт в графическом редакторе, однако очень быстро понял, что необходимые расчеты трудно реализовать через кубики. В R у меня уже был некоторый опыт работы, а с C# я был совсем не знаком, поэтому возникла идея заказать написание связки между TSLab’ом и R. Весь анализ, происходит в R, а TSLab только поставляет данные в R и исполнет сигналы.

Поиск по форумам привел к статье Родиона, посвященной именно этой идее, поэтому было принято решение обратиться с заказом к нему.

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

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

Заказчик

Главной изюминкой ТЗ было то, что в нем содержалось только описание механизмов открытия и закрытия позиций, а торговой логики не было. Всю логику заказчик планировал описать самостоятельно через скрипты R, а от нас требовалось только подготовить данные в TSLab, передать их в R и запустить механизм расчета, после чего отработать сигналы открытия позиций. Открытые позиции мы должны были вести по заданной схеме и закрывать их по этой же схеме. Благодаря этому, я могу свободно изложить все детали ТЗ, так как логика совершенно сокрыта даже для меня как разработчика :). Вообще, это идеальный способ нанять программистов, разбить логику на части где каждый знает только кусочек :).

Главной проблемой на старте разработки казалось то, что торговый робот Краб имел сложные механизмы открытия и закрытия позиций:

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

Все это давило на кучу больных точек TSLab которые нельзя обойти без TSLab API и некоторых особых техник работы с ним. При всем при этом  требовалось обеспечить:

  1. Отсутствие пропусков входов/выходов. Работать должно без вмешательства.
  2. Передавать в R цену инструмента на момент появления сигнала открытия позиции и делать для всех открытых позиций (появление сигнала и открытие позиции может быть шибко далеки друг от друга во времени из-за малой ликвидности).
  3. Обеспечить возможность одновременного открытия большого числа позиций, а не только одной пары.
  4. Обеспечить удержание позиции на время до нескольких дней. Перезапуск TSLab не должен ломать логику работы.
  5. Торговля на слаболиквидных опционах где плохо наливают.
  6. Обязательно работать лимитными ордерами.
  7. Защитить от возможных сбоев брокера/биржи.

Каждый пункт был проблемой, которую нужно было решать. Что же мы сделали?

Выбор модели скрипта

Лимитные заявки с удержанием их в стакане сразу диктовали необходимость применения прямого управления ордерами (ISecurityRt). И сначала была принята такая модель работы, но потом в результате осмысления ТЗ пришло понимание, что мы можем оставаться в рамках обычной модели, и это сразу упрощало работу и уменьшало стоимость разработки. Проблемной точкой могло стать открытие позиций, но так как цена открытия не изменялась в течение всего времени удержания ордеров открытия в рынке, то мы могли обойтись без прямого управления. TSLab 1.2 не дает менять цену лимитной заявки открытия позиции, он сразу будет формировать позицию даже если набрана только часть позиции.

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

  • однажды выставив ордер открытия позиции мы не можем передвигать его изменяя цену.
  • пропуски входов и выходов. Это нарушает работу алгоритма и требует вмешательства, а нам требуется бОльшая автономность.

Первый пункт нам особо не мешал, ведь исходная логика открытия не предусматривала перемещения ордеров открытия, и это нас спасло :).

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

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

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

Реализация

Устранение пропусков входов/выходов

Решается через отказ от торгового цикла. Изучаем ситуацию на последнем баре и выставляем сигналы. Не бегаем по истории, не изучаем каждый бар. Пропуск сигнала будет только если он создан для бара 10, но сделок для него нет и сейчас уже бар 11. То есть сигнал не реализовался в реале. А у нас не будет создаваться сигналов на барах в прошлом, только на lastBar+1.

Связка TSLab+R

Это делается с применением чудесной библиотеки RserveCLI. Некоторое время назад я провел целый вебинар по этому вопросу, и не будем повторяться. Просто посмотрите его и у вас не должно оставаться вопросов: “Как? Как ты это делаешь мужик?”

ССЫЛКА НА ВЕБИНАР

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

Одной из забот, была проблема собственно старта расчета в R. Нужен был красивый способ, чтобы заказчик мог менять логику без вмешательства в скрипт, отвечающий только за исполнение. Решением было написать функции в R, которые вызывать из TSLab с набором параметров. Вот пример функции болванки:

get.signal = function(mx_prices, mx_bid.ask, i_date, i_time, vn_signal.prices, vn_strikes) {
    # вставляю простую рандомную генерилку сигналов. Так как нет тела метода
    n_rnd = runif(1, 0, 1)
    return(n_rnd > 0.8)
}

Собственно вызов этой функции из TSLab не очень сложно реализовать:

// производим вычисления
var cmd = @"
           source('{0}');
           b_signal = get.signal(mx_prices, mx_bid.ask, i_date, i_time, vn_signal.prices, vn_strikes);
           vn_pos.size = get.pos.size(mx_depo, vn_current.price, d_pos.lim);
           ".Put(_rScriptPath.Replace(@"\", @"\\"));

cmd = cmd.Replace("\n", "");   // убираем переводы строки иначе не работает ничего
cmd = cmd.Replace("\r", "");   // надо очистить строку, иначе не работает
engine.VoidEval(cmd);

// забираем результаты
signal =  engine["b_signal"].AsBool;
posSize = engine["vn_pos.size"].AsInts;

Ничего сложного, ничего космического. Особенно когда уже все написано и работает :).

Сохранение текущего состояния скрипта

Это решает вопрос с пунктами 2-4 и без этого не удалось обойтись по трем причинам:

  1. Нужно удерживать сигнал открытия некоторое время (возможно в течение многих свечей)
  2. Нужно удерживать сигнал закрытия и менять его цену по истечению некоторого времени.
  3. Нужно помнить цену базового инструмента в момент появления сигнала открытия для каждой позиции которая еще не закрыта.

Ну и обязательно было обеспечить работу скрипта после рестарта TSLab. Тут не обойтись без нашей “суперразработки” под названием Менеджер Кэша которая использует кэш скриптов. Кэшировать все мы решили в память и на диск, поэтому накидали класс для хранения состояния скрипта такого вида:

    [DataContract, KnownType(typeof(CacheMark))]
    public class State : StateBaseXml
    {
        [DataMember] public Dictionary<string, Signal> PendingOpen;
        [DataMember] public Dictionary<string, Signal> PendingClose;
        [DataMember] public Dictionary<string, double> APrices;

        public State()
        {
            PendingOpen = new Dictionary<string, Signal>();
            PendingClose = new Dictionary<string, Signal>();
            APrices = new Dictionary<string, double>();
        }
    }

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

Данный класс хранит в себе информацию по активным сигналам открытия/закрытия и по ценам базового инструмента для них. То что доктор прописал. Менеджер Кэша позаботится о кэшировании и нам особо тут больше делать нечего. Главное следить за содержимым словарей и удалять лишнее.

 Удержание лимитных заявок в стакане

Пункты 5 и 6. Кто работал с TSLab знает что это проблема, ведь на каждом новом баре все стоящие заявки будут сняты если сигнал не будет еще раз создан. Мы решаем это путем кэширования информации об активных сигналах. Смотрим на список сигналов, принимаем решение что делать дальше. Возможные реакции сигнала открытия:

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

Возможные реакции на сигнал закрытия:

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

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

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

Торговый робот "Краб"Рисунок 1. Блоки кода на открытие позиций

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

Защита от сбоев и ошибочной генерации сигналов

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

Мы заложили в алгоритм простую логику, которая не дает скрипту ничего делать пока не пройдет заданное время. За это время успевают подгрузиться все рыночные данные и “дурных” заявок не случается. Код данного фрагмента тривиален и изображен ниже.

 var key = "__scriptStartDate__";
 var cache = ctx.LoadObject(key);
 if (cache == null)
     ctx.StoreObject(key, DateTime.UtcNow);

 cache = ctx.LoadObject(key);
 var startDate = (DateTime) cache;
 if (DateTime.UtcNow - startDate < TimeSpan.FromSeconds(startDelay))
 {
     ctx.Log("пауза перед стартом скрипта", new Color());
     return;
 }

Снова используется кэш скриптов. Очень полезная штуковина.

Защита алгоритма от кривых биржевых данных была реализована простыми проверками и не заслуживает внимания.

Нежданчики от TSLab

До конца еще проблема не прояснилась, но очевидно что она связана с вопросом синхронизации числа бар по 3 инструментам задействованным в работе. Так как 2 из них имеют очень низкую ликвидность, то число бар по инструментам часто отличается друг от друга и TSLab начинает процессы синхронизации. В результате некоторые сигналы на открытие/закрытие могут сниматься и потом снова ставиться. Мы ожидали что это решается выставлением сигнала не на +1 бар, а на +10, но по факту оказалось не так. Помимо этого выставление на +10 вообще хоронило любой сигнал в недрах TSLab и ничего не работало совсем. Это второй нежданчик, ведь все обычно работало. Пробуем пофиксить проблему штатным способом через поддержку TSLab.

Итоги

  • Все задачи были реализованы. Каких либо нереализованных функций не осталось.
  • Работает стабильно.
  • Удалось остаться в рамках обычной модели реализации, что оставило вкладки Доход, Сделки, Результаты.
  • Опробовали вынесение торговой логики во внешний R скрипт, чего ранее не делалось. Обычно она зашивалась в сам скрипт в виде команд R.

В результате эксплуатации бот показал положительный результат. Даже с учетом стартовых проблем в процессе отладки. Там видим просадку по счету.

Торговый робот "Краб"

Рисунок 2. График дохода робота на реальном рынке

Торговый робот "Краб"

Рисунок 3. Результаты торговли


comments powered by HyperComments

Николай
2016-10-29 00:56:54
Добрый ночер :=) На картинках, особенно на первой ничего не понятно. На второй тоже мало что понятно. Это наверное потому что я привык к МТ 4. Мне нужно сравнить результат "Краба" с результатами своего "Грааля" . Мне кажется у моего результаты повыше. Правда на Демо.
ra81
2016-10-29 11:43:54
НУ, нам старперам тслаба все вроде бы понятно :). Ну и это не демо :)
Гриша
2017-01-02 05:29:15
Секс знакомства http://bit.ly/2hkF2s9
8
Сен
2017

Доверительное управление. Результаты в августе 2017 года.

Доверительное управление. Результаты в августе 2017 года. Стратегия “Опционы” принесла в августе прибыль в размере… »

6
Авг
2017

Доверительное управление. Результаты в июле 2017 года.

Доверительное управление. Результаты в июле 2017 года. В июле индекс РТС вновь колебался в достаточно узком… »

14
Июл
2017

Доверительное управление. Результаты в июне 2017 года.

Доверительное управление. Результаты в июне 2017 года. Июнь индекс РТС вновь провел преимущественно в боковых движениях, а… »

11
Июн
2017

Доверительное управление. Результаты в мае 2017 года.

Доверительное управление. Результаты в мае 2017 года. В мае “болтанка” индекса РТС продолжилась, на паре… »

7
Май
2017

Доверительное управление. Результаты в апреле 2017 года.

Доверительное управление. Результаты в апреле 2017 года. В апреле мы наблюдали очередной месяц “боковика” по… »