orekton
orekton личный блог
25 декабря 2014, 16:09

Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Этот урок будет посвящен ответу на некоторые ваши вопросы, которые накопились в ходе публикации данных уроков.

Qlua для чайников. Часть 1

Qlua для чайников. Часть 2

Qlua для чайников. Часть 3. Делаем робота-спредера

Qlua для чайников. Часть 4. Анализ информации из стакана и работа с заявками

Qlua для чайников. Часть 5. Работа с таблица Quik. Поиск заявок. Искусство отладки

Qlua для чайников. Часть 6. Модуль торговли. Остатки по бумагам на фондовом рынке. Удаление заявок


Вопрос: Можно пример, что бы в 23.40 закрывались все открытие позиции по рынку?

Для решения поднятой в данном вопросе задачи необходимо следующее:

  • Знать, как выставлять заявки. Это мы уже умеем. Данную тему мы изучили на уроке 1 (http://robostroy.ru/community/article.aspx?id=773) и уроке 6 (http://robostroy.ru/community/article.aspx?id=790), где мы писали блок совершения сделок биржевого робота.
  • Получить список позиций (частично этот вопрос мы так же изучили на уроке 6).
  • Работать со временем. Этому мы сейчас будем учиться.
  • Выставлять заявку именно по рынку. Этому тоже мы будем сейчас учиться.

Итак, начнем. Сначала работа со временем. Косвенно мы уже умеем это. Давайте вспомним кусок кода из робота, которого мы писали на уроках 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, затем выводим результат. У нас должно выскочить первое сообщение вот такое (там их будет три):

Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1 

Затем программа преобразует из этого формата обратно в таблицу и выводит дату и время на экран, обращаясь к полям этой таблицы:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

А затем точно так же выводит текущую дату и время:

Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1 

И так, обратите внимание на последние две строки:

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».

Как же быть? — спросите вы. Ну что ж, нам ничего не остается, как самому вычислить рыночную цену и поставить ее в заявку. А как вычислить? Можно взять из текущей таблицы параметров:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Для обращения к текущей таблице параметров используем getParamEx, например, так:

bid=getParamEx(«SPBFUT»,«GZH5»,«BID»)

message(bid.param_value,1)

Этот пример сообщит цену спроса:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 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.

Итак, для начала создадим новый проект:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Тип проекта «Проект Win32», язык Visual C++:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Так же в диалоге выбора типа проекта не забудьте указать имя вашего проекта (латинскими буквами, без пробелов) и выбрать каталог, куда вы поместите проект.

Далее у вас откроется вот такое окно, тут надо нажать «Далее»:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

И мы перейдем к диалогу настройки параметров приложения, надо выбрать «Библиотека DLL», все «галочки» оставить выключенными:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

После этого у нас откроется созданный проект:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Как правило, в верхнем правом углу у нас присутствует обозреватель решений, в котором видно дерево объектов нашего проекта: файлы C++, заголовочные файлы, ресурсы и прочее:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Для того чтобы нашу dll-ку можно было подключить к lua-скрипту, нам необходима библиотека lua5.1 и соответствующие заголовочные файлы (все это, а так же полный рабочий дистрибутив lua можно скачать с сайта lua.org, распространятся он бесплатно). Нужные файлы присутствуют в приложении 2 (исходники примера подключения dll к скрипту lua).

Итак, перечислю эти файлы:

  • lauxlib.h
  • lua.h
  • luaconf.h
  • lua5.1.lib

Все их нужно положить в отдельный каталог, например, contrib и для удобства скопировать этот каталог в папку проекта:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Теперь нужно подключить эти файлы к проекту. Для этого в папке «Заголовочные файлы» создадим новую папку:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

А в саму папку добавим все заголовочные файлы из списка:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

После этого они должны отобразиться в соответствующей ветке:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

У нас еще остался файл lua5.1.lib. Его подключаем через свойства проекта:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Ищем в дереве «Свойства конфигурации» -> «Компоновщик» -> «Ввод»:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Идем в дополнительные зависимости, выбираем «Изменить»:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Добавляем путь к нашей библиотеке contrib/lua5.1.lib:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Жмем «ОК», сохраняем проект. Теперь можем перейти к программированию. Откроем файл dllmain.cpp:

 Qlua для чайников. Часть 7. Отвечаю на ваши вопросы. Часть 1

Здесь мы видим функцию 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;

}



Полная версия статьи и текущий код робота на robostroy.ru 
На вопросы автор отвечает там же.
4 Комментария

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

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