Блог им. orekton
Этот урок будет посвящен ответу на некоторые ваши вопросы, которые накопились в ходе публикации данных уроков.
Qlua для чайников. Часть 3. Делаем робота-спредера
Qlua для чайников. Часть 4. Анализ информации из стакана и работа с заявками
Qlua для чайников. Часть 5. Работа с таблица Quik. Поиск заявок. Искусство отладки
Qlua для чайников. Часть 6. Модуль торговли. Остатки по бумагам на фондовом рынке. Удаление заявок
Вопрос: Можно пример, что бы в 23.40 закрывались все открытие позиции по рынку?
Для решения поднятой в данном вопросе задачи необходимо следующее:
Итак, начнем. Сначала работа со временем. Косвенно мы уже умеем это. Давайте вспомним кусок кода из робота, которого мы писали на уроках 1-6:
p_file:write(os.date().." начало remember_order\n") |
Как вы помните, этот кусок кода выводит сообщение в лог (в файл лога). Кроме основного сообщения, в логе присутствует дата и время. Вот образец:
10/15/14 12:53:58 limit: 0 10/15/14 12:53:58 OnTransReply 10/15/14 12:53:58 начало remember_order 10/15/14 12:53:58 remember_order: заявка активна 10/15/14 12:53:58 флаг:0 |
Таким образом, os.date() у нас возвращает текущую дату и время, причем в формате, установленном по умолчанию (американский формат, когда сначала идет месяц, потом число, потом год). Но это только в том случае, когда эта функция без параметров. На самом деле у функции есть два параметра, оба из них необязательны. Первый параметр – это формат, второй – это дата и время в формате POSIX. Формат POSIX – это количество секунд, прошедших с нуля часов (полуночи) 1 января 1970 года. Кроме POSIX есть еще формат в виде таблицы datetime. Оба этих формата можно преобразовывать друг в друга.
Давайте рассмотрим пример:
datetime = { year = 2013, month = 09, day = 13, hour = 21, min = 40, sec = 15 } seconds_since_epoch = os.time(datetime) message(tostring(seconds_since_epoch),1) dt=os.date("*t",seconds_since_epoch) message(tostring(dt[«year»].."/"..dt[«month»].."/"..dt[«day»].." "..dt[«hour»].."/"..dt[«min»].."/"..dt[«sec»]),1) dt1=os.date("*t") message(tostring(dt1[«year»].."/"..dt1[«month»].."/"..dt1[«day»].." "..dt1[«hour»].."/"..dt1[«min»].."/"..dt1[«sec»]),1) |
В этом примере мы задам дату в виде таблицы datetime, преобразуем ее в POSIX, затем выводим результат. У нас должно выскочить первое сообщение вот такое (там их будет три):
Затем программа преобразует из этого формата обратно в таблицу и выводит дату и время на экран, обращаясь к полям этой таблицы:
А затем точно так же выводит текущую дату и время:
И так, обратите внимание на последние две строки:
dt1=os.date("*t") message(tostring(dt1[«year»].."/"..dt1[«month»].."/"..dt1[«day»].." "..dt1[«hour»].."/"..dt1[«min»].."/"..dt1[«sec»]),1) |
Именно они иллюстрируют, как работать с датой в виде таблицы, чтобы выполнить нашу задачу — закрыть позиции в 23.40.
Итак, нам надо получить дату и время в виде таблицы datetime, для чего мы используем os.date() с параметром "*t", второй параметр опускаем, так как нам надо получить текущую дату. В полученной дате проверяем часы (должно быть равно 23) и минуты (должно быть больше или равно 40):
curr_date=os.date("*t") if curr_date[«hour»]==23 and curr_date[«min»]>=40 then close_all_position() end |
Теперь нам осталось реализовать саму функцию close_all_position. Что должна сделать данная функция? Во-первых, перебрать все открытые позиции, а во вторых, для каждой из открытых позиций выставить заявку на закрытие, причем по рынку.
Перебирать все позиции на фондовом рынке вы умеете из урока 6 (http://robostroy.ru/community/article.aspx?id=790). Единственное, в фильтры не надо ставить конкретный инструмент, что бы он перебрал все позиции. А вот как получить позиции на срочном рынке, мы еще не рассматривали. А на срочном рынке мы получаем позиции точно так же. Только таблица другая, а именно futures_client_holding. Вот пример такого перебора:
function fn(limit_type) if limit_type==0 then return true else return false end end local NO=getNumberOf(«futures_client_holding») t_limits = SearchItems(«futures_client_holding», 0, NO-1, fn, «type») if t_limits ~= nil then for i=1,#t_limits,1 do t_limit_item=getItem(«futures_client_holding», t_limits[i]) message(tostring(t_limit_item[«type»]).." "..tostring(t_limit_item[«sec_code»]).." "..tostring(t_limit_item[«totalnet»]),1) end end |
Как видим, в отличие от того, что делали на уроке 6, мы используем только фильтр по типу лимитов, так как нам надо пробежаться по всем инструментам, но при этом нам не нужно рассматривать всякие технологические лимиты.
Теперь перейдем к рыночным заявкам. По фондовому рынку это сделать легко, нужно просто поставить в поле TYPE значение «M» вместо «L», а в цену поставить нуль, вот пример:
t = { [«CLASSCODE»]=«TQBR», [«SECCODE»]=«POLY», [«ACTION»]=«NEW_ORDER», [«ACCOUNT»]=«L01-00000F00», [«CLIENT_CODE»]=«52134», [«TYPE»]=«M», [«OPERATION»]=«S», [«QUANTITY»]=«1», [«PRICE»]=«0», [«TRANS_ID»]=«1» } res=sendTransaction(t) message(res,1) |
Со срочным рынком такой номер не пройдет. Там нельзя выставлять заявки типа «M».
Как же быть? — спросите вы. Ну что ж, нам ничего не остается, как самому вычислить рыночную цену и поставить ее в заявку. А как вычислить? Можно взять из текущей таблицы параметров:
Для обращения к текущей таблице параметров используем getParamEx, например, так:
bid=getParamEx(«SPBFUT»,«GZH5»,«BID») message(bid.param_value,1) |
Этот пример сообщит цену спроса:
Соответственно, для получения цены предложения надо использовать «OFFER» вместо «BID». К сожалению, в таблице futures_client_holding нет кода класса, его придется получать отдельно, например, вот так:
a=getSecurityInfo("", «GZH5»).class_code message(a,1) |
Теперь, собственно говоря, вы все знаете для того, чтобы написать процедуру закрытия всех позиций по расписанию. В приложении 1 вы найдете пример закрытия всех позиций по расписанию, сделанный для небольших объемов торговли и для срочного рынка (фьючерсы). Просто вставьте в свой код функции close_all_position и fn, а вызов close_all_position через условия проверки времени в то место вашего кода, где будете проверять время, например, в цикл main.
Переходим к следующему вопросу.
Вопрос: Ув. megabax вопрос — скажите можно в скриптах Lua использовать внешние dll? Если, да то не могли бы Вы описать синтаксис функции обращения? P.S. Просто я использую внешнею dll написанную под Omega и хотелось бы ее «прикрутить» к графику цены в Квике через LUA.
Да, внешние dll использовать возможно, хотя это не так то просто. Но все же попытаюсь популярно объяснить синтаксис. Начну с того, что не всякую dll можно подключить к lua-скрипту, а только те, которые оформлены определенным образом. Сейчас мы разберем простейший пример создание такой dll на языке C++ в среде разработки Visual Studio 2010.
Итак, для начала создадим новый проект:
Тип проекта «Проект Win32», язык Visual C++:
Так же в диалоге выбора типа проекта не забудьте указать имя вашего проекта (латинскими буквами, без пробелов) и выбрать каталог, куда вы поместите проект.
Далее у вас откроется вот такое окно, тут надо нажать «Далее»:
И мы перейдем к диалогу настройки параметров приложения, надо выбрать «Библиотека DLL», все «галочки» оставить выключенными:
После этого у нас откроется созданный проект:
Как правило, в верхнем правом углу у нас присутствует обозреватель решений, в котором видно дерево объектов нашего проекта: файлы C++, заголовочные файлы, ресурсы и прочее:
Для того чтобы нашу dll-ку можно было подключить к lua-скрипту, нам необходима библиотека lua5.1 и соответствующие заголовочные файлы (все это, а так же полный рабочий дистрибутив lua можно скачать с сайта lua.org, распространятся он бесплатно). Нужные файлы присутствуют в приложении 2 (исходники примера подключения dll к скрипту lua).
Итак, перечислю эти файлы:
Все их нужно положить в отдельный каталог, например, contrib и для удобства скопировать этот каталог в папку проекта:
Теперь нужно подключить эти файлы к проекту. Для этого в папке «Заголовочные файлы» создадим новую папку:
А в саму папку добавим все заголовочные файлы из списка:
После этого они должны отобразиться в соответствующей ветке:
У нас еще остался файл lua5.1.lib. Его подключаем через свойства проекта:
Ищем в дереве «Свойства конфигурации» -> «Компоновщик» -> «Ввод»:
Идем в дополнительные зависимости, выбираем «Изменить»:
Добавляем путь к нашей библиотеке contrib/lua5.1.lib:
Жмем «ОК», сохраняем проект. Теперь можем перейти к программированию. Откроем файл dllmain.cpp:
Здесь мы видим функцию DllMain. Ее надо сделать такой:
// стандартная точка входа для DLL BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } |
Впереди функции DllMain надо вставить следующий текст:
#include <windows.h> #include <process.h> // в случае вызова функций из LUA-кода во внешней DLL // необходимо определить эти константы до подключения заголовочных файлов LUA #define LUA_LIB #define LUA_BUILD_AS_DLL // заголовочные файлы LUA из дистрибутива LUA extern «C» { #include «contrib/lauxlib.h» #include «contrib/lua.h» } |
Здесь мы подключаем нужные библиотеки, устанавливаем директивы препроцессора и подключаем библиотеки для работы с lua. Они нужны нам, чтобы получать от lua-скрипта параметры функции и вернуть функции какое либо значение, а так же для различных вспомогательных функций, типа регистрации добавленных функций и так далее. Собственно, это и есть подключение lua5.1.lib.
Далее, после функции DllMain мы размещаем наши функции, которые будут вызываться из lua-скрипта, например, такие:
//Сложение двух чисел static int forLua_SummTwoNumbers(lua_State *L) { // получаем первый и второй параметры вызова функции из стека с проверкой каждого на число double d1 = luaL_checknumber(L, 1); double d2 = luaL_checknumber(L, 2); // помещаем в стек результат сложения lua_pushnumber(L, d1 + d2); return(1); // эта функция возвращает одно значение } //сложение нескольких чисел, сколько — заранее неизвестно static int forLua_SummAllNumbers(lua_State *L) { const int n = lua_gettop(L); // количество переданных аргументов double res = 1; bool isNumberFound = false; for (int i = 1; i <= n; ++i) if (lua_type(L, i) == LUA_TNUMBER) { res += lua_tonumber(L, i); isNumberFound = true; } if (isNumberFound) lua_pushnumber(L, res); else lua_pushnil(L); return(1); } |
После того как мы объявили наши функции, их нужно зарегистрировать:
// регистрация реализованных в dll функций, чтобы они стали «видимы» для LUA static struct luaL_reg ls_lib[] = { {«SummTwoNumbers», forLua_SummTwoNumbers}, {«SummAllNumbers», forLua_SummAllNumbers}, {NULL, NULL} }; extern «C» LUALIB_API int luaopen_dllmain(lua_State *L) { luaL_openlib(L, «dllmain», ls_lib, 0); return 0; } |
осталось только как то собраться и прочитать все уроки)