Korssar64
Korssar64 личный блог
11 октября 2021, 12:28

lua quik

Добрый день. Вопрос для программистов на lua. Я начал разбираться с написанием робота. Сам робот на простой ma10. Вход при пробитии закрытия свечи ma(закрытием снизу вверх — лонг, сверху в них — шорт). Выход по стопам, при этом после стопа входить в противоположную сторону по открытию следующего часа. И выход по пробитию закрытия свечи и ma.
1)Не могу понять в какую часть когда записать следующее условие: нужно что бы робот выходил при пробитии закрытия свечи ma и делал переворот.
2)Как прописать условие переворота, по открытию следующего часа, переворота при выбивании стопа.
3)Как прописать перенос сделок через ноч.

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;
   
   
Буду благодарен за помощь.
15 Комментариев
  • Kotcher
    11 октября 2021, 12:50
    Привет!
    1-2)В QtyStopOrder для «SIMPLE_STOP_ORDER» указывай количество контрактов в два раза больше, тогда у тебя будет закрытие открытых позиций и открытие новых. Например, было открыто 3 лота, стоп ставишь 6. По достижении стопа 3 лота закроется и -3 лота откроется.
    3) По поводу переноса через ночь: 'EXPIRY_DATE' = 'GTC' (до отмены). Но тут зависит от брокера: в ВТБ прокатывает, а СБЕР у меня требовал конкретную дату действия приказа. Там просто добавлял дней 10, например
    • Tema ☑️
      11 октября 2021, 12:58
      Kotcher, а чтобы на 10 дней сделать нужно просто GTC поменять на 10?
        • Tema ☑️
          11 октября 2021, 13:18
          Korssar64, у меня так у сбера брокера нельзя до отмены и я заменил GTC на TODAY. А надо бы на несколько дней (до 30 можно), конкретную дату же не будешь каждый день в скрипт лазить и дату вручную выставлять. если че я не программист
  • Кирилл Гудков
    11 октября 2021, 13:55
    Не забудьте этот код протестировать на случае обрыва связи/автоподключения. Только функционал выставления заявок перед экспериментом замените на логирование. Могут произойти веселые вещи, когда роботу перезаливают историю, а он думает, что это текущие котировки.

    Ну и имейте ввиду, что main и все коллбэки — это 2 разных потока. Вот это изменение из main может быть видно в обратном порядке, например:
    trans_status = trans_reply.status;
     trans_number = trans_reply.order_num;
  • GOLD
    11 октября 2021, 15:09
    все алгаши* ползут на кладбище одной дорогой))
    • Кирилл Гудков
      11 октября 2021, 19:21
      $100, ерунду говорите. Дорога на кладбище у каждого своя.
  • Виталий
    11 октября 2021, 18:16
    Не так все просто, в 3 строчках же это не напишешь, надо вам заказывать доработку под ваши задачи
    По уму робот это тысячи строк — с логированием и обработкой ошибок, которых емае сколько, везде есть нюансы
      • Виталий
        12 октября 2021, 12:50
        Korssar64, создайте тему в разделе алготрейдинг, кто-нибудь да откликнется, только учтите на будущее, что при обновлении квика сразу могут перестать работать какие-то части кода… То есть либо не обновляться потом, либо придется снова обращаться по мелочи к программисту.
        Я не возьмусь, т.к. фигню на 100 строчек неустойчивую совесть не даст продать за 10тыс ( а именно такие и продают гуры в инете), а нормальная устойчивая прога стоит больше, вам будет дорого моя цена
  • Винни Пух
    11 октября 2021, 21:54

    1)Не могу понять в какую часть когда записать следующее условие: нужно что бы робот выходил при пробитии закрытия свечи ma и делал переворот.

    — суть вопроса остается неясной. Для полной версии нужно отслеживать состояние сделок (в позе/без позы) и в зависимости от этого уже плясать. Логировать/считывать можно с помощью io.open/write через .csv файл

    2)Как прописать условие переворота, по открытию следующего часа, переворота при выбивании стопа.

    — переворот по открытию следующего часа решается запросом времени сервера и дальнейшей работы с ним (например " getInfoParam(«SERVERTIME») ")

    — переворот при выбивании стопа не нужен, т.к. стоп выбило и за ним следует просто вход в сделку по условию. В остальных случаях колбэки/чеки по number/id

    3)Как прописать перенос сделок через ноч.

    — самостоятельно в заявке указывать дату/GTC удержания заявки в строке EXPIRY_DATE

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн