Оптимизация быстродеятельности эксперта. Подскажите, что можно усовершенствовать на рабочем примере.

 
Ниже приведен пример простенького эксперта который имеет достаточно прмитивный вход, прыгающий трейлинг стоп и модуль для четкого подтягивания стопа на любой уровень по достижению профитности. Вот хотелось бы поинтересоватся, можно ли этот код сделать еще более совершенным (в плане быстродействия)? Может у когото из вас есть свои нюансы или некие специфические азы в написании экспертов. Жду предложений.

#property copyright "Buttler"
#property link      "fedakin@ukr.net"

extern double  SL = 58;
extern double  TP = 310;

extern double  MA_Type = 2;
extern double  fast = 3;
extern double  slow = 23;
extern double  shiftfast = 0;
extern double  shiftslow = 0;
extern double  hour_work_from = 11;
extern double  hour_work_to = 15;
extern double  Lots = 1;

extern double  TS_enabled = 0;
extern double  TS_PipsZeroStop = 40;
extern double  TS_PipsToMoveStop = 110;
extern double  TS_PipsStop = 80;

extern double  StopsEnabled = 2;
extern double  Target1 = 180;
extern double  Stop1 = 140;
extern double  Target2 = 300;
extern double  Stop2 = 245;
extern double  Target3 = 80;
extern double  Stop3 = 60;

double   fast2 = 0;
double   fast1 = 0;
double   slow2 = 0;
double   slow1 = 0;
double   TS_value = 0;

int i = 0;

int init() {

   return(0);
}

int deinit() {

   return(0);
}

void CheckForOpen() {
   fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   if ((fast1>slow1) && (fast2<slow2)) {      
      OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); 
   }
   if ((fast1<slow1) && (fast2>slow2)) {
      OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green);
   }
}

void CheckForCloseByTS() {
   for(int i=0;i<OrdersTotal();i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)        break;
      if(OrderSymbol()!=Symbol()) continue;
      if(OrderType()==OP_BUY) {
         if ((Bid-OrderOpenPrice()) > (TS_PipsZeroStop*Point)) {
            TS_value = 0;
            if (OrderStopLoss() < OrderOpenPrice()) {
               TS_value=OrderOpenPrice();
            }
            else {
               if ((Bid - OrderStopLoss()) > (TS_PipsToMoveStop*Point)) {
                  TS_value = OrderStopLoss() + (TS_PipsStop*Point);
               }
            }
            if (TS_value > 0) {
               OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Blue);
            }  
         }
      }
      if(OrderType()==OP_SELL) {
         if ((OrderOpenPrice()-Ask) > (TS_PipsZeroStop*Point)) {
            TS_value = 0;
            if (OrderStopLoss() > OrderOpenPrice()) {
               TS_value=OrderOpenPrice();        
            }            
            else {
               if ((OrderStopLoss()-Ask) > (TS_PipsToMoveStop*Point)) {
                  TS_value = OrderStopLoss() - TS_PipsStop*Point;
               }
            }
            if (TS_value > 0) {
               OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Green);
            } 
         }
         break;
      }
   }
}

void CheckForCloseByPips() {
   for(int i=0;i<OrdersTotal();i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)        break;
      if(OrderSymbol()!=Symbol()) continue;
      if(OrderType()==OP_BUY) {
         if ((StopsEnabled > 0) && ((Bid-OrderOpenPrice()) > (Target1*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop1*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop1*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 1) && ((Bid-OrderOpenPrice()) > (Target2*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop2*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 2) && ((Bid-OrderOpenPrice()) > (Target3*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop3*Point), OrderTakeProfit(),0,Blue);           
         }
         break;         
      }
      if(OrderType()==OP_SELL) {
         if ((StopsEnabled > 0) && ((OrderOpenPrice()-Ask) > (Target1*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop1*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop1*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 1) && ((OrderOpenPrice()-Ask) > (Target2*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop2*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop2*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 2) && ((OrderOpenPrice()-Ask) > (Target3*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop3*Point), OrderTakeProfit(),0,Blue);           
         }
         break;
      }
   }
}

int start() {
   if ((OrdersTotal() < 1) && ((Hour() >= hour_work_from) && (Hour() < hour_work_to))) {
      CheckForOpen();      
   }     
   if ((OrdersTotal() > 0) && (TS_enabled > 0)) {
      CheckForCloseByTS();
   }
   if ((OrdersTotal() > 0) && (StopsEnabled > 0)) {
      CheckForCloseByPips();
   }
   return(0);
} 
 
Этого эксперта можно ускорить.
Например, заголовок цикла
   for(int i=0;i<OrdersTotal();i++)


Сколько итераций, столько и обращений к функции OrdersTotal, плюс ещё 1 обращение

   int total=OrdersTotal();
   for(int i=0;i<total;i++)


2. То же самое касается вызова функции Symbol(). Заведите глобальную строковую переменную и присваивайте ей значение при инициализации эксперта

string symbol;
...
int init()
  {
   symbol=Symbol();
  }
...
      if(OrderSymbol()!=symbol) continue;
...



Самая главная проблема - в функции start
Вот отработает удачно функция CheckForOpen() и значение, возвращаемое функцией OrdersTotal() станет не 0, а 1. Сразу же вызовутся CheckForCloseByTS и CheckForCloseByPips. А оно надо? Похоже, что нет, лишняя работа

 
Хорошо. Вы верно подметили на счет повторного вызова функций. Но что даст то что мы на один тик выиграем то что не будем проверять CheckForCloseByTS и CheckForCloseByPips?. Это легко можно изменить написав перед CheckForOpen(). Но что мы выиграем?.. Минус один лишний вход в эти функции после открытия позиций?. Поставить задержку в вызове функций в 5 минут тоже результата недаст. Потому что нам нужно мониторить каждый тик для подтягивания стопа... Сделать функцию которая бы оценивала сколько там еще до уровня подтягивания стопа и на основе этих данных оценить на сколько трейлингу упасть в спячку для проверки?.. Ну впринципе можно и попробовать. Даст ли это результат. И сколько мы выиграем?... Незнаю...
 
Да, легко изменить. Только тогда лишний раз будет вызываться уже CheckForOpen
 
Merab, почитайте, пожалуйста, вот здесь: "Проблемы с памятью"
Возможно это не совсем в тему про Ваш советник, но всё равно Вам может быть полезно. Вы наверное просто идёте по пути научного тыка, который формулируется следующим образом:"А давай как я возьму десяток параметров и найду в n-мерном пространстве глобальный максимум загнав тестер сразу на оптимизацию по всем параметрам с мелким шагом, чтобы чего не пропустить. Наверняка что-то получится?" Действительно что-то получится. Правда если Вам нужен только ТАКОЙ способ решения, то сразу скажу, что никакие двуядерные супер-пупергигагерцовые системы Вашу задачу не решат поскольку технические средства не могут соревноваться с геометрической прогрессией (и могу сказать для справки - и НИКОГДА не смогут!). И дело тут совершенно не в том во сколько раз MQL4 медленнее, чем C++. Даже если разработчикам МТ4 удастся ускорить работу тестера в 10 раз это Вам в лучшем случае сможет сэкономить лишь считанные проценты (и не более того!) времени до того самого момента когда Вы станете миллионером ;o)! Я же со своей стороны могу Вам лишь предложить то, чем сам пользуюсь постоянно:

solandr:
Я например по своему опыту провожу оптимизацию каждого параметра по отдельности используя метод последовательных приближений (для информации один прогон в тестере моего эксперта занимает 3мин на P4 2,4ГГц – период М1(все тики) за 1,5 года). То есть делю диапазон значений например на 20 интервалов, далее в области максимума делю пару прилегающих к максимуму интервалов из предыдущей оптимизации ещё на 20 интервалов и делаю ещё один такой же шаг. Обычно 3-4 таких итерации оказывается вполне достаточно. Таким образом мы получаем в итоге результат по точности сопоставимый с прогоном сразу по 8000 интервалам с самым мелким шагом (20 в 3 степени). При этом мы имеем экономию в 8000-3*20=7940 итераций. То есть в идеале тратим время в 133 раза меньше, чем при тупом прогоне сразу на мелком шаге. При этом нужно отметить, что если у нас имеется ограниченное количество самого времени, за которое мы хотим получить результат (например мы можем подождать результат до вечера рабочего дня), то мы можем взять в 133 раза более длительный период для тестирования (например вместо 1 недели можно будет посчитать по 133 неделям) и в итоге мы будем иметь результат который будет хоть чего-то стоить, потратив на это тоже самое количество расчётного времени! А результаты быстрых прогонов численностью в 2,7 млн. штук, которых человек в состоянии дождаться, у меня вызывают серьёзные сомнения в плане применимости полученных результатов.
Конечно же мне можно возразить в плане того, что при просчёте всех мелких интервалов можно получить какой-то новый максимум, который будет упущен при расчёте по методике последовательных приближений, описанной выше. Но на это можно сказать, что в таком случае можно поймать лишь максимум, отражающий особенности выборки, и которых не будет в будущем! То есть при оптимизации должны выбираться параметры, изменение которых в небольших пределах, вызывает небольшое изменение в конечном результате. Ну а если у вас на мелком шаге окажется супер-пупер максимум, который мы можем не отследить на более крупном шаге, то можно быть точно уверенным, что данный максимум отражает ТОЛЬКО особенности конкретной выборки и его лучше не принимать во внимание вообще.
 
Провёл я испытания.
Запустил представленного эксперта как есть на оптимизацию (чтобы вывод в логи не мешался).
36 прогонов за 4 минуты 2 секунды. На 1 прогон - 6.7 секунды

Устранил по собственному совету все лишние вызовы функций.
Те же 36 прогонов за 3 минуты 7 секунд. 1 прогон 5.2 секунды

Дополнительно вставил элементарнейшую проверку на наличие денег
2 минуты 55 секунд или 4.9 секунды на прогон

А вы говорите, что это даст.
 
Каким это образом у вас проверка на наличи денег повышает скорость работы?.. А вообще немоглибы вы выложить весь код?
 
Каким это образом у вас проверка на наличи денег повышает скорость работы?.. А вообще немоглибы вы выложить весь код?

Просто не вызывается функция CheckForOpen
код пожалуйста:
extern double  SL = 58;
extern double  TP = 310;

extern double  MA_Type = 2;
extern double  fast = 3;
extern double  slow = 23;
extern double  shiftfast = 0;
extern double  shiftslow = 0;
extern double  hour_work_from = 11;
extern double  hour_work_to = 15;
extern double  Lots = 1;

extern double  TS_enabled = 0;
extern double  TS_PipsZeroStop = 40;
extern double  TS_PipsToMoveStop = 110;
extern double  TS_PipsStop = 80;

extern double  StopsEnabled = 2;
extern double  Target1 = 180;
extern double  Stop1 = 140;
extern double  Target2 = 300;
extern double  Stop2 = 245;
extern double  Target3 = 80;
extern double  Stop3 = 60;

double   fast2 = 0;
double   fast1 = 0;
double   slow2 = 0;
double   slow1 = 0;
double   TS_value = 0;

int i = 0;
string symbol;

int init()
{
   symbol=Symbol();
   return(0);
}

int deinit() {

   return(0);
}

void CheckForOpen() {
   fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   if ((fast1>slow1) && (fast2<slow2)) {      
      OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); 
   }
   else
   {
      if ((fast1<slow1) && (fast2>slow2))
         OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green);
   }
}

void CheckForCloseByTS() {
   int total=OrdersTotal();
   for(int i=0;i<total;i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)        break;
      if(OrderSymbol()!=symbol) continue;
      int type=OrderType();
      if(type==OP_BUY) {
         if ((Bid-OrderOpenPrice()) > (TS_PipsZeroStop*Point)) {
            TS_value = 0;
            if (OrderStopLoss() < OrderOpenPrice()) {
               TS_value=OrderOpenPrice();
            }
            else {
               if ((Bid - OrderStopLoss()) > (TS_PipsToMoveStop*Point)) {
                  TS_value = OrderStopLoss() + (TS_PipsStop*Point);
               }
            }
            if (TS_value > 0) {
               OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Blue);
            }  
         }
         break;
      }
      if(type==OP_SELL) {
         if ((OrderOpenPrice()-Ask) > (TS_PipsZeroStop*Point)) {
            TS_value = 0;
            if (OrderStopLoss() > OrderOpenPrice()) {
               TS_value=OrderOpenPrice();        
            }            
            else {
               if ((OrderStopLoss()-Ask) > (TS_PipsToMoveStop*Point)) {
                  TS_value = OrderStopLoss() - TS_PipsStop*Point;
               }
            }
            if (TS_value > 0) {
               OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Green);
            } 
         }
         break;
      }
   }
}

void CheckForCloseByPips() {
   int total=OrdersTotal();
   for(int i=0;i<total;i++) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)        break;
      if(OrderSymbol()!=symbol) continue;
      int type=OrderType();
      if(type==OP_BUY) {
         if ((StopsEnabled > 0) && ((Bid-OrderOpenPrice()) > (Target1*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop1*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop1*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 1) && ((Bid-OrderOpenPrice()) > (Target2*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop2*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 2) && ((Bid-OrderOpenPrice()) > (Target3*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop3*Point), OrderTakeProfit(),0,Blue);           
         }
         break;         
      }
      if(type==OP_SELL) {
         if ((StopsEnabled > 0) && ((OrderOpenPrice()-Ask) > (Target1*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop1*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop1*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 1) && ((OrderOpenPrice()-Ask) > (Target2*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop2*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop2*Point), OrderTakeProfit(),0,Blue);           
         }
         if ((StopsEnabled > 2) && ((OrderOpenPrice()-Ask) > (Target3*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop3*Point))) {
            OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop3*Point), OrderTakeProfit(),0,Blue);           
         }
         break;
      }
   }
}

int start() {
   static bool money=true;
   if (OrdersTotal() < 1)
   {
      int hour=Hour();
      if ((hour >= hour_work_from) && (hour < hour_work_to) && money) {
         CheckForOpen();
         if(GetLastError()==134) money=false;
      }
   }
   else
   {     
      if (TS_enabled > 0)   CheckForCloseByTS();
      if (StopsEnabled > 0) CheckForCloseByPips();
   }
   return(0);
} 
//+------------------------------------------------------------------+
 
2 Slawa
ИМХО : код еще можно намного ускорить - сама функция

void CheckForOpen() {
   fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   if ((fast1>slow1) && (fast2<slow2)) {      
      OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); 
   }
   else
   {
      if ((fast1<slow1) && (fast2>slow2))
         OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green);
   }
}



топ "тормозного искусства" :). Незачем на каждом тике пересчитывать значения мувингов на предыдущих барах, а это происходит, если нет открытых ордеров. Код лень писать - ИМХО - и так очевидно: эти значения не изменяются - считаем один раз, заносим в массив и используем. Причем, пересчет нужен только по последнему бару, один раз на момент открытия этого бара, все остальные элементы массива просто смещаем на один.

Удачи и попутных трендов.

 
2 Slawa
ИМХО : код еще можно намного ускорить - сама функция

void CheckForOpen() {
   fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast);
   slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast);
   if ((fast1>slow1) && (fast2<slow2)) {      
      OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); 
   }
   else
   {
      if ((fast1<slow1) && (fast2>slow2))
         OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green);
   }
}



топ "тормозного искусства" :). Незачем на каждом тике пересчитывать значения мувингов на предыдущих барах, а это происходит, если нет открытых ордеров. Код лень писать - ИМХО - и так очевидно: эти значения не изменяются - считаем один раз, заносим в массив и используем. Причем, пересчет нужен только по последнему бару, один раз на момент открытия этого бара, все остальные элементы массива просто смещаем на один.

Удачи и попутных трендов.

======А можете показать в вашем исполнении (выложить здесь полный код эксперта с вашим виденыем)уже ускоренный варянт,там где мои тормоза Вы смогли снять?Буду очень признателен!!!
 
А можете показать в вашем исполнении (выложить здесь полный код эксперта с вашим виденыем)уже ускоренный варянт,там где мои тормоза Вы смогли снять?Буду очень признателен!!!

Так это и есть Ваш эксперт. Я в нём сделал только те изменения, о которых говорил раньше. Из самодеятельности я просто добавил один break (похоже, Вы его просто забыли, в функции CheckForCloseByTS первый break).

Добавил переменные symbol, total, type, money. Поиском по тексту эти переменные ищите.

Переписал функцию start - при визуальном сравнении Вашего и моего текста сразу увидите.

Возможна проблема с тем, что открывающие фигурные скобки я ставил в новой строке.
Причина обращения: