Избранное трейдера MrD
Продолжаем разработку универсального робота!
Выкладываю код OUR-0.3, который в настоящий момент еще далеко не полный – это только основа, скачать можно здесь https://yadi.sk/d/l3uic67yruCxa
Код прокомментирован подробно, но дам дополнительное описание общего плана, чтобы логику работы робота можно было представить.
Итак, по порядку:
Робот состоит из двух файлов: OUR.lua содержит основные функции (OnInit, main, коолбэки – пока только один OnStop), FunOUR.lua содержит вспомогательные функции – все остальные. Дополнительно приложен файл с информацией и файл с образцом котировок.
Функция OnInit
1 Первоначально котировки с сервера поступают в источник – таблицу с барами TBar (там все заполняется автоматически при подключении источника).
2 Далее робот делает различные вычисления, результаты которых он помещает в таблицу с данными TDat (также туда копируются параметры баров из TBar), эту таблицу нужно заполнять самому, ключи таблицы на свое усмотрение, но конечно часть ключей в алгоритм уже заложены, это «key»,«O»,«H»,«L»,«C»,«V»,«T» от них идут все вычисления. TDat – это таблица, содержащая таблицы по каждому бару, ключ соответствует номеру бара в источнике. Структура такого типа:
TDat = { [1321] = {"O","H","L","C","SMAf","SMAs"…}, [1322] = {"O","H","L","C","SMAf","SMAs"…}, … }
Небольшая статья с ресурса http://www.talaikis.com/ о построении простой стратегии, использующую наивный байесовский классификатор при создании процесса возврата к среднему. Весь код в статье приведен на языке Python.
Это достаточно большая область исследований, но расскажем все очень кратко. Мы попытаемся найти взаимоотношение между временными сериями (в данном случае возьмем в качестве сигнала взаимный фонд XLF из финансового сектора, сдвинутый по времени на 1 день назад), а нашей целью будет фьючерс S&P500 в форме CFD. Будем входить в длинную позицию по этой бумаге при нулевой вероятности приращения. Логически нулевая вероятность ни о чем не говорит, другими словами, будем покупать возврат к среднему.
1. Получение данных
Y = read_mongo(dbase, "S&P5001440") X = read_mongo(dbase, syms[s]).shift() #готовим набор данных res = pd.concat([X.CLOSE, Y.CLOSE], axis=1, join_axes=[X.index]).pct_change().dropna() res.columns = ['X', 'Y']
Подумал, что многие не знают, как подступиться к языку Qlua и запустить робота в квике. А между тем, это настолько просто, что даже не требует ничего кроме квика, виндусовского блокнота и знаний самого Qlua.
Qlua – это скриптовый язык поддерживаемый квиком, в основе язык lua 5.1 (в моем квике версия такая).
Скрипты, написанные на Qlua – это обычные текстовые файлы, которые имеют расширение «.lua». То есть можно сделать файл в обычном блокноте и после сохранения поменять в нем расширение с «.txt» на «.lua». Если внутрь этого файла записать инструкции кода на языке Qlua, то квик будет выполнять их.
Для удобства написания инструкций кода лучше пользоваться не виндусовым стандартным блокнотом, а например Notepad++, который можно скачать официально и бесплатно здесь https://notepad-plus-plus.org/download/v6.9.1.html. Он позволяет включить подсветку синтаксиса различных языков программирования, в том числе и lua, что очень помогает при написании кода.
В Notepad++ в «Опции -> Настройки» можно выбрать русский язык, а в «Опции -> Определение стиля» установить для lua понравившийся стиль отображения. Я для «Язык -> lua» ставлю стиль «Выбрать стиль -> Bespin» и еще в окошке «Стиль» для последних трех «FUNC» переопределяю цвет, иначе они с фоном сливаются.
Если говорить образно. То, чтобы отправить сообщение на сервер, нам просто нужно сформировать нужную строку со всеми данными и отправить ее на биржу. Ну например:
8=FIX.4.4;9=78;35=A;49=FG;56=tgFhcfx901U05;34=1;52=20160212-11:42:51.812;98=0;108=3000;141=Y;10=047;
Если быть внимательным, то мы увидим, что кол-во символов в строке у нас 100, а в заголовке сообщения мы передаем, что 78 (9 = 78). По правилам протокола FIX, длину сообщения нужно считать без учета концовки и первых двух полей заголовка. А именно:
С длиной сообщения разобрались. Теперь про разделитель. Пока в моем скрине это ";". В документациях западных написано что это символ SOH. Чтобы однозначно ответить на этот вопрос, я запустил прилагаемого клиента биржи и сниффером стал перехватывать сообщения между клиентом и биржей. Кстати, программа ведет логи, и их общение выглядит так (зеленое — передача запроса на биржу, красное — ответ от биржи)://Получаем ip сервера
IPAddress ipAddr = IPAddress.Parse(server);
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, port);
//Создаем заголовк
HeaderMessage msHeader = new HeaderMessage
{
BeginString = «FIX.4.4»,
MsgType = «A», //Тип сообщения на установку сессии
SenderCompID = "",
TargetCompID = «FG»,
MsgSeqNum = 1
};
//Создаем сообщение на подключение onLogon
LogonMessage msLogon = new LogonMessage
{
EncryptMethod = 0,
HeartBtInt = 3000,
ResetSeqNumFlag = true
};//Вычисляем длину сообщения
msHeader.BodyLength = msHeader.GetHeaderSize() + msLogon.GetMessageSize();
//Создаем концовку сообщения
TrailerMessage msTrailer = new TrailerMessage(msHeader.ToString() + msLogon.ToString());//Формируем полное готовое сообщение
string fullMessage = msHeader.ToString() + msLogon.ToString() + msTrailer.ToString();
Console.WriteLine(«Сообщение для отправки {0}»,fullMessage);//Создаем сокет для подключения
sSender = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//Подключаемся
sSender.Connect(ipEndPoint);
Console.WriteLine(«Сокет соединился с {0} », sSender.RemoteEndPoint.ToString());
byte[] msg = Encoding.UTF8.GetBytes(fullMessage);
//Отправляем сообщение
int bytesSent = sSender.Send(msg);
Console.WriteLine(«Отправил {0} байт», bytesSent.ToString());
//Получаем ответ от сервера
byte[] bytes = new byte[1024];
int bytesRec = 0;
bytesRec = sSender.Receive(bytes);
Console.WriteLine(«Ответ от сервера: {0}», Encoding.UTF8.GetString(bytes, 0, bytesRec));
Окончание.Начало здесь.
Проверка эффективности индикаторов на реальных данных
В качестве проверки верности результатов на симуляционных данных мы использовали два реальных набора данных для исследования производительности индикаторов. Эти тесты требовали уровня 6 данных, для того, чтобы мы смогли точно воссоздать книгу заявок, отслеживая каждый лимитный ордер в моменты его поступления, изменения и удаления из очереди заявок, так же как и исполнение различных ордеров для точной реконструкции событий. В дополнение, мы присвоили каждому ордеру идентификатор, который классифицировал этот ордер как автоматический или ручной. Последней задачей был поиск событий, происходящих на отдельных рынках для изучения их влияния на происхождение мини обвалов цены.
Мы использовали два отдельных набора данных для исследования предсказательной способности индикаторов:
Сейчас я попробую разложить торговлю по полочкам, вычленить независимые составляющие и их проанализировать.
Пусть у нас есть торговый алгоритм, который выдает приказ на покупку или продажу. Для выхода используем тупой алгоритм типа таймаут, случайный выход, выхода по стоп-лосс, тейк-профит, трейлинг-стоп и т.п. Комиссию не учитываем.
Обозначим рекомендацию алгоритма O[i] = -1, 0, 1, где i — номер потенциальной сделки. -1 соответствует рекомендации продать, 1 — купить, 0 — ничего не делать. Объем сделки обозначим V[i] >= 0.
Результат сделки и при единичном объеме и при условии что только покупаем обозначим R[i]. Будем считать что на рынке на всем периоде торговли нет устойчивого тренда вверх т.е. стратегия “купил и держи” в среднем прибыли/убытка не приносит. Тогда матожидание (M) от произвольной сделки на покупку равно нулю M(R[i])=0.
Итого, мы разделили торговлю на три независимые составляющие: