Когда передо мной встала задача удаления поставленного стоп-ордера, наткнулся в интернете на скудность информации по данной тематике.
Самая распространенная ошибка начинающего программиста отправка в SendTransaction в STOP_ORDER_KEY trans_id стоп-ордера
Робот выставляет стоп-заявку на покупку по определенной цене, затем через 2 секунды снимает её.
Также в коде имеются следующие фишки:
- Запись удобочитаемого лог-файла. Записи с интервалом <=1 сек. группируются в пул. Между пулами - пустая строка. После остановки скрипта в файл добавляется двойная линия.
- Функция преобразования числа в строку с удалением точки и нулей правее нее для отправки этой строки в SendTransaction
- Функция, возвращающая Entry или Exit в зависимости от trans_id принадлежности транзакций к входу или выходу
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | --/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/ 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 |
--/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/ 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
В итоге получается текстовый лог-файл, удобный для чтения человеком, с которого можно считать данные программой (например на 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;
======================================================================================================================
======================================================================================================================
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!