Блог им. ram555

ПОСТАНОВКА И СНЯТИЕ STOP-ОРДЕРА В QLUA(LUA)

Когда передо мной встала задача удаления поставленного стоп-ордера, наткнулся в интернете на скудность информации по данной тематике.

Самая распространенная ошибка начинающего программиста отправка в SendTransaction в STOP_ORDER_KEY  trans_id стоп-ордера

Робот выставляет стоп-заявку на покупку по определенной цене, затем через 2 секунды снимает её.

Также в коде имеются следующие фишки:

  • Запись удобочитаемого лог-файла.  Записи с интервалом <=1 сек. группируются в пул. Между пулами — пустая строка. После остановки скрипта в файл добавляется двойная линия.
  • Функция преобразования числа в строку с удалением точки и нулей правее нее для отправки этой строки в SendTransaction
  • Функция, возвращающая Entry или Exit в зависимости от trans_id принадлежности транзакций к входу или выходу
[Начало:LUA]
--/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/
NAME_OF_STRATEGY = 'Str1_' -- НАЗВАНИЕ СТРАТЕГИИ (не более 9 символов!)
CLASS_CODE = "QJSIM" -- Код класса SPBFUT
SEC_CODE = "SBER" -- Код бумаги SiZ6 SiH7, SiM7, SiU7
ACCOUNT = "NL0011100043" -- Идентификатор счета SPBFUT00355
CLIENT_CODE = NAME_OF_STRATEGY..SEC_CODE -- "Код клиента"
QTY_LOTS = "1" -- Кол-во торгуемых лотов
FILE_LOG_NAME = "C:\\TRADING\\QUIK Junior\\Scripts\\Log.txt" -- ИМЯ ЛОГ-ФАЙЛА
 
--/*РАБОЧИЕ ПЕРЕМЕННЫЕ РОБОТА (менять не нужно)*/
g_price_step = 0 -- ШАГ ЦЕНЫ ИНСТРУМЕНТА
g_trans_id_entry = 110001 -- Задает начальный номер ID транзакций на вход
g_trans_id_exit = 220001 -- Задает начальный номер ID транзакций на выход
g_arrTransId_entry = {} -- массив ID транзакций на вход
g_arrTransId_exit = {} -- массив ID транзакций на выход
g_transId_del_order = "1234" -- ID ордера на удаление заявки (не меняется)
g_transId_del_stopOrder = "6789" -- ID ордера на удаление стоп заявки (не меняется)
g_currentPosition = 0 -- в позиции? сколько лотов и какое направление
g_IsTrallingStop = false -- выставлен ли трейлинг стоп на сервере
g_stopOrderEntry_num= "" -- номер стоп-заявки на вход в системе, по которому её можно снять
g_stopOrderExit_num = "" -- номер стоп-заявки на выход в системе, по которому её можно снять
g_order_num = "" -- номер заявки в системе, по которому её можно снять
g_oldTrade_num = "" -- номер предыдущей обработанной сделки
g_previous_time = os.time() -- помещение в переменную времени сервера в формате HHMMSS 
isRun = true -- Флаг поддержания работы бесконечного цикла в main

function OnInit()
   -- Получает ШАГ ЦЕНЫ ИНСТРУМЕНТА
    g_price_step = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value
 
    f = io.open(FILE_LOG_NAME, "a+") -- открывает файл 
    myLog("Initialization finished")
end
function main()
   g_trans_id_exit = g_trans_id_exit + 1
   g_arrTransId_exit[#g_arrTransId_exit+1] = g_trans_id_exit
 
   SendStopOrder("131.9", QTY_LOTS, "B", g_trans_id_exit) -- Отправить стоп ордер
   sleep(2000)                                            -- через 2 секунды
   DeleteStopOrder(g_stopOrderExit_num)                   -- удалить стоп-ордер
 
   while isRun do
      sleep(5000) -- обрабатываем цикл с задержкой 5сек. 
   end
end

-- функция вызывается терминалом ТЕРМИНАЛОМ QUIK при остановке скрипта
function OnStop()
   myLog("Script Stoped") 
   f:close() -- Закрывает файл
   isRun = false
end

function SendStopOrder(stopPrice, quantity, operation, trans_id)
   local offset=50 -- отступ для гарантированного исполнения ордера по рынку (в кол-ве шагов цены)
   local price
   local direction
 
   if operation=="B" then
      price = stopPrice + g_price_step*offset 
      direction = "5" -- Направленность стоп-цены. «5» - больше или равно
   else 
      price = stopPrice - g_price_step*offset 
      direction = "4" -- Направленность стоп-цены. «4» - меньше или равно
   end
   --message("stopPrice"..stopPrice)
   --Пошлем стоп заявку
   local Transaction = {
                       ['ACTION'] = "NEW_STOP_ORDER", 
                       ['PRICE'] = tostring(price), 
                       ['EXPIRY_DATE'] = "TODAY",--"GTC", -- на учебном серве только стоп-заявки с истечением сегодня, потом поменять на GTC
                       ['STOPPRICE'] = tostring(stopPrice),
                       ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
                       ['TRANS_ID'] = removeZero(tostring(trans_id)),
                       ['CLASSCODE'] = CLASS_CODE,
                       ['SECCODE'] = SEC_CODE,
                       ['ACCOUNT'] = ACCOUNT,
                       ['CLIENT_CODE'] = CLIENT_CODE, -- Комментарий к транзакции, который будет виден в транзакциях, заявках и сделках 
                       ['TYPE'] = "L",
                       ['OPERATION'] = tostring(operation),
                       ['CONDITION'] = direction, -- Направленность стоп-цены. Возможные значения: «4» - меньше или равно, «5» – больше или равно
                       ['QUANTITY'] = tostring(math.abs(quantity))
                       }
   local res = sendTransaction(Transaction)
   if string.len(res) ~= 0 then
      message('Error: '..res, 3)
      myLog("SendStopOrder(): Error "..res)
   else
      myLog("SendStopOrder(): "..EntryOrExit(trans_id).."; trans_id="..trans_id)
   end
end

function DeleteStopOrder(stopOrder_num)
   local Transaction = {
                       ['ACTION'] = "KILL_STOP_ORDER", 
                       ['CLASSCODE'] = CLASS_CODE,
                       ['SECCODE'] = SEC_CODE,
                       ['ACCOUNT'] = ACCOUNT,
                       ['CLIENT_CODE'] = CLIENT_CODE,
                       ['TYPE'] = "L",
                       ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER",
                       ['TRANS_ID'] = g_transId_del_order, -- ID УДАЛЯЮЩЕЙ транзакции
                       ['STOP_ORDER_KEY'] = tostring(stopOrder_num)
                       }
 
   local res = sendTransaction(Transaction)
   if string.len(res) ~= 0 then
      message('Error: '..res, 3)
      myLog("DeleteStopOrder(): fail "..res)
   else
      myLog("DeleteStopOrder(): "..stopOrder_num.." success ")
   end 
end
-- создан/изменен/сработал стоп-ордер 
function OnStopOrder(stopOrder)
   -- Если не относится к роботу, выходит из функции
   if stopOrder.brokerref:find(CLIENT_CODE) == nil then return end

   local string state="_" -- состояние заявки
   --бит 0 (0x1) Заявка активна, иначе не активна
   if bit.band(stopOrder.flags,0x1)==0x1 then
      state="стоп-заявка создана"
      if EntryOrExit(stopOrder.trans_id) == "Entry" then
         g_stopOrderEntry_num = stopOrder.order_num 
      end
      if EntryOrExit(stopOrder.trans_id) == "Exit" then
         g_stopOrderExit_num = stopOrder.order_num
      end 
   end
   if bit.band(stopOrder.flags,0x2)==0x1 or stopOrder.flags==26 then
      state="стоп-заявка снята"
   end
   if bit.band(stopOrder.flags,0x2)==0x0 and bit.band(stopOrder.flags,0x1)==0x0 then
      state="стоп-ордер исполнен"
   end
   if bit.band(stopOrder.flags,0x400)==0x1 then
      state="стоп-заявка сработала, но была отвергнута торговой системой"
   end
   if bit.band(stopOrder.flags,0x800)==0x1 then
      state="стоп-заявка сработала, но не прошла контроль лимитов"
   end
   if state=="_" then
      state="Набор битовых флагов="..tostring(stopOrder.flags)
   end
   myLog("OnStopOrder(): sec_code="..stopOrder.sec_code.."; "..EntryOrExit(stopOrder.trans_id)..";\t"..state..
         "; condition_price="..stopOrder.condition_price.."; transID="..stopOrder.trans_id.."; order_num="..stopOrder.order_num ) 
end
------------------------- Сервисные функции--------------------
-- функция записывает в лог строчку с временем и датой 
function myLog(str)
   if f==nil then return end

   local current_time=os.time()--tonumber(timeformat(getInfoParam("SERVERTIME"))) -- помещене в переменную времени сервера в формате HHMMSS 
   if (current_time-g_previous_time)>1 then -- если текущая запись произошла позже 1 секунды, чем предыдущая
      f:write("\n") -- добавляем пустую строку для удобства чтения
   end
   g_previous_time = current_time 
 
   f:write(os.date().."; ".. str .. ";\n")
 
   if str:find("Script Stoped") ~= nil then 
      f:write("======================================================================================================================\n\n")
      f:write("======================================================================================================================\n")
   end
   f:flush() -- Сохраняет изменения в файле
end
-- удаление точки и нулей после нее
function removeZero(str)
   while (string.sub(str,-1) == "0" and str ~= "0") do
      str = string.sub(str,1,-2)
   end
   if (string.sub(str,-1) == ".") then 
      str = string.sub(str,1,-2)
   end 
   return str
end
-- Возвращает Entry или Exit в зависимости от trans_id
function EntryOrExit(trans_id)
   if isArrayContain(g_arrTransId_entry, trans_id) then 
      return "Entry"
   elseif isArrayContain(g_arrTransId_exit, trans_id) then 
      return "Exit"
   elseif trans_id==g_transId_del_order then
      return "DeleteOrder"
   elseif trans_id==g_transId_del_stopOrder then
      return "DeleteStopOrder"
   else
      return "NoId"
   end
end

function isArrayContain(array, value)
   if #array < 1 then return end
      for i=1, #array do
         if array[i]==value then
         return true
      end
   end
   return false
end
[Конец:LUA]

В итоге получается текстовый  лог-файл, удобный для чтения человеком, с которого можно считать данные программой (например на  C#) и подбить статистику:

01/17/17 19:34:56; Initialization finished;
01/17/17 19:34:56; SendStopOrder(): Exit; trans_id=220002;
01/17/17 19:34:56; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка создана; condition_price=131.9; transID=220002; order_num=7902164;

01/17/17 19:34:58; DeleteStopOrder(): 7902164 success ;
01/17/17 19:34:58; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка снята; condition_price=131.9; transID=220002; order_num=7902164;

01/17/17 19:35:01; Script Stoped;
======================================================================================================================

======================================================================================================================
[Конец: Log.txt]

 

  • Ключевые слова:
  • qlua
★16
7 комментариев
Снятие оставшихся стопов из-за разрыва соединения как решаешь?
Робот соединения интернета?
Можно проще использовать. Много строк
avatar
Less, Less, QUIK может сам восстанавливать соединение. Система-> Соединения...->Восстанавливать соединение автоматически...
Робот при восстановлении соединения без проблем снимает поставленные ранее стоп-заявки. Номер заявки храниться в переменной g_stopOrderExit_num и пропадет только после остановки скрипта или квика. снятие стоп-заявки

Log:
02/17/17 20:05:21; Initialization finished;
02/17/17 20:05:21; SendStopOrder(): Exit; trans_id=220002;
02/17/17 20:05:21; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка создана; condition_price=132.5; transID=220002; order_num=7932588;

02/17/17 20:05:33; OnDisconnected();

02/17/17 20:06:11; DeleteStopOrder(): fail Not connected;

02/17/17 20:06:54; OnDisconnected();

02/17/17 20:07:01; DeleteStopOrder(): fail Not connected;

02/17/17 20:07:51; DeleteStopOrder(): fail Not connected;

02/17/17 20:07:59; OnConnected();
02/17/17 20:08:00; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка создана; condition_price=132.5; transID=220002; order_num=7932588;

02/17/17 20:08:41; DeleteStopOrder(): 7932588 success ;
02/17/17 20:08:41; OnStopOrder(): sec_code=SBER; Exit; стоп-заявка снята; condition_price=132.5; transID=220002; order_num=7932588;

02/17/17 20:09:31; DeleteStopOrder(): 7932588 success ;

 
avatar
Роман, это понятно. Соединение модема мегафона? а потом это соединение. Ещо есть запуск квика где то читал.
Один пакет запуск соединения модема и квика и соединиться.было бы хорошо!
Ну робота не ВКЛ.аТО понаделает делов.
avatar
Роман, на автостопы в интере стопами завалиыает.если писать в лог не завалит? найдёт и снимет?
Надо написать робота «снятие лишних стопзаявок».если робот выключен заявки робот перед выкл. снимает заявки свои
avatar
Less, не все понял. Отвечу на те вопросы, которые понял. 
1. Насчет восстановления соединения с интернетом. Собственный сервер или компьютер 24/7 — плохая идея, не обеспечит бесперебойность интернета, питания. Раньше арендовал и буду арендовать сервер, там инет и сам серв падает пару раз в год. Можно настроить винду так, чтоб она сама подключалась к инету. QUIK в автозапуск. Для соединения с брокером с вводом пароля и логина имеется скрипт на lua:http://o-s-a.net/posts/avtorizaciya.html
Если поставлен стоп-ордер на выход из существующей позиции, при разрыве соединения или отключении сервера, stop-loss останется стоять у брокера, таким образом, позиция защищена. Если поставлен стоп-ордер на вход, и произошел разрыв — можем подучить незащищенную позицию.
2. писать в лог смысла нет: скрипт туда только дописывает свою инфу для пользователя, не читает 
3. снять стоп-заявки при выключении робота очень просто:
[Начало:LUA]<br>-- функция вызывается терминалом ТЕРМИНАЛОМ QUIK при остановке скрипта<br>function OnStop()<br>DeleteStopOrder(g_stopOrderExit_num)<br>myLog("Script Stoped") <br>f:close()  -- Закрывает файл<br>isRun = false<br>end

 

avatar
Пишите ещё
avatar

теги блога Роман

....все тэги



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