Заметка

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

  7  

Закончим писать робота, который мы начали на предыдущих уроках. Напомню, что мы пишем робота-спредера. Он будет выставлять заявки на покупку и на продажу, если в стакане достаточно большой спред. На данный момент наша заготовка робота может корректно определять крайние цены в стакане с учетом уже выставленных заявок. Настало время написать модуль, который будет торговать.

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

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

Qlua для чайников. Часть 3. Работа со стаканом

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

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

Какие функции должен выполнять этот модуль торговли? Сейчас я перечислю их:

  • Проверять размер спреда, если он больше заданной величины, то это сигнал на совершение сделки.
  • Выставлять заявки, если есть сигнал.
  • Следить затем, чтобы не было выставлено лишних заявок – если заявка выставлена но еще не сработала, то новую заявку не выставляем.
  • Переставлять заявки, если изменились крайние цены в стакане.
  • Следить за количеством инструмента – если, например, мы выставили заявку на покупку и на продажу, одна из них сработала или частично сработала, а потом изменились цены, то при перевыставлении заявок надо учесть этот факт.

Что бы робот следил за тем, сколько инструментов мы купили/продали, нам необходимо научится получать количество инструмента на балансе. Стоит заметить, что если мы решили привязаться к количеству акций на балансе, то мы предполагаем, что другие роботы по этому инструменту на этом счете не торгуют и вручную этим инструментом мы тоже не торгуем. В противном случае нам придется по-другому отслеживать количество проданных или купленных акций. Как вариант обрабатывать событие OnTrade (совершение сделки), как это сделано в роботе на RSI (http://robostroy.ru/community/article.aspx?id=765).

Но в нашем случае мы будет отслеживать через количество акций на балансе (чтобы изучить данную тему). При написании собственных роботов, можете выбрать тот способ, который сочтете нужным.

Итак, начнем. Сначала разработаем функцию, предназначенную для получения остатка по ценной бумаге. Стоит заметить, что для срочного и фондового рыков способы получения остатков отличаются. Сегодня мы изучим способ для фондового рынка.

На фондовом рынке остатки по акциям можно получить из таблицы DEPO_LIMITS – остатки по бумагам. С таблицами мы уже работали на уроке 5 (http://robostroy.ru/community/article.aspx?id=788). Тут точно так же, только другое имя таблицы. Таким образом, подпрограмма поиска лимита включает в себя две функции find_limit – сама функция поиска и fn_limit – вспомогательная функция, которая отвечает за осуществление отбора при поиске. Вот эти функции:

--Поисковая функция лимита

function fn_limit(sec_code, limit_kind, currentbal)

      if sec_code==p_seccode and limit_kind==2 then 

            return true

      else

            return false

      end

end

--Процедура поиска лимита

function find_limit()

      local NO=getNumberOf("DEPO_LIMITS")

      t_limits = SearchItems("DEPO_LIMITS", 0, NO-1, fn_limit, "sec_code, limit_kind, currentbal")

      if t_limits ~= nil then

            t_limit_item=getItem("DEPO_LIMITS", t_limits[1])

            return t_limit_item["currentbal"]

      end

      return 0

end

У вас может возникнуть вопрос – а что же такое limit_kind?. А это тип лимита. Сейчас на фондовом рынке акции торгуются по принципу T2 – все расчет сдвинуты на два дня. Акции, которые вы купили сегодня, поступают на остаток Т2.  На следующий день они окажутся на Т1 и еще на следующий на Т0. Тип остатка Т0 – это то, что  у вас есть сейчас. Т1 – что будет завтра, а Т2 – что будет послезавтра.

Если вы торгуете интрадей, то целесообразно анализировать остатки типа Т2, так как при покупке акций у вас они зачислиться именно туда. Оттуда же и списываются при продаже. Правда, финансовый результат от подобных операций будет доступен только через два дня, когда он попадет на Т0.

Проверить эту функцию можно просто выводя остатки в логе или через message. Вот как к ней можно обращаться:

p_file:write(os.date().." limit:  "..find_limit().."\n")

Теперь займемсявыставлением заявок. Из урока 1 (http://robostroy.ru/community/article.aspx?id=773) вы уже знаете, как это делать. Можно просто скопировать оттуда код и отредактировать, как вам надо. В частности, в поля CLASSCODE, SECCODE, ACCOUNT, CLIENT_CODE поставить параметры. Другой вопрос, что ставить в поля OPERATION, QUANTITY, PRICE? Эти поля необходимо сначала вычислить. Поэтому, для выставления заявки целесообразно создать отдельную функцию, например, trade:

function trade(a_oper, a_count, a_price)

      t = {

                  ["CLASSCODE"]=p_classcode",

                  ["SECCODE"]=p_seccode,

                  ["ACTION"]="NEW_ORDER",

                  ["ACCOUNT"]=p_account,

                  ["CLIENT_CODE"]=p_client_code,

                  ["TYPE"]="L",

                  ["OPERATION"]=a_oper,

                  ["QUANTITY"]=tostring(a_count),

                  ["PRICE"]=tostring(a_price),

                  ["TRANS_ID"]="1"

            }

      res=sendTransaction(t)

      message(res,1)

end

Обратите внимание, что мы добавили парочку параметров, так что их нужно прописать в самом начале программы (там, где у нас другие параметры):

p_account="L01-00000F00" --Клиентский счет(новый параметр)

p_client_code="52720" -- Код клиента(новый параметр)

Разумеется, значение параметров нужно поставить свои.

Для того чтобы написать код выставления заявки, нам опять же, потребуются дополнительные параметры: количество лот, которым мы торгуем, размер лота (потому что в заявке мы задаем количество в лотах, а остатки программа нам возвращает в акциях), ну, и разумеется, минимальный спред. Разметите эти параметры также в начале программы:

p_count=5 -- скольки лотами торгуем

p_min_spread=0.000010 --минимальный сперд, при котором мы торугем

p_lost_size=100000 --размер лота

А вот и сам код:

            --а вот тут мы совершаем сделку

            if tonumber(v_offer)-tonumber(v_bid)>=p_min_spread then

                  count=tonumber(find_limit())/tonumber(p_lost_size)

                  p_file:write(os.date().."   count="..count.."\n")

                  count_buy=p_count-count

                  count_sell=p_count+count

                 

                  if count_buy~=0 and buy_order=="" then

                        trade("B", count_buy, v_bid)

                  end

                  if count_sell~=0 and sell_order=="" then

                        trade("S", count_sell, v_offer)

                  end              

            end

Вставите его в конце OnQuote, но перед концом первого условия (перед оператором end).

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

Что будет, если этот участок кода буде выполнятся повторно? В этом случае у нас номер выставленных заявок, как на покупку, так и на продажу, будет храниться в памяти компьютера. То есть в переменных buy_order и sell_order будет какое-то значение, отличное от пустого. Условие не выполнился и заявка повторно не станет выставляться. За это отвечают те участки программы, которые мы долго и упорно писали на предыдущих уроках.

Если мы заявку удалим, или она у нас полностью исполниться, то переменные buy_order и sell_order  примут пустое значение. Этот код мы так же написали на прошлых уроках. Таким образом, программа сможет выставить новую заявку, если только количество лот, на которое надо выставить заявку, не равно нулю. А нулю оно будет равно в том случае, если мы докупили или допродали до полного объема. Например, купили 5 лот, значит, на покупку больше заявка не может быть выставлена. А на продажу может – нам надо продать 10 лот. Если мы продали один лот и у нас осталось 4 – то мы можем докупиться до пяти лот – компьютер при следующем выполнении кода (когда появятся изменения в стакане) сможет выставить заявку на покупку 1 лота.

Может так оказаться, что по той бумаге, которой мы торгуем, нельзя шортить. Тогда программа выдаст вот такое сообщение:

 

И программа выставит только заявку в лонг. Причем она будет пытаться выставить заявку в шорт при каждом изменении стакана. Можно внести в программу флаг, показывающий, что по данной акции нельзя шортить. Это будет новый параметр — переменная, который надо добавить в начало программы. Назовем его p_can_short:

p_can_short=false -- разрешено ли шортить по данному инструменту

Изменим немного код, отвечающий за торговлю:

            --а вот тут мы совершаем сделку

            if tonumber(v_offer)-tonumber(v_bid)>=p_min_spread then

                  p_file:write(os.date().." Входим в код совершения сделки\n")

                  count=tonumber(find_limit())/tonumber(p_lost_size)

                  p_file:write(os.date().."   count="..count.."\n")

                  count_buy=p_count-count

                  if p_can_short then

                        count_sell=p_count+count

                  else

                        count_sell=count

                  end

                 

                  p_file:write(os.date().."   count_buy="..count_buy.."   count_sell="..count_sell.."   buy_order="..buy_order.."  sell_order="..sell_order.."\n")

                  if count_buy>0 and buy_order=="" then

                        trade("B", count_buy, v_bid)

                  end

                  if count_sell>0 and sell_order=="" then

                        trade("S", count_sell, v_offer)

                  end              

            end

Теперь осталось только написать перевыставление заявок. Как мы это сделаем? Очень просто, запоминаем предыдущие крайние цены, если при очередном изменениии стакана цены поменялись, то просто удаляем выставленные заявки, давая возможность программе их заново выставить.

Для удаления заявки необходимо воспользоваться той же функцией, что и для выставления (sendTransaction), только надо задать немного другие параметры. В частности, параметр ACTION следует указать KILL_ORDER.

Для удаления заявки (по ее номеру) целесообразно  предусмотреть отдельную функцию:

--Удалить заявку

function delete_order(a_num)

      if a_num=="" then

            return

      end

      t = {

                  ["CLASSCODE"]=p_classcode,

                  ["SECCODE"]=p_seccode,

                  ["ACTION"]="KILL_ORDER",

                  ["ACCOUNT"]=p_account,

                  ["CLIENT_CODE"]=p_clientcode,

                  ["TYPE"]="L",

                  ["OPERATION"]=l_oper,

                  ["TRANS_ID"]="1",

                  ["ORDER_KEY"]=tostring(a_num)

            }

      res=sendTransaction(t)

      message("Удаляем заявку: сообщение "..tostring(res).."; номер "..a_num,1)

end

При вызове функции delete_order мы указываем ей только номер заявки. Номер выставленных заявок у нас запоминается. Таким образом, мы можем сделать вот так:

            been_deleted=false

            if last_offer~=0 and last_offer~=v_offer then

                  p_file:write(os.date().."Удаяем ордер на продажу\n")

                  delete_order(sell_order)

                  been_deleted=true

            end

            if last_bid~=0 and last_bid~=v_bid then

                  p_file:write(os.date().."Удаяем ордер на покупку\n")

                  delete_order(buy_order)

                  been_deleted=true

            end

            last_offer=v_offer

            last_bid=v_bid

            if  been_deleted then

                  sleep(100)

                  return

            end

Этот код мы поставим в процедуру OnQuote перед кодом, который выставляет заявки на покупку и на продажу. Как вы помните, сам этот код у нас обернут в условие if, где идет проверка размера спреда. Если спред меньше, чем надо, то выставленные заявки так же нужно удалить.   Поэтому, в этом условии ставим ветку else и добавляем туда вызов функции удаления:

        else

                  delete_order(sell_order)

                  delete_order(buy_order)

        end

Мы ввели новые глобальные переменные last_offer и last_bid. Их нужно объявить в начале программы, но не там где параметры (для удобства, чтобы не смешить переменные), а немного ниже:

last_offer=0

last_bid=0

Итак, простой вариант робота спредера у нас готов. Разумеется, его нельзя назвать полноценным роботом, это всего лишь учебный вариант. Для реальной торговли нужно предусмотреть много нюансов, к которым мы еще вернемся. А пока я заканчиваю тему робота спредера и на следующих уроках мы начнем разбирать другие возможности языка qlua, возможно, напишем при этом другого робота.

Полный текст роботам см. приложение.

Прикрепленные файлы

·   Робот-спредер.rar

Комментарии

thejobber — 27 октября 2014 г.

Добрый день, пытался запустить... уже даже почти всё получилось... только заявка выставляется и сразу же снимается...
мои переменные выглядят следующий образом:

p_classcode="TQBR" --Eia eeanna
p_seccode="LKOH" --Eia eino?oiaioa
p_delta=0.9 -- ionooi io e?aaa noaeaia
p_account="L01+00000F00" --Eeeaioneee n?ao(iiaue ia?aiao?)
p_client_code="10534" -- Eia eeeaioa(iiaue ia?aiao?)
p_count=1 -- neieuee eioaie oi?aoai
p_min_spread=0.3 --ieieiaeuiue nia?a, i?e eioi?ii iu oi?oaai
p_lost_size=1 --?acia? eioa
p_can_short=true -- ?ac?aoaii ee oi?oeou ii aaiiiio eino?oiaioo

и выставляется почему-то 3 лота, вместо 1

0 +

megabax — 28 октября 2014 г.

thejobber, То что выставляет вместо одного три лота, возможно, у вас по этой бумаге стоит позиция в шорт. В этом случае н пытается и шорт закрыть, и купить.
Что касается, что заявки сразу снимаются...попробуйте выставить вручную с теми же параметрами, что он выдаст, какое сообщение? Вы на демо запускаете робота или на реальном счете?

1 +

thejobber — 28 октября 2014 г.

megabax, демо, может подключитесь ко мне через тим вьювер? для операвтиности... а то мы так будем долго дебажить ))

0 +

megabax — 28 октября 2014 г.

thejobber, Напишите мне на емайл, попробуем.

0 +

megabax — 29 октября 2014 г.

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

0 +

thejobber — 29 октября 2014 г.

megabax, потестил ещё немного ... MGNT - таже фигня... ставит и сразу снимает, даже 1 секудна не успевает пройти... IRAO - на много лучше... успевает постоять, даже поторговало немного в плюс на демо... правда когда уже полный круг проходит, снова не ставит, хотя спред при котором нужно торговать и там минимальный выставил 1 п. при реальном спреде в стакане 20п. также в сообщещния всё время пишет что не может снять заявку по которой уже прошла сделка...
понял наконец что такое у вас отступ от краёв стакана... всё ещё въехать не мог почему заявки не самыми лучшими ценами выставляются )

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

0 +

megabax — 30 октября 2014 г.

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

0 +

vasterkin — 28 октября 2014 г.

Добрый день!
Спасибо за огромную работу. Считаю очень полезными ваши уроки.

При запуске скрипта столкнулся с аналогичной проблемой, что и в предыдущем комментарии - после определения спрэда робот ставит заявки и тут же их снимает.

0 +

roborzn — 1 декабря 2014 г.

Добрый день!
Долго искал что-то похожее. Последний раз программировал лет 30 назад.
Как следствие все дается не легко. Уроки четкие и понятные .
Спасибо. Хотелось бы для освоения
1 научиться писать индикаторы.
2 научиться декомпилировать файлы Lua
3 уметь писать робот работающий одновременно на базе двух (и более) индикаторах
научить сможете?

0 +

megabax — 2 декабря 2014 г.

roborzn, по п. 3 думаю да. Насчет 1 и 2 пока не прорабатывал эту тему. П. 2 вообще маловероятно что возможно, lua - это не .NET, их так просто не декомилировать.

0 +

fuckme — 29 апреля 2015 г.

Подскажите, как получить значение "Цена последней сделки"?

0 +

Написать комментарий

Чтобы написать комментарий, необходимо авторизоваться.

Написать администратору