Блог им. orekton

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

    • 20 октября 2014, 12:55
    • |
    • orekton
  • Еще
Мы продолжаем создавать нашего биржевого робота спредера. В этом уроке будем учиться искать заявки и разбираться с процессом отладки.

Предыдущие уроки:

Qlua для чайников. Часть 1
Qlua для чайников. Часть 2
Qlua для чайников. Часть 3. Делаем робота-спредера
Qlua для чайников. Часть 4. Анализ информации из стакана и работа с заявками


На прошлом уроке мы с вами написали заготовку, которая рассчитывает цены выставления наших заявок, на основе крайних цен в стакане (программа считает заданный отступ от этих цен). Если вы не читали прошлый урок, все равно зайдите на него и скачайте приложение – заготовку робота, в этом уроке вам она понадобится.
Как я уже говорил, у нашей программы есть недочеты. Во-первых, из-за того, что события изменения стакана приходит раньше, чем событие выставления заявок, у нас иногда проскакивают неверные цены. Подробнее опять см. прошлый урок. Во-вторых, после запуска у нас робот начинает работать только после того, как произойдут первые изменения в стакане. Как исправить эти недочеты? Давайте подумаем.

Итак, начнем с первой проблемы – рассинхронизация событий. Как вариант решения – все таки сделать поиск заявок, а не читать параметры введенной заявки в событии OnOrder. А сейчас  внимание!!! – важная информация. Поиск заявок – это одна из задач, которые можно решить при помощи функции SearchItems. Эта функция предназначена для поиска информации в различных таблицах Quik. Таблица заявок – это одна из таких таблиц.  Полный список таких таблиц можно посмотреть здесь http://help.qlua.org/ch4_5_3.htm, нас же интересует пока только таблица orders – заявки.
Для поиска заявок пишем функцию find_orders:

--Процедура поиска ордеров
function find_orders()
      local NO=getNumberOf(«orders»)
      t_orders = SearchItems(«orders», 0, NO-1, fn, «flags, sec_code, class_code»)
      if t_orders ~= nil then
            for i=1,#t_orders,1 do
                  t_orders_item=getItem(«orders», t_orders[i])
                  remember_order(t_orders_item)
            end
      end
end
Что делает эта функция? Во-первых, она получает количество элементов в таблице orders (количество заявок):

local NO=getNumberOf(«orders»)
Для чего это нам надо? Для того, что бы вызвать функцию SearchItems, которой необходимо указать диапазон поиска. Диапазон поиска начинается с нуля. Поэтому мы указываем так:

t_orders = SearchItems(«orders», 0, NO-1, fn, «flags, sec_code, class_code»)
Первый параметр SearchItems – это имя таблицы, в которой мы ищем, в данном случае orders. Второй параметр – начало диапазона поиска, третий конец диапазона поиска, четвертый –поисковая функция, о ней сейчас скажу отдельно. Пятый параметр  — это список полей таблицы ордеров, которые будет анализировать поисковая функция. У каждой таблицы свой набор полей, что касается таблицы orders, то полный список полей можно посмотреть тут http://help.qlua.org/ch4_6_4.htm. В нашем же случае используются следующие поля:
  • Flags – набор битовых флагов, про них я рассказывал на уроке 4. (ссылка)
  • sec_code – код инструмента.
  • class_code – код класса.
Теперь сама поисковая функция:

--Поисковая функция
function fn(flags, sec_code, class_code)
      if sec_code==p_seccode and class_code==p_classcode and bit.band(flags,1)>0 then          
            return true
      else
            return false
      end
end
Эта функция производит анализ входных параметров. Значения входных параметров – это значения полей, перечисленных в пятом параметре функции SearchItems. В частности, мы проверяем поля sec_code и class_code – наш ли это инструмент и проверяем первый флаг битовых флагов, который сигнализирует о том, выставленная ли заявка. Работа с флагами так же была описана в уроке 4 (ссылка).
Если заявка найдена, а это значит, что она удовлетворяет заданным условиям – активна и по нашем инструменту – то  эта заявка запоминается. Для этого используется функция remember_order:

function remember_order(order)
      p_file:write(os.date().."  заявка "..order[«order_num»].."\n")
      --если заявка активна, то запоминаем ее
      if bit.band(order[«flags»],1)>0 then
            --message(«флаг:»..bit.band(order[«flags»],4),1)
            if bit.band(order[«flags»],4)>0 then
                  sell_order=order[«order_num»]
                  sell_price=tonumber(order[«price»])
                  sell_count=tonumber(order[«balance»])
            else
                  buy_order=order[«order_num»]
                  buy_price=tonumber(order[«price»])
                  buy_count=tonumber(order[«balance»])
            end
      else
            --если заявка не активна то сбрасываем информацию о заявке
            if bit.band(order[«flags»],1)>0 then
                  if bit.band(order[«flags»],4)>0 then
                        sell_order=""
                        sell_price=0
                        sell_count=0
                  else
                        buy_order=""
                        buy_price=0
                        buy_count=0
                  end
            end
      end
end
По сути, эта функция – кусок кода, выдранный из OnOrder и повторяющий ее. Поэтому мы можем просто вызвать remember_order из функции OnOrder, сократив ее:

function OnOrder(order)
      p_file:write(os.date().." OnOrder\n");
      --сначала проверим, по нашему ли инструменту эта заявка
      if order[«sec_code»]==p_seccode and order[«class_code»]==p_classcode then
            remember_order(order)
      end
end
А в функции анализа стакана OnQuote добавим вызов find_orders() :

function OnQuote(class_code, sec_code)
      if class_code==p_classcode and sec_code==p_seccode then
     
            find_orders()



Теперь, теоретически, у нас должен исправиться недочет. Но не тут-то было. Если посмотреть лог, то можно увидеть, что перед выставлением заявки крайние цены все равно считаются неправильно. Ну что ж, зато есть повод поучиться отлаживать программу. Сразу скажу, что в Квике с отладчиком очень тяжко. Точнее, его нет совсем. Так что придется довольствоваться либо логированием, либо сообщениями.
Начнем отладку. Процесс поиска ошибок можно проводить по следующему алгоритму:
  1. Выдвигаем гипотезу, где может быть ошибка.
  2. Проверяем эту гипотезу.
  3. Если ошибка найдена, то исправляем. Иначе см. п. 1.
В данном случае логично предположить, что не работает функция поиска. Так что вставляем отладочное сообщение в нее:

--Процедура поиска ордеров
function find_orders()
      local NO=getNumberOf(«orders»)
      t_orders = SearchItems(«orders», 0, NO-1, fn, «flags, sec_code, class_code»)
      message(«find_orders: „..tostring(t_orders),1)
      if t_orders ~= nil then
            for i=1,#t_orders,1 do
                  t_orders_item=getItem(“orders», t_orders[i])
                  remember_order(t_orders_item)
            end
      end
end
Запускаем. Видим ряд сообщений. Вводим заявку. Сначала видим сообщение о факте ввода заявки:
 Qlua для чайников. Часть 5. Работа с таблица Quik. Поиск заявок. Искусство отладки
Затем видим наше сообщение:
 Qlua для чайников. Часть 5. Работа с таблица Quik. Поиск заявок. Искусство отладки 
Оно говорит о том, что функция SearchItems ничего не нашла. Но потом, с очередным изменением стакана приходит другое сообщение:
 Qlua для чайников. Часть 5. Работа с таблица Quik. Поиск заявок. Искусство отладки 
Оно уже говорит нам о том, что функция все-таки что-то нашла.
Мы выяснили, что функция SearchItems находит выставленные заявки не сразу, а лишь спустя некоторое время после их выставления.
Итак, нам опять придется искать выход. Еще можно попробовать событие OnTransReply. Оно вызывается, когда происходит транзакция. То есть, если мы выставили заявку, то у нас должно прийти OnTransReply. Теоретически, OnTransReply должно прийти раньше, чем OnOrder. Но лучше все-таки это проверить.  Для этого добавляем в нашего робота вот такую функцию:

--обработка события транзакции
function OnTransReply(trans_reply)
      nord=trans_reply[«order_num»] --Номер заявки
      if nord==nil or nord==0 or nord==«0» then
            message(«Заявка не выставилась»,1)
            return
      end
      if trans_reply[«sec_code»]==p_seccode and trans_reply[«class_code»]==p_classcode then
            remember_order(trans_reply)
      end
end
Но, увы, и это ничего не дало. По-прежнему один такт выдается неверная цена:

09/25/14 14:31:31    300.68,302.2
09/25/14 14:31:31    300.68,302.2
09/25/14 14:31:32    301.01(!!!!),302.2
09/25/14 14:31:32 OnOrder
09/25/14 14:31:32  заявка 3217747
09/25/14 14:31:32 OnOrder
09/25/14 14:31:32  заявка 3217747
09/25/14 14:31:33  заявка 3217747
09/25/14 14:31:33    300.68,302.2
09/25/14 14:31:34  заявка 3217747
              09/25/14 14:31:34    300.68,302.19
Как быть? Продолжать отглючивать (отлаживать).  Воспользуемся тем же методом, что и ранее.  Возможно,  OnTransReply так же приходит после обновления стакана. А может, ошибка и в самом обработчике OnTransReply. Что бы это выяснить, добавляем туда логирование:

--обработка события транзакции
function OnTransReply(trans_reply)
      p_file:write(os.date().." OnTransReply\n")
      nord=trans_reply[«order_num»] --Номер заявки
      p_file:write(os.date()..«nord=»..nord.."\n")
      if nord==nil or nord==0 or nord==«0» then
            message(«Заявка не выставилась»,1)
            return
      end
      p_file:write(os.date()..«сейчас будет проверка условия»..trans_reply[«sec_code»].."   "..trans_reply[«class_code»].."\n")
      if trans_reply[«sec_code»]==p_seccode and trans_reply[«class_code»]==p_classcode then
            p_file:write(«Сейчас запустим remember_order\n»)
            remember_order(trans_reply)
      end
end
Логирование показало, что OnTransReply не запускается вообще. Обычно в таком случае имеет смыл задать вопрос на форуме по qlua (http://www.quik.ru/forum/lua/), хотя ответа иногда приходиться ждать несколько часов или дней. Я задал вопрос и получил ответ, что для заявок, введенных вручную, OnTransReply не работает.

Полная версия статьи и текущий код робота на robostroy.ru 
На вопросы автор отвечает там же.
6.2К | ★68
5 комментариев
спасибо за вашу работу! ловите плюсы
avatar
полезно и интересно, спасибо
avatar
Спасибо! Очень интересно.
avatar
спасибо и + в проф.
avatar
Спасибо, очень полезно! А сделайте, пожалуйста, пост как написать скрипт для «плавающего стопа»))
avatar

Читайте на SMART-LAB:
Фото
ЛЧИ-2026: спецноминации для клиентов «Финама» с призовым фондом 5 млн ₽
🏆 «Финам» выступит партнером легендарного конкурса Московской биржи «Лучший частный инвестор» (ЛЧИ-2026) и предложит своим клиентам...
Персонализация в МФО: практика рынка и Займера
Интересное исследование  о применении индивидуального подхода к клиентам в МФО провел медиахолдинг «Просто» на основе опроса 15 крупнейших...
Фото
🔒 Экономим ваше время на размещениях
Чтобы вовремя заметить выгодную сделку или, наоборот, не тратить время на то, что не актуально для портфеля, важно видеть разницу между закрытым и...
Фото
Мой Рюкзак #65: Ставка на энергетический и продовольственный кризис из-за перекрытия проливов
Если февраль радовал стоимостных инвесторов, то март пока радует только валютных спекулянтов и миноритариев Роснефти и Совкомфлота (в совкомфлоте...

теги блога orekton

....все тэги



UPDONW
Новый дизайн