Блог им. AleksandrTrubnikov
Account = ""; -- счет клиента ClientCode = ""; -- код клиента FirmCode = ""; -- код фирмы SecCode = ""; -- код инструмента ClassCode = ""; -- класс инструмента Tag = SecCode .. "_PRICE"; -- индикатор графика цены EMA1 = SecCode .. "_EMA1" EMA2 = SecCode .. "_EMA2" Run = true; -- отвечает за работу скрипта LastOpenBarIndex = 0; FlagOrder = false; -- флаг наличия активной заявки FlagStopOrder = false; OrderNumber = 0; -- номер заявки trans_id = os.time(); -- уникальный id транзакции trans_number = 0; -- номер транзакции trans_status = 0; -- статус транзакции ClosePosition = true; Count = 0; Cycles = 60; Rest = 1; -- остаток в заявке Start = 65955; -- начало работы скрипта Stop = 233000; -- конец работы скрипта StopLossl = 350; -- размер стоп-лосса StopLosss = 405; -- размер стоп-лосса TakeProfit = 3.8/100 Step = 1; -- шаг цены Slip = 0; -- проскальзывание цены Unit = 10; -- количество лотов в заявке -- Главная функция скрипта -- function main() -- Основной цикл скрипта -- while Run do if IsWindowClosed(t_id) then -- при закрытие окна торгового робота скрипт останавливается!!! Run = false; message("Stop trading robot!!!"); end; if not_trade() then file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "r"); if file ~= nil then LastOpenBarIndex = tonumber(file:read("*n")); file:close(); end; if LastOpenBarIndex == nil then LastOpenBarIndex = 0 end; if FlagOrder then done_order() elseif FlagStopOrder then stop_order() elseif LastOpenBarIndex < getNumCandles(Tag) then Predict = predict() -- получаем прогноз Position = position() -- узнаем позицию по инструменту if Predict > 0 and Position <= 0 then if Position < 0 then QtyBuy = math.abs(Position) + Unit; elseif Position == 0 then QtyBuy = Unit; end; buy(QtyBuy) -- покупаем elseif Predict < 0 and Position >= 0 then if Position > 0 then QtySell = Position + Unit; elseif Position == 0 then QtySell = Unit; end; sell(QtySell) -- продаем end; LastOpenBarIndex = getNumCandles(Tag); file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "w"); if file ~= nil then file:write(tostring(LastOpenBarIndex)); file:close(); end; end; -- вывод параметров скрипта в таблицу -- SetCell(t_id, 1, 1, tostring(getInfoParam("SERVERTIME"))); SetCell(t_id, 1, 2, SecCode); SetCell(t_id, 1, 3, tostring(position())); SetCell(t_id, 1, 4, tostring(round(tonumber(predict()), 10))); SetCell(t_id, 1, 5, tostring(round(tonumber(getPortfolioInfo(FirmCode, ClientCode).total_limit_open_pos), 2))); SetCell(t_id, 1, 6, tostring(round(tonumber(getPortfolioInfo(FirmCode, ClientCode).varmargin), 2))); sleep(1000); else sleep(1000); end; end; end; function not_trade() local Error = true; if isConnected() == 1 then local Time = 0; SERVER_TIME = getInfoParam("SERVERTIME"); if string.len(SERVER_TIME) == 8 then Time = tonumber(SERVER_TIME:sub (1,2) .. SERVER_TIME:sub (4,5) .. SERVER_TIME:sub (7,8)); else Time = tonumber(SERVER_TIME:sub (1,1) .. SERVER_TIME:sub (3,4) .. SERVER_TIME:sub (6,7)); end; if Time == nil then Time = 0 end; if Time < Start then Error = false; SetCell(t_id, 1, 1, "ErrorTime"); for i = 2,6 do SetCell(t_id, 1, i, "0"); end; elseif Time > 135900 and Time < 140600 then Error = false; SetCell(t_id, 1, 1, "Clearing"); for i = 2,6 do SetCell(t_id, 1, i, "0"); end; elseif Time > 184400 and Time < 190600 then Error = false; SetCell(t_id, 1, 1, "Clearing"); for i = 2,6 do SetCell(t_id, 1, i, "0"); end; elseif Time > Stop then Error = false; SetCell(t_id, 1, 1, "ErrorTime"); for i = 2,6 do SetCell(t_id, 1, i, "0"); end; -- закрытие позиции и снятие стоп-заявки в конце торговой сессии -- --[[local Position = position(); if Position ~= 0 then if ClosePosition then if Position > 0 then Slip = 0; sell(Position); elseif Position < 0 then Slip = 0; buy(math.abs(Position)); end; FlagOrder = false; ClosePosition = false; end; elseif Position == 0 then stop_order() ClosePosition = true; end;]] end; else Error = false; SetCell(t_id, 1, 1, "ErrorConnection"); for i = 2,6 do SetCell(t_id, 1, i, "0"); end; end; return Error; end; -- Функция покупки -- function buy(QtyBuy) local PriceBuy = 0; -- цена по которой покупаем while PriceBuy <= 0 do PriceBuy = tonumber(getParamEx(ClassCode, SecCode, "offer").param_value) - Step * Slip -- получаем лучшую цену предложения end; message("Order to buy at the price: " .. PriceBuy .. ", qty: " .. QtyBuy); trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "NEW_ORDER", ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['OPERATION'] = "B", ['TYPE'] = "M", ['QUANTITY'] = tostring (math.floor(QtyBuy)), ['ACCOUNT'] = Account, ['PRICE'] = tostring(math.floor(PriceBuy)), ['CLIENT_CODE'] = ClientCode } trans_status = 0; trans_number = 0; sendTransaction(trans_params) while trans_status ~= 3 do sleep(20); end; OrderNumber = trans_number; FlagOrder = true; end; -- Функция продажи -- function sell(QtySell) local PriceSell = 0; -- цена по которой продаем while PriceSell <= 0 do PriceSell = tonumber(getParamEx(ClassCode, SecCode, "bid").param_value) - Step * Slip -- получаем лучшую цену спроса end; message("Order to sell at the price: " .. PriceSell .. ", in the quantity: " .. QtySell); trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "NEW_ORDER", ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['OPERATION'] = "S", ['TYPE'] = "M", ['QUANTITY'] = tostring (math.floor(QtySell)), ['ACCOUNT'] = Account, ['PRICE'] = tostring(math.floor(PriceSell)), ['CLIENT_CODE'] = ClientCode } trans_status = 0; trans_number = 0; sendTransaction(trans_params) while trans_status ~= 3 do sleep(20); end; OrderNumber = trans_number; FlagOrder = true; end; -- Функция контроля исполнения активной заявки -- function done_order() local QtyOrder = 0; local PriceOrder = 0; for i = 0, getNumberOf("orders") - 1 do if getItem("orders", i).order_num == OrderNumber then Rest = getItem("orders", i).balance; while PriceOrder <= 0 do PriceOrder = getItem("orders", i).price; QtyOrder = getItem("orders", i).qty; sleep(20); end; end; end; -- если заявка полностью исполнилась -- if Rest == 0 then message(getInfoParam("SERVERTIME") .. " Order №" .. OrderNumber .. " fully executed, at the price without slipping: " .. PriceOrder .. " in quantity: " .. QtyOrder, 1) FlagOrder = false; FlagStopOrder = true; Count = 0; -- если заявка не исполнилась, снимаем отстаток -- elseif Count > Cycles then kill_order(OrderNumber); message(getInfoParam("SERVERTIME") .. " Order №" .. OrderNumber .. " canceled, rest: " .. Rest, 1) Count = 0; FlagOrder = false; if position() ~= 0 then FlagStopOrder = true; end; file = io.open(getScriptPath() .. "/LastOpenBarIndex.txt", "w"); file:write(""); file:close(); -- если заявка еще не исполнилась, но время на исполнение еще осталось -- elseif Rest > 0 then Count = Count + 1; end; sleep(1000); end; -- Функция выставления stop_loss -- function stop_order() local PriceStopOrder = 0; local Position = position(); -- снимаем старую стоп-заявку -- for i = 0, getNumberOf("stop_orders") - 1 do if bit.test(getItem("stop_orders", i).flags, 0) == true then kill_stop_order(getItem("stop_orders", i).order_num); end; end; -- определяем цену по которой заключена сделка -- if Position ~= 0 then for i = 0, getNumberOf("trades") - 1 do if getItem("trades", i).order_num == OrderNumber then while PriceStopOrder <= 0 do PriceStopOrder = getItem("trades", i).price; sleep(20); end; end; end; end; if PriceStopOrder > 0 and Position ~= 0 then if Position > 0 then StopPrice = PriceStopOrder - StopLossl; PriceX = PriceStopOrder - Step * 200; QtyStopOrder = Position; Operation = "S"; elseif Position < 0 then StopPrice = PriceStopOrder + Step * StopLosss; PriceX = PriceStopOrder + Step * 200; QtyStopOrder = math.abs(Position); Operation = "B"; end; trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "NEW_STOP_ORDER", ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER", ['EXPIRY_DATE'] = "TODAY", ['STOPPRICE'] = tostring(math.floor(StopPrice)), ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['PRICE'] = tostring(math.floor(PriceX)), ['OPERATION'] = Operation, ['TYPE'] = "L", ['QUANTITY'] = tostring(math.floor(QtyStopOrder)), ['ACCOUNT'] = Account, ['CLIENT_CODE'] = ClientCode } trans_status = 0; -- сбрасываем прошлый статус транзакции sendTransaction(trans_params); -- отправляем транзакцию while trans_status ~= 3 do sleep(20); end; if Position > 0 then StopPrice = PriceStopOrder - StopLossl; PriceX = PriceStopOrder - Step * 200; QtyStopOrder = Position; Operation = "S"; elseif Position < 0 then StopPrice = PriceStopOrder + Step * StopLosss; PriceX = PriceStopOrder + Step * 200; QtyStopOrder = math.abs(Position); Operation = "B"; end; trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "NEW_STOP_ORDER", ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER", ['EXPIRY_DATE'] = "TODAY", ['STOPPRICE'] = tostring(math.floor(StopPrice)), ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['PRICE'] = tostring(math.floor(PriceX)), ['OPERATION'] = Operation, ['TYPE'] = "L", ['QUANTITY'] = tostring(math.floor(QtyStopOrder)), ['ACCOUNT'] = Account, ['CLIENT_CODE'] = ClientCode } trans_status = 0; -- сбрасываем прошлый статус транзакции sendTransaction(trans_params); -- отправляем транзакцию while trans_status ~= 3 do sleep(20); end; --[[]] if Position > 0 then StopPrice = PriceStopOrder *(TakeProfit+1) ; PriceX = PriceStopOrder - Step * 200; QtyStopOrder = Position; Operation = "S"; elseif Position < 0 then StopPrice = PriceStopOrder *(1-TakeProfit); PriceX = PriceStopOrder + Step * 200; QtyStopOrder = math.abs(Position); Operation = "B"; end; trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "NEW_STOP_ORDER", ['STOP_ORDER_KIND'] = "SIMPLE_STOP_ORDER", ['EXPIRY_DATE'] = "TODAY", ['STOPPRICE'] = tostring(math.floor(StopPrice)), ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['PRICE'] = tostring(math.floor(PriceX)), ['OPERATION'] = Operation, ['TYPE'] = "L", ['QUANTITY'] = tostring(math.floor(QtyStopOrder)), ['ACCOUNT'] = Account, ['CLIENT_CODE'] = ClientCode } trans_status = 0; -- сбрасываем прошлый статус транзакции sendTransaction(trans_params); -- отправляем транзакцию while trans_status ~= 3 do sleep(20); end; FlagStopOrder = false; end; end; -- Функция снятия заявки -- function kill_order(Number) trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "KILL_ORDER", ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['ACCOUNT'] = Account, ['ORDER_KEY'] = tostring(Number) } sendTransaction(trans_params); end; -- Функция снятия стоп-заявки -- function kill_stop_order(Number) trans_id = trans_id + 1; local trans_params = { ['TRANS_ID'] = tostring(trans_id), ['ACTION'] = "KILL_STOP_ORDER", ['CLASSCODE'] = ClassCode, ['SECCODE'] = SecCode, ['ACCOUNT'] = Account, ['STOP_ORDER_KEY'] = tostring(Number) } sendTransaction(trans_params); end; -- Функция прогнозирования движения цены инструмента -- function predict() local t, n, l = nil,nil,nil while t == nil do x1 = getNumCandles('Tag') t, n, l = getCandlesByIndex('Tag', 0, 0, x1) ma_1 = t[0].close for i=1, x1-1 do local C = t[i].close local C1 = t[i-1].close local C2 = t[i-2].close local C3 = t[i-3].close local C4 = t[i-4].close local C5 = t[i-5].close local C6 = t[i-6].close local C7 = t[i-7].close local C8 = t[i-8].close local C9 = t[i-9].close ma_2 = (C1+C2+C3+C4+C5+C6+C7+C8+C9+C)/10 ma_1 = ma_2 end -- sleep(10) end; local ema2 = ma_1; local ema1 = t[x1].close local result if ema1 > ema2 then result = 1; elseif ema1 < ema2 then result = -1 end; return result; end; -- Функция определния позиции по инструменту -- function position() local res = 0; for i = 0, getNumberOf("futures_client_holding") - 1 do if getItem("futures_client_holding", i).sec_code == SecCode then res = tonumber(getItem("futures_client_holding", i).totalnet); end; end; return res; end; -- Функция обратного вызова запускается первой и создает таблицу торгового робота -- function OnInit() message("Start trading robot!"); t_id = AllocTable(); -- создаем таблицу AddColumn (t_id,1,"Time", true, QTABLE_TIME_TYPE, 15); AddColumn (t_id,2,"Sec_Code", true, QTABLE_STRING_TYPE, 10); AddColumn (t_id,3,"Position", true, QTABLE_STRING_TYPE, 10); AddColumn (t_id,4,"Predict", true, QTABLE_STRING_TYPE, 15); AddColumn (t_id,5,"Balance", true, QTABLE_STRING_TYPE, 10); AddColumn (t_id,6,"Margin", true, QTABLE_STRING_TYPE, 10); CreateWindow(t_id); -- создаем окно таблицы SetWindowCaption(t_id, "Trading robot"); InsertRow(t_id, 1); end; -- Функция обратного вызова для получения результата отправки транзакции -- function OnTransReply(trans_reply) if trans_reply.trans_id == trans_id then trans_status = trans_reply.status; trans_number = trans_reply.order_num; end; end; -- Функция обратного вызова остановки скрипта -- function OnStop() Run = false; DestroyTable(t_id); end; -- Функция округления чисел -- function round(x, n) n = math.pow(10, n or 0) x = x * n if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end; return x / n end;Буду благодарен за помощь.
1-2)В QtyStopOrder для «SIMPLE_STOP_ORDER» указывай количество контрактов в два раза больше, тогда у тебя будет закрытие открытых позиций и открытие новых. Например, было открыто 3 лота, стоп ставишь 6. По достижении стопа 3 лота закроется и -3 лота откроется.
3) По поводу переноса через ночь: 'EXPIRY_DATE' = 'GTC' (до отмены). Но тут зависит от брокера: в ВТБ прокатывает, а СБЕР у меня требовал конкретную дату действия приказа. Там просто добавлял дней 10, например
1)нужно где-то прописать отслеживание самого индикаторы и выход из позы, если закрытие свечи пробивает ma
2)Это сработало бы при мгновенном перевороте, а мне нужно что бы выбился стоп. А вход(переворот) в другую сторону проходил по открытию следующего часа
3)в таком случае мне нужно руками каждый день прописывать статус робота. Я слышал, что есть базы банных(или внутренние библиотеки ), в которые можно записывать все сделки, что бы на утро все роботы торговали «свои контракты».
Ну и имейте ввиду, что main и все коллбэки — это 2 разных потока. Вот это изменение из main может быть видно в обратном порядке, например:
По уму робот это тысячи строк — с логированием и обработкой ошибок, которых емае сколько, везде есть нюансы
Я не возьмусь, т.к. фигню на 100 строчек неустойчивую совесть не даст продать за 10тыс ( а именно такие и продают гуры в инете), а нормальная устойчивая прога стоит больше, вам будет дорого моя цена
1)Не могу понять в какую часть когда записать следующее условие: нужно что бы робот выходил при пробитии закрытия свечи ma и делал переворот.
— суть вопроса остается неясной. Для полной версии нужно отслеживать состояние сделок (в позе/без позы) и в зависимости от этого уже плясать. Логировать/считывать можно с помощью io.open/write через .csv файл
2)Как прописать условие переворота, по открытию следующего часа, переворота при выбивании стопа.
— переворот по открытию следующего часа решается запросом времени сервера и дальнейшей работы с ним (например " getInfoParam(«SERVERTIME») ")
— переворот при выбивании стопа не нужен, т.к. стоп выбило и за ним следует просто вход в сделку по условию. В остальных случаях колбэки/чеки по number/id
3)Как прописать перенос сделок через ноч.
— самостоятельно в заявке указывать дату/GTC удержания заявки в строке EXPIRY_DATE