Избранное трейдера MrD
Settings = { Name = "xBollinger_LinReg", period = 40, deviation=2, line= { { Name = "xBollinger_LinReg", Color = RGB(0, 0, 255), Type = TYPE_LINE, Width = 2 }, { Name = "xBollinger_LinReg", Color = RGB(192, 0, 0), Type = TYPE_LINE, Width = 2 }, { Name = "xBollinger_LinReg", Color = RGB(0, 128, 0), Type = TYPE_LINE, Width = 6 } } } function c_FF() local AMA={} local CC={} return function(ind, _p,_ddd) local period = _p local index = ind local vol = 0 local sigma = 0 local sigma2 = 0 local aav = 0 local bb = 0 local ZZZ = 0 if index == 1 then AMA={} CC={} CC[index]=(C(index)+H(index)+L(index))/3 AMA[index]=(C(index)+O(index))/2 return nil end ------------------------------ AMA[index]=AMA[index-1] CC[index]=(C(index)+H(index)+L(index))/3 if index < (_p) then return nil end period =_p if index < period then period = index end --------------- sigma=0 sigma2=0 aav=0 ZZZ=0 for i = 0, period-1 do ZZZ=CC[index+i-period+1] aav=aav+ZZZ sigma=sigma+ZZZ*(-(period-1)/2+i) sigma2=sigma2+(-(period-1)/2+i)^2 end bb=sigma/sigma2 aav=aav/period AMA[index]=aav+bb*((period-1)/2) sigma=0 sigma2=0 sigma3 = 0 for i = 0, period-1 do ZZZ=CC[index+i-period+1] sigma2=aav+bb*(-(period-1)/2+i) sigma=sigma+(ZZZ-sigma2)^2 end sigma=(sigma/period)^(1/2) return AMA[index]-sigma*_ddd,AMA[index]+sigma*_ddd, AMA[index] end end function Init() myFF = c_FF() return 3 end function OnCalculate(index) return myFF(index, Settings.period,Settings.deviation) end
Settings = { Name = "xLinRegP", period = 128, deviation=2, line= { { Name = "xLinRegP", Color = RGB(128, 128, 255), Type = TYPE_LINE, Width = 4 }, { Name = "xLinRegP", Color = RGB(192,128,128), Type = TYPE_LINE, Width = 4 }, { Name = "xLinRegP", Color = RGB(96, 128,96), Type = TYPE_LINE, Width = 4 } } } ---------------------------------------------------------- ---------------------------------------------------------- ---------------------------------------------------------- function cached_FF() local AMA={} local CC={} local II2 = 0 local II4 = 0 return function(ind, _p,_ddd) local period = _p local index = ind local vol = 0 local sigma = 0 local sigma2 = 0 local sigma3 = 0 local sigma4 = 0 local aav = 0 local aa = 0 local bb = 0 local cc = 0 local ZZZ = 0 local ttt = 0 if index == 1 then AMA={} CC={} CC[index]=(C(index)+H(index)+L(index))/3 AMA[index]=(C(index)+O(index))/2 II2=0 II4=0 for i = 0, period-1 do ttt=(-(period-1)/2+i)^2 II2=II2+ttt II4=II4+ttt^2 end return nil end ------------------------------ AMA[index]=AMA[index-1] ---------------------------------- CC[index]=(C(index)+H(index)+L(index))/3 --------------------- if index < (Size()-2) then return nil end ---------------------------------------------------- sigma=0 sigma2=0 sigma3=0 sigma4=0 aav=0 ZZZ=0 for i = 0, period-1 do ZZZ=CC[index+i-period+1] aav=aav+ZZZ sigma=sigma+ZZZ*(-(period-1)/2+i) ttt=(-(period-1)/2+i)^2 sigma3=sigma3+ZZZ*ttt end ------------------------ bb=sigma/II2 cc=(sigma3-aav*II2/period)/(II4-II2*II2/period) aa=(aav-cc*II2)/period aav=aav/period AMA[index]=aa+bb*((period-1)/2)+cc*((period-1)/2)^2 ------- парабола ------------------------------- sigma=0 sigma2=0 sigma3 = 0 for i = 0, period-1 do ZZZ=CC[index+i-period+1] sigma2=aa+bb*(-(period-1)/2+i)+cc*(-(period-1)/2+i)^2 ------- парабола sigma=sigma+(ZZZ-sigma2)^2 end sigma=(sigma/period)^(1/2) for i = 1, period-1 do ZZZ=aa+bb*(-(period-1)/2+i)+cc*(-(period-1)/2+i)^2 SetValue(index+i-period+1, 3, ZZZ) SetValue(index+i-period+1, 2, ZZZ+sigma*_ddd) SetValue(index+i-period+1, 1, ZZZ-sigma*_ddd) end SetValue(index+0-period+1, 3, nil) SetValue(index+0-period+1, 2, nil) SetValue(index+0-period+1, 1, nil) ---------------------------------- return AMA[index]-sigma*_ddd,AMA[index]+sigma*_ddd, AMA[index] end end ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- function Init() myFF = cached_FF() return 3 end function OnCalculate(index) return myFF(index, Settings.period,Settings.deviation) end
Продолжаем разработку универсального робота!
Выкладываю код 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));