Блог им. 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. снять стоп-заявки при выключении робота очень просто:
[Начало:LUA]<br>-- функция вызывается терминалом ТЕРМИНАЛОМ QUIK при остановке скрипта<br>function OnStop()<br>DeleteStopOrder(g_stopOrderExit_num)<br>myLog("Script Stoped") <br>f:close() -- Закрывает файл<br>isRun = false<br>end