Блог им. ram555
Когда передо мной встала задача удаления поставленного стоп-ордера, наткнулся в интернете на скудность информации по данной тематике.
Самая распространенная ошибка начинающего программиста отправка в SendTransaction в STOP_ORDER_KEY trans_id стоп-ордера
Робот выставляет стоп-заявку на покупку по определенной цене, затем через 2 секунды снимает её.
Также в коде имеются следующие фишки:
[Начало: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]
Робот соединения интернета?
Можно проще использовать. Много строк
Робот при восстановлении соединения без проблем снимает поставленные ранее стоп-заявки. Номер заявки храниться в переменной 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 ;
Один пакет запуск соединения модема и квика и соединиться.было бы хорошо!
Ну робота не ВКЛ.аТО понаделает делов.
Надо написать робота «снятие лишних стопзаявок».если робот выключен заявки робот перед выкл. снимает заявки свои
1. Насчет восстановления соединения с интернетом. Собственный сервер или компьютер 24/7 — плохая идея, не обеспечит бесперебойность интернета, питания. Раньше арендовал и буду арендовать сервер, там инет и сам серв падает пару раз в год. Можно настроить винду так, чтоб она сама подключалась к инету. QUIK в автозапуск. Для соединения с брокером с вводом пароля и логина имеется скрипт на lua:http://o-s-a.net/posts/avtorizaciya.html
Если поставлен стоп-ордер на выход из существующей позиции, при разрыве соединения или отключении сервера, stop-loss останется стоять у брокера, таким образом, позиция защищена. Если поставлен стоп-ордер на вход, и произошел разрыв — можем подучить незащищенную позицию.
2. писать в лог смысла нет: скрипт туда только дописывает свою инфу для пользователя, не читает
3. снять стоп-заявки при выключении робота очень просто: