Close By. мт5 - страница 2

 
Andrey Barinov:
Я уже проверил :)
Проверил на каком типе счёта?
 
Alexey Viktorov:
Проверил на каком типе счёта?
CloseBy только на hedge счетах возможен. Проверял на сервере MetaQuotes Demo
 

итак, представляю на суд новую функцию. Всё сортирует и закрывает сначала большие объёмы.

bool fnCloseBy()
   {//void
      struct stBegin{
         string               name;
         ulong                tkt;
         double               lot;  
         ENUM_POSITION_TYPE   ptype;
      };
      struct stSide{
         stBegin Buy;
         stBegin Sell;
      };
      stSide   pos[];      //полностью отсортированный массив по символам и по объёмам
      stBegin  temp[];     //промежуточный массив, нужен для сортировки
      
/*//////////////////////////////////////////////////////////////////////////////////////////////////////
      Создаём массив открытых позиций. Позиции по которым запрещена торговля или есть
      иные ограничения сразу пропускаем
//////////////////////////////////////////////////////////////////////////////////////////////////////*/
      while(!IsStopped()){//while 1.      Крутим в цикле пока не останется встречных ордеров
     
      ArrayResize(temp,0);   
      
      for(int i=PositionsTotal()-1;i>=0;i--){
        
         string smb=PositionGetSymbol(i);
         //if (!lcSymbolChekLite(smb)) continue;   //проверяем символ на ограничения
         int cnt=ArrayRange(temp,0);
         if (ArrayResize(temp,cnt+1)==-1) continue;
         
         temp[cnt].name=smb;
         temp[cnt].lot=NormalizeDouble(PositionGetDouble(POSITION_VOLUME),2);//заменить нормализацию на функцию автоопределения знаков в лотах
         temp[cnt].tkt=(int)PositionGetInteger(POSITION_TICKET);
         temp[cnt].ptype=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      }
      
/*//////////////////////////////////////////////////////////////////////////////////////////////////////
      Соединяем противоположные позиции по символу. Если позиция по символу одна, то её пропускаем
      Если по символу есть позиции только в одну сторону то их тоже пропускаем
//////////////////////////////////////////////////////////////////////////////////////////////////////*/      

      ArrayResize(pos,0);      
      for(int i=ArraySize(temp)-1;i>0;i--){// i
      
         if (temp[i].name=="") continue;              //одиночные или обработанные позиции пропустим сразу
         int b=0,s=0;                                 //индексы со стороны бай и селл
         int cnt=ArraySize(pos);
         for(int j=i-1;j>=0;j--){// j
            if (temp[j].name!=temp[i].name) continue;                     
            
            switch(temp[j].ptype){
               case  POSITION_TYPE_BUY:
                  if (b>=s) if (ArrayResize(pos,cnt+1+b)==-1) continue;   
                  pos[cnt+b].Buy.name=    temp[j].name;
                  pos[cnt+b].Buy.lot=     temp[j].lot;
                  pos[cnt+b].Buy.tkt=     temp[j].tkt;
                  pos[cnt+b].Buy.ptype=   temp[j].ptype;
                  b++;
               break;
               case POSITION_TYPE_SELL:
                  if (s>=b) if (ArrayResize(pos,cnt+1+s)==-1) continue;   
                  pos[cnt+s].Sell.name=   temp[j].name;
                  pos[cnt+s].Sell.lot=    temp[j].lot;
                  pos[cnt+s].Sell.tkt=    temp[j].tkt;
                  pos[cnt+s].Sell.ptype=  temp[j].ptype;                                  
                  s++;       
               break;
            }
            temp[j].name="";                          //обработанную позицию затираем
         }// j
         //и не забываем запомнить начальный ордер
         if (b!=0 || s!=0){
            switch(temp[i].ptype){
               case  POSITION_TYPE_BUY:
                  if (b>=s) if (ArrayResize(pos,cnt+1+b)==-1) continue;  
                  pos[cnt+b].Buy.name=    temp[i].name;
                  pos[cnt+b].Buy.lot=     temp[i].lot;
                  pos[cnt+b].Buy.tkt=     temp[i].tkt;
                  pos[cnt+b].Buy.ptype=   temp[i].ptype;
                  b++;
               break;
               case POSITION_TYPE_SELL:
                  if (s>=b) if (ArrayResize(pos,cnt+1+s)==-1) continue; 
                  pos[cnt+s].Sell.name=   temp[i].name;
                  pos[cnt+s].Sell.lot=    temp[i].lot;
                  pos[cnt+s].Sell.tkt=    temp[i].tkt;
                  pos[cnt+s].Sell.ptype=  temp[i].ptype;                                  
                  s++;       
               break;
            }
            temp[i].name="";
         }         
         //если одна из сторон пустая то удаляем эти ордера
         if (b==0 || s==0) ArrayResize(pos,cnt); else
         //если по сторонам не равное количество оредров, то пропуски затираем.
         if (b>s) for(int k=s;k<b;k++) pos[cnt+k].Sell.name="";else
         if (s>b) for(int k=b;k<s;k++) pos[cnt+k].Buy.name="";         
      }// i
      
      if (ArraySize(pos)==0) return(true);//значит больше встречных нет и выходим из функции
            
//      //сделаем проверочку
//      string txt="Таблица ордеров:\n";
//      for(int i=0;i<ArraySize(pos);i++){
//         if (pos[i].Buy.name=="") txt=txt+"----------------------"; else
//         txt=txt+pos[i].Buy.name+"  "+DoubleToString(pos[i].Buy.lot,2)+"  "+(string)pos[i].Buy.tkt+"  "+string(pos[i].Buy.ptype)+"    ";
//
//         if (pos[i].Sell.name=="")txt=txt+"----------------------"; else         
//         txt=txt+pos[i].Sell.name+"  "+DoubleToString(pos[i].Sell.lot,2)+"  "+(string)pos[i].Sell.tkt+"  "+string(pos[i].Sell.ptype);
//         txt=txt+"\n";
//      }

/*//////////////////////////////////////////////////////////////////////////////////////////////////////
      Сортируем по объёмам от большего к меньшему. Сначала сторону бай, потом селл.      
//////////////////////////////////////////////////////////////////////////////////////////////////////*/ 

      string smb="";      
      ZeroMemory(temp);
      int total=ArraySize(pos)-1;
      while(total>0){
         if (pos[total].Buy.name=="") {total--;continue;}
         smb=pos[total].Buy.name;
         
         int i=0;
         for(i=total-1;i>=0;i--) if (pos[i].Buy.name!=smb) break; //находим диапазон индексов, от total до i для одного символа по бай
         i=total-i;  //сколько всего элементов подряд по одному символу
         ArrayResize(temp,i);
         for(int j=i-1;j>=0;j--){// а вот и массив который нужно отсортировать
            temp[j].name=     pos[total-j].Buy.name;
            temp[j].lot=      pos[total-j].Buy.lot;
            temp[j].tkt=      pos[total-j].Buy.tkt;            
            temp[j].ptype=    pos[total-j].Buy.ptype;                 
         }         
         
         //сортурем массив пузырьком. С меньшим индексом стоят большие объёмы
         for(int a=i-1;a>=0;a--){
            bool swap=false;
            for(int b=i-1;b>0;b--)
               if (temp[b].lot>temp[b-1].lot){
                  stBegin big[1];
                  big[0].lot=       temp[b].lot;
                  big[0].name=      temp[b].name;
                  big[0].ptype=     temp[b].ptype;
                  big[0].tkt=       temp[b].tkt;
                  
                  temp[b].lot=      temp[b-1].lot;
                  temp[b].name=     temp[b-1].name;
                  temp[b].ptype=    temp[b-1].ptype;
                  temp[b].tkt=      temp[b-1].tkt;
                  
                  temp[b-1].lot=    big[0].lot;
                  temp[b-1].name=   big[0].name;
                  temp[b-1].ptype=  big[0].ptype;
                  temp[b-1].tkt=    big[0].tkt;
                  swap=true;
               } 
            if (!swap) break;
         }           

         //вставляем данные обратно в массив
         for(int j=total, k=i-1;k>=0;j--,k--){
            pos[j].Buy.name=    temp[k].name;
            pos[j].Buy.lot=     temp[k].lot;
            pos[j].Buy.tkt=     temp[k].tkt;            
            pos[j].Buy.ptype=   temp[k].ptype;           
         }                  
         total-=i;
      }
      
/*//////////////////////////////////////////////////////////////////////////////////////////////////////
      Сортируем по объёмам от большего к меньшему. Сортировка на стороне Sell
//////////////////////////////////////////////////////////////////////////////////////////////////////*/ 

      smb="";      
      ZeroMemory(temp);
      total=ArraySize(pos)-1;
      while(total>0){//while 2
         if (pos[total].Sell.name=="") {total--;continue;}
         smb=pos[total].Sell.name;
         
         int i=0;
         for(i=total-1;i>=0;i--) if (pos[i].Sell.name!=smb) break; //находим диапазон индексов, от total до i для одного символа по бай
         i=total-i;  //сколько всего элементов подряд по одному символу
         ArrayResize(temp,i);
         for(int j=i-1;j>=0;j--){// а вот и массив который нужно отсортировать
            temp[j].name=     pos[total-j].Sell.name;
            temp[j].lot=      pos[total-j].Sell.lot;
            temp[j].tkt=      pos[total-j].Sell.tkt;            
            temp[j].ptype=    pos[total-j].Sell.ptype;                 
         }         
         
         //сортурем массив пузырьком. С меньшим индексом стоят большие объёмы
         for(int a=i-1;a>=0;a--){
            bool swap=false;
            for(int b=i-1;b>0;b--)
               if (temp[b].lot>temp[b-1].lot){
                  stBegin big[1];
                  big[0].lot=       temp[b].lot;
                  big[0].name=      temp[b].name;
                  big[0].ptype=     temp[b].ptype;
                  big[0].tkt=       temp[b].tkt;
                  
                  temp[b].lot=      temp[b-1].lot;
                  temp[b].name=     temp[b-1].name;
                  temp[b].ptype=    temp[b-1].ptype;
                  temp[b].tkt=      temp[b-1].tkt;
                  
                  temp[b-1].lot=    big[0].lot;
                  temp[b-1].name=   big[0].name;
                  temp[b-1].ptype=  big[0].ptype;
                  temp[b-1].tkt=    big[0].tkt;
                  swap=true;
               } 
            if (!swap) break;
         }           

         //вставляем данные обратно в массив
         for(int j=total, k=i-1;k>=0;j--,k--){
            pos[j].Sell.name=    temp[k].name;
            pos[j].Sell.lot=     temp[k].lot;
            pos[j].Sell.tkt=     temp[k].tkt;            
            pos[j].Sell.ptype=   temp[k].ptype;           
         }                  
         total-=i;
      }//while 2
      
//      //сделаем проверочку 2
//      txt=txt+"\nОтсортированный массив:\n";
//      for(int i=0;i<ArraySize(pos);i++){
//         if (pos[i].Buy.name=="") txt=txt+"----------------------"; else
//         txt=txt+pos[i].Buy.name+"  "+DoubleToString(pos[i].Buy.lot,2)+"  "+(string)pos[i].Buy.tkt+"  "+string(pos[i].Buy.ptype)+"    ";
//
//         if (pos[i].Sell.name=="")txt=txt+"----------------------"; else         
//         txt=txt+pos[i].Sell.name+"  "+DoubleToString(pos[i].Sell.lot,2)+"  "+(string)pos[i].Sell.tkt+"  "+string(pos[i].Sell.ptype);
//         txt=txt+"\n";
//      }
//      Comment(txt);   

/*//////////////////////////////////////////////////////////////////////////////////////////////////////
      Удаляем пары символов
//////////////////////////////////////////////////////////////////////////////////////////////////////*/
      total=ArraySize(pos);
      for(int i=0;i<total;i++){
         if (pos[i].Buy.name=="") continue;
         if (pos[i].Sell.name=="") continue;
         
          if (Ctrd.PositionCloseBy(pos[i].Buy.tkt,pos[i].Sell.tkt)){
               //Print("Закрываем:" +pos[i].Buy.name+" тикет: "+(string)pos[i].Buy.tkt);
               //Print("Встречный:" +pos[i].Sell.name+" тикет: "+(string)pos[i].Sell.tkt);                     
               //Print("Успешно");
               pos[i].Buy.name="";
               pos[i].Sell.name="";
            }      
      }
      }//while 1      
      return(false);
   }//void

 

Особенности:

1.Не считаем ничего лишнего, сразу игнорим одиночные позиции и ситуацию когда по символу открыты позы только в одну сторону.
2.Скорость работы по сравнению с первым вариантом выросла примерно на 60% (для точности надо сделать норм тесты, но мне уже было лень, замерил на 1 прогоне :)
3.Код вырос значительно, но это всё благодаря структурам. Удобство именования элементов оборачивается невозможностью прогнать их в цикле
4.Код можно немного сжать, если некоторые повторяющиеся моменты вынести в отдельные функции, но хотелось чтобы это была 1 функция, поэтому кое где занимался копипастом :)
5.Неожиданно обнаружилась ошибка в стандартной библиотеке. Вот она:



посмотрел код библиотеки Trade.mqh и увидел что после отправки торгового запроса, на основе структуры MqlTradeRequest формируется текстовое сообщение и том что было сделано. Так вот, если поле action=TRADE_ACTION_CLOSE_BY то это событие не прописано. Конкретно по шагам выглядит так: В файле Trade.mqh доходим до строки 961 (switch(request.action)) и уходим в default ввиду отсутствия описания события. Отправлю репорт в сервис деск.

6.Можно ещё немного увеличить скорость, отказавшись от стандартной библиотеки и описав торговые запросы самостоятельно. 

Буду рад если кто нить выложит свою аналогичную функцию. Интересует сравнение скорости. Ну и так же если есть комментарии и предложения, то готов выслушать.


 

Тестил всё вот таким простым роботом.

//#include <My\iCheck.mqh>
#include <Trade\Trade.mqh>
//CSymbolInfo    Csmb;
CTrade Ctrd;

int OnInit()
  {
   EventSetTimer(1);
   
   datetime tm[1];
   CopyTime("GBPUSD",Period(),0,1,tm);
   CopyTime("USDCHF",Period(),0,1,tm);
   CopyTime("EURGBP",Period(),0,1,tm);
   CopyTime("USDJPY",Period(),0,1,tm);
   
   Ctrd.Buy(1,"EURUSD");
   Ctrd.Sell(0.26,"EURUSD");
   Ctrd.Buy(0.2,"EURGBP"); 
   Ctrd.Buy(0.91,"GBPUSD"); 
   Ctrd.Buy(0.51,"EURUSD");   
   Ctrd.Buy(0.11,"EURUSD");     
   Ctrd.Buy(1,"EURGBP");
   Ctrd.Sell(2,"USDJPY");
   Ctrd.Sell(1.65,"EURGBP");
   Ctrd.Buy(2,"EURGBP");   
   Ctrd.Buy(2,"EURUSD");
   Ctrd.Buy(2,"GBPUSD");
   Ctrd.Sell(0.65,"EURUSD");
   Ctrd.Sell(1,"USDJPY");
   Ctrd.Sell(0.26,"EURGBP");
   Ctrd.Buy(0.25,"EURGBP"); 
   Ctrd.Buy(1,"USDJPY");
   Ctrd.Sell(2.26,"EURUSD");
   Ctrd.Sell(0.45,"USDJPY");
   Ctrd.Buy(0.33,"GBPUSD");    
   Ctrd.Buy(1,"USDJPY"); 
   Ctrd.Buy(1,"EURUSD");
   Ctrd.Buy(2,"GBPUSD");
   Ctrd.Sell(4,"EURGBP");
   Ctrd.Buy(2,"USDCHF");
   Ctrd.Buy(2,"GBPUSD");    
   Ctrd.Buy(1,"EURGBP");   
   Ctrd.Sell(1,"USDJPY");       
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {EventKillTimer();}

void OnTick()
  {
      fnCloseBy();  
  }
void OnTimer()
  { OnTick(); }
 
bool fnCloseBy()
   {
      return(true);
   }


Что удивительно, первая функция в конце оставила меньше позиций чем вторая. Крайне неожиданно, думал будет наоборот. Но по объёмам посчитал вроде всё ок. Любопытно почему та получилось. По уму бы сделать трассировку обоих функций и сравнить, но уже лень.

Причина обращения: