Генетические алгоритмы
 
При создании самооптимизирующегося советника, на определенном этапе его работы, требуется автоматический вызов кода, который заново оптимизирует параметры советника на истории финансового инструмента, и далее советник продолжит свою работу уже с новыми параметрами.
Так как сам советник работает на текущем баре и не использует историю символа, код оптимизации параметров советника должен опираться на код индикатора, который в свою очередь создан на основе кода советника.
Для оптимизации параметров советника, или теперь уже параметров индикатора, совпадающих с параметрами советника, можно использовать полный перебор, однако для сокращения времени оптимизации можно применить генетический алгоритм.
Обратимся к терминологии.
Хромосома состоит из набора генов – набора случайно выбранных параметров советника в допустимых диапазонах.
Поколение это набор хромосом.
Фитнес функция FF это код, возвращающий значение советника, по которому производится оптимизация, например прибыль.
Значение функции фитнеса VFF ( ) используется для вычисления вероятности воспроизведения хромосомы – члена популяции. 
 
Начальная популяция формируется случайным образом и размер популяции (количество особей) фиксируется и не изменяется в течение работы всего алгоритма.
На основе вероятностей воспроизведения хромосом начальной популяции формируется новая популяция, и так далее пока не сработает условие завершения работы алгоритма.
Каждая следующая популяция формируется из предыдущей с помощью отбора, скрещивания и мутации.
Для отбора могут использоваться такие алгоритмы как рулетка, турнирный отбор и элитный отбор.
При применении рулетки, колесо рулетки делится на сектора, число которых совпадает с числом особей популяции. Площадь каждого сектора пропорциональна вероятности воспроизведения особи популяции. Отбор производится с помощью вращения колеса рулетки. Таким образом, особь с наибольшим значением вероятности воспроизведения попадает в следующую популяцию наибольшее число раз. Количество копий особи в следующей популяции определяется по формуле:
 
Где N – число особей в популяции.
При турнирном отборе из популяции случайно выбираются две особи, из которых остается особь с наибольшим значением вероятности воспроизведения.
При элитном отборе несколько лучших особей переходят в следующую популяцию без изменений, не участвуя в отборе и скрещивании.
После отбора идет скрещивание особей, при котором сначала выбираются две особи, затем случайно определяется точка разрыва в диапазоне числа параметров фитнес функции, после чего особи обмениваются сегментами хромосомы. Само скрещивание производится с вероятностью ½.
При применении мутации, сначала случайно выбирается параметр фитнес функции, затем он модифицируется. Сама мутация производится с вероятностью 0,001.
Библиотека UGAlib MQL5 Community реализации генетического алгоритма использует представление хромосомы в виде массива double Chromosome[], где 0 индекс это значение фитнес функции, а остальные индексы это параметры фитнес функции или гены хромосомы.
Оптимизация параметров фитнес функции ведется в одном диапазоне от RangeMinimum до RangeMaximum. Поэтому если оптимизируемые параметры имеют разные диапазоны, их нужно привести к одному диапазону, как правило, от 0.0 до 1.0.
Популяция особей представлена двумерным массивом double Population [][1000], где строки это хромосомы.
Популяция ранжируется по значению фитнес функции таким образом, что первый ее член имеет наилучшее значение фитнес функции по критерию оптимизации.
Популяция делится на две части. Вначале вся популяция заселяется особями со случайно выбранными генами в диапазоне. Затем для этих особей вычисляется значение фитнес функции, которое помещается в 0 индекс хромосомы. При этом функция GetFitness расчета значения фитнес функции оперирует колонией потомков, представленной массивом double Colony [][500], имеющим размер в два раза меньший, чем размер популяции. Таким образом, популяция заселяется двумя колониями потомков.
Колония потомков имеет размер меньший, чем размер популяции, для того, чтобы после мутаций и скрещиваний заселить ту часть популяции, которая имеет худшие значения фитнес функции. При этом особи, полученные в результате мутаций и скрещиваний, заселяют именно колонию потомков.
После начального заселения популяции производится удаление дубликатов с помощью функции RemovalDuplicates, в которой также производится ранжирование популяции по значению фитнес функции.
После подготовки начальной популяции вызывается цикл эпох – цикл рождения новых популяций, который продолжается до тех пор, пока количество эпох без улучшения не превысит порог Epoch.
Критерием улучшения служит эталонная хромосома – первая особь популяции.
В цикле эпох популяция модифицируется с помощью репликации, естественной мутации, искусственной мутации, заимствования генов и скрещивания, применяемых для заселения новой колонии потомков, которая затем замещает часть популяции, имеющей худшие значения фитнес функции.
В цикле заселения новой колонии потомков функции CycleOfOperators, операторы репликации, естественной мутации, искусственной мутации, заимствования генов и скрещивания выбираются случайно, в зависимости от их доли от 0 до 100.
После создания новой популяции, в ней также удаляются дубликаты, и она ранжируется по значению фитнес функции.
Здесь репликация заключается в выборе двух хромосом популяции и создании на их основе новой хромосомы, для которой гены случайно выбираются в расширенном диапазоне с помощью сдвига от гена первой особи влево и от гена второй особи вправо.
Выбор двух родителей из популяции осуществляется с помощью алгоритма рулетки, упомянутого выше.
Естественная мутация производится с помощью выбора одного родителя из популяции, используя алгоритм рулетки, и замены его генов генами, случайно выбранными в диапазоне от RangeMinimum до RangeMaximum. Замена генов производится с вероятностью NMutationProbability от 0.0 до 100.0.
При искусственной мутации выбираются два родителя из популяции, используя алгоритм рулетки, и гены потомка случайно выбираются из незанятого генами родителей пространства на числовой прямой в диапазонах от RangeMinimum до сдвига от гена первой особи вправо и от сдвига от гена второй особи влево до RangeMaximum.
При заимствовании генов для первого гена потомка выбирается родитель из популяции, используя алгоритм рулетки, и берется у него первый ген, далее, для второго гена отбирается второй родитель и берется второй ген и т.д.
При скрещивании выбираются два родителя из популяции, используя алгоритм рулетки, и гены потомка формируются за счет обмена отрезками хромосом мамы и папы.
Полный код реализации генетического алгоритма.
//+——————————————————————————————————————————————————————————————————————+
//|                                                       JQS UGA v1.3.1 |
//|                                       Copyright © 2010, JQS aka Joo. |
//|                                     http://www.mql4.com/ru/users/joo |
//|                                  https://login.mql5.com/ru/users/joo |
//+——————————————————————————————————————————————————————————————————————+
//Библиотека "Универсального Генетического Алгоритма UGAlib"             |
//использующего представление хромосомы вещественными числами.           |
//+——————————————————————————————————————————————————————————————————————+
 
//----------------------Глобальные переменные-----------------------------
double Chromosome[];            //Набор оптимизируемых аргументов функции - генов
                                //(например: веса нейронной сети и т.д.)-хромосома
int    ChromosomeCount     =0;  //Максимально возможное количество хромосом в колонии
int    TotalOfChromosomesInHistory=0;//Общее количество хромосом в истории
int    ChrCountInHistory   =0;  //Количество уникальных хромосом в базе хромосом
int    GeneCount           =0;  //Количество генов в хромосоме
 
double RangeMinimum        =0.0;//Минимум диапазона поиска
double RangeMaximum        =0.0;//Максимум диапазона поиска
double Precision           =0.0;//Шаг поиска
int    OptimizeMethod      =0;  //1-минимум, любое другое - максимум
 
double Population   [][1000];   //Популяция
double Colony       [][500];    //Колония потомков
int    PopulChromosCount   =0;  //Текущее количество хромосом в популяции
int    Epoch               =0;  //Кол-во эпох без улучшения
int    AmountStartsFF=0;        //Количество запусков функции приспособленности
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Основная функция UGA
void UGA
(
double ReplicationPortion, //Доля Репликации.
double NMutationPortion,   //Доля Естественной мутации.
double ArtificialMutation, //Доля Искусственной мутации.
double GenoMergingPortion, //Доля Заимствования генов.
double CrossingOverPortion,//Доля Кроссинговера.
//---
double ReplicationOffset,  //Коэффициент смещения границ интервала
double NMutationProbability//Вероятность мутации каждого гена в %
)
  //сброс генератора, производится только один раз
  MathSrand((int)TimeLocal());
  //-----------------------Переменные-------------------------------------
  int    chromos=0, gene  =0;//индексы хромосом и генов
  int    resetCounterFF   =0;//счетчик сбросов "Эпох без улучшений"
  int    currentEpoch     =1;//номер текущей эпохи
  int    SumOfCurrentEpoch=0;//сумма "Эпох без улучшений"
  int    MinOfCurrentEpoch=Epoch;//минимальное "Эпох без улучшений"
  int    MaxOfCurrentEpoch=0;//максимальное "Эпох без улучшений"
  int    epochGlob        =0;//общее количество эпох
  // Колония [количество признаков(генов)][количество особей в колонии]
  ArrayResize    (Population,GeneCount+1);
  ArrayInitialize(Population,0.0);
  // Колония потомков [количество признаков(генов)][количество особей в колонии]
  ArrayResize    (Colony,GeneCount+1);
  ArrayInitialize(Colony,0.0);
  // Банк хромосом
  // [количество признаков(генов)][количество хромосом в банке]
  double          historyHromosomes[][100000];
  ArrayResize    (historyHromosomes,GeneCount+1);
  ArrayInitialize(historyHromosomes,0.0);
  //----------------------------------------------------------------------
  //--------------Проверка корректности входных параметров----------------
  //...количество хромосом должно быть не меньше 2
  if (ChromosomeCount<=1)  ChromosomeCount=2;
  if (ChromosomeCount>500) ChromosomeCount=500;
  //----------------------------------------------------------------------
  //======================================================================
  // 1) Создать протопопуляцию                                     —————1)
  ProtopopulationBuilding ();
  //======================================================================
  // 2) Определить приспособленность каждой особи                  —————2)
  //Для 1-ой колонии
  for (chromos=0;chromos<ChromosomeCount;chromos++)
    for (gene=1;gene<=GeneCount;gene++)
      Colony[gene][chromos]=Population[gene][chromos];
 
  GetFitness(historyHromosomes);
 
  for (chromos=0;chromos<ChromosomeCount;chromos++)
    Population[0][chromos]=Colony[0][chromos];
 
  //Для 2-ой колонии
  for (chromos=ChromosomeCount;chromos<ChromosomeCount*2;chromos++)
    for (gene=1;gene<=GeneCount;gene++)
      Colony[gene][chromos-ChromosomeCount]=Population[gene][chromos];
 
  GetFitness(historyHromosomes);
 
  for (chromos=ChromosomeCount;chromos<ChromosomeCount*2;chromos++)
    Population[0][chromos]=Colony[0][chromos-ChromosomeCount];
  //======================================================================
  // 3) Подготовить популяцию к размножению                         ————3)
  RemovalDuplicates();
  //======================================================================
  // 4) Выделить эталонную хромосому                               —————4)
  for (gene=0;gene<=GeneCount;gene++)
    Chromosome[gene]=Population[gene][0];
  //======================================================================
  ServiceFunction();
 
  //Основной цикл генетического алгоритма с 5 по 6
  while (currentEpoch<=Epoch)
  {
    //====================================================================
    // 5) Операторы UGA                                            —————5)
    CycleOfOperators
    (
    historyHromosomes,
    //---
    ReplicationPortion, //Доля Репликации.
    NMutationPortion,   //Доля Естественной мутации.
    ArtificialMutation, //Доля Искусственной мутации.
    GenoMergingPortion, //Доля Заимствования генов.
    CrossingOverPortion,//Доля Кроссинговера.
    //---
    ReplicationOffset,  //Коэффициент смещения границ интервала
    NMutationProbability//Вероятность мутации каждого гена в %
    );
    //====================================================================
    // 6) Сравнить гены лучшего потомка с генами эталонной хромосомы. 
    // Если хромосома лучшего потомка лучше эталонной,
    // заменить эталонную.                                         —————6)
    //Если режим оптимизации - минимизация
    if (OptimizeMethod==1)
    {
      //Если лучшая хромосома популяции лучше эталонной
      if (Population[0][0]<Chromosome[0])
      {
        //Заменим эталонную хромосому
        for (gene=0;gene<=GeneCount;gene++)
          Chromosome[gene]=Population[gene][0];
        ServiceFunction();
        //Сбросим счетчик "эпох без улучшений"
        if (currentEpoch<MinOfCurrentEpoch)
          MinOfCurrentEpoch=currentEpoch;
        if (currentEpoch>MaxOfCurrentEpoch)
          MaxOfCurrentEpoch=currentEpoch;
        SumOfCurrentEpoch+=currentEpoch; currentEpoch=1; resetCounterFF++;
      }
      else
        currentEpoch++;
    }
    //Если режим оптимизации - максимизация
    else
    {
      //Если лучшая хромосома популяции лучше эталонной
      if (Population[0][0]>Chromosome[0])
      {
        //Заменим эталонную хромосому
        for (gene=0;gene<=GeneCount;gene++)
          Chromosome[gene]=Population[gene][0];
        ServiceFunction();
        //Сбросим счетчик "эпох без улучшений"
        if (currentEpoch<MinOfCurrentEpoch)
          MinOfCurrentEpoch=currentEpoch;
        if (currentEpoch>MaxOfCurrentEpoch)
          MaxOfCurrentEpoch=currentEpoch;
        SumOfCurrentEpoch+=currentEpoch; currentEpoch=1; resetCounterFF++;
      }
      else
        currentEpoch++;
    }
    //====================================================================
    //Прошла ещё одна эпоха....
    epochGlob++;
  }
  Print("Прошло всего эпох=",epochGlob," Всего сбросов=",resetCounterFF);
  Print("Мин.эпох без улучш.=",MinOfCurrentEpoch,
        " Средн.эпох без улучш.=",
        NormalizeDouble((double)SumOfCurrentEpoch/(double)resetCounterFF,2),
        " Макс.эпох без улучш.=",MaxOfCurrentEpoch);
  Print(ChrCountInHistory," - Уникальных хромосом");
  Print(AmountStartsFF," - Общее кол-во запусков FF");
  Print(TotalOfChromosomesInHistory," - Общее кол-во хромосом в истории");
  Print(NormalizeDouble(100.0-((double)ChrCountInHistory*100.0/
                               (double)TotalOfChromosomesInHistory),2),"% дубликатов");
  Print(Chromosome[0]," - Лучший результат");
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Создание протопопуляции
void ProtopopulationBuilding()
  PopulChromosCount=ChromosomeCount*2;
  //Заполнить популяцию хромосомами со случайными
  //...генами в диапазоне RangeMinimum...RangeMaximum
  for (int chromos=0;chromos<PopulChromosCount;chromos++)
  {
    //начиная с 1-го индекса (0-ой -зарезервирован для VFF) 
    for (int gene=1;gene<=GeneCount;gene++)
      Population[gene][chromos]=
      SelectInDiscreteSpace(RNDfromCI(RangeMinimum,RangeMaximum),RangeMinimum,RangeMaximum,Precision,3);
    TotalOfChromosomesInHistory++;
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Получение приспособленности для каждой особи.
void GetFitness
(
double &historyHromosomes[][100000]
)
  for (int chromos=0;chromos<ChromosomeCount;chromos++)
    CheckHistoryChromosomes(chromos,historyHromosomes);
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Проверка хромосомы по базе хромосом.
void CheckHistoryChromosomes
(
int     chromos,
double &historyHromosomes[][100000]
)
  //-----------------------Переменные-------------------------------------
  int   Ch1=0;  //Индекс хромосомы из базы
  int   Ge =0;  //Индекс гена
  int   cnt=0;  //Счетчик уникальных генов. Если хоть один ген отличается 
                //- хромосома признается уникальной
  //----------------------------------------------------------------------
  //Если в базе хранится хоть одна хромосома
  if (ChrCountInHistory>0)
  {
    //Переберем хромосомы в базе, чтобы найти такую же
    for (Ch1=0;Ch1<ChrCountInHistory && cnt<GeneCount;Ch1++)
    {
      cnt=0;
      //Сверяем гены, пока индекс гена меньше кол-ва генов и пока попадаются одинаковые гены
      for (Ge=1;Ge<=GeneCount;Ge++)
      {
        if (Colony[Ge][chromos]!=historyHromosomes[Ge][Ch1])
          break;
        cnt++;
      }
    }
    //Если набралось одинаковых генов столько же, можно взять готовое решение из базы
    if (cnt==GeneCount)
      Colony[0][chromos]=historyHromosomes[0][Ch1-1];
    //Если нет такой же хромосомы в базе, то рассчитаем для неё FF...
    else
    {
      FitnessFunction(chromos);
      //.. и если есть место в базе сохраним
      if (ChrCountInHistory<100000)
      {
        for (Ge=0;Ge<=GeneCount;Ge++)
          historyHromosomes[Ge][ChrCountInHistory]=Colony[Ge][chromos];
        ChrCountInHistory++;
      }
    }
  }
  //Если база пустая, рассчитаем для неё FF и сохраним её в базе
  else
  {
    FitnessFunction(chromos);
    for (Ge=0;Ge<=GeneCount;Ge++)
      historyHromosomes[Ge][ChrCountInHistory]=Colony[Ge][chromos];
    ChrCountInHistory++;
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Цикл операторов UGA
void CycleOfOperators
(
double &historyHromosomes[][100000],
//---
double    ReplicationPortion, //Доля Репликации.
double    NMutationPortion,   //Доля Естественной мутации.
double    ArtificialMutation, //Доля Искусственной мутации.
double    GenoMergingPortion, //Доля Заимствования генов.
double    CrossingOverPortion,//Доля Кроссинговера.
//---
double    ReplicationOffset,  //Коэффициент смещения границ интервала
double    NMutationProbability//Вероятность мутации каждого гена в %
)
{
  //-----------------------Переменные-------------------------------------
  double          child[];
  ArrayResize    (child,GeneCount+1);
  ArrayInitialize(child,0.0);
 
  int gene=0,chromos=0, border=0;
  int    i=0,u=0;
  double p=0.0,start=0.0;
  double          fit[][2];
  ArrayResize    (fit,6);
  ArrayInitialize(fit,0.0);
 
  //Счетчик посадочных мест в новой популяции.
  int T=0;
  //----------------------------------------------------------------------
 
  //Зададим долю операторов UGA
  double portion[6];
  portion[0]=ReplicationPortion; //Доля Репликации.
  portion[1]=NMutationPortion;   //Доля Естественной мутации.
  portion[2]=ArtificialMutation; //Доля Искусственной мутации.
  portion[3]=GenoMergingPortion; //Доля Заимствования генов.
  portion[4]=CrossingOverPortion;//Доля Кроссинговера.
  portion[5]=0.0;
  //----------------------------
  if (NMutationProbability<0.0)
    NMutationProbability=0.0;
  if (NMutationProbability>100.0)
    NMutationProbability=100.0;
  //----------------------------
  //------------------------Цикл операторов UGA---------
  //Заполняем новую колонию потомками 
  while (T<ChromosomeCount)
  {
    //============================
    
    for (i=0;i<6;i++)
    {    
      fit[i][0]=start;
      fit[i][1]=start+MathAbs(portion[i]-portion[5]);
      start=fit[i][1];
      
    }
    
    p=RNDfromCI(fit[0][0],fit[4][1]);
    for (u=0;u<5;u++)
    {
      if ((fit[u][0]<=p && p<fit[u][1]) || p==fit[u][1])
        break;
    }
    Print(u);
    //============================
    switch (u)
    {
    //---------------------
    case 0:
      //------------------------Репликация--------------------------------
      //Если есть место в новой колонии, создадим новую особь
      if (T<ChromosomeCount)
      {
        Replication(child,ReplicationOffset);
        //Поселим новую особь в новую колонию
        for (gene=1;gene<=GeneCount;gene++) Colony[gene][T]=child[gene];
        //Одно место заняли, счетчик перемотаем вперед
        T++;
        TotalOfChromosomesInHistory++;
      }
      //---------------------------------------------------------------
      break;
      //---------------------
    case 1:
      //---------------------Естественная мутация-------------------------
      //Если есть место в новой колонии, создадим новую особь
      if (T<ChromosomeCount)
      {
        NaturalMutation(child,NMutationProbability);
        //Поселим новую особь в новую колонию
        for (gene=1;gene<=GeneCount;gene++) Colony[gene][T]=child[gene];
        //Одно место заняли, счетчик перемотаем вперед
        T++;
        TotalOfChromosomesInHistory++;
      }
      //---------------------------------------------------------------
      break;
      //---------------------
    case 2:
      //----------------------Искусственная мутация-----------------------
      //Если есть место в новой колонии, создадим новую особь
      if (T<ChromosomeCount)
      {
        ArtificialMutation(child,ReplicationOffset);
        //Поселим новую особь в новую  колонию
        for (gene=1;gene<=GeneCount;gene++) Colony[gene][T]=child[gene];
        //Одно место заняли, счетчик перемотаем вперед
        T++;
        TotalOfChromosomesInHistory++;
      }
      //---------------------------------------------------------------
      break;
      //---------------------
    case 3:
      //-------------Образование особи с заимствованными генами-----------
      //Если есть место в новой колонии, создадим новую особь
      if (T<ChromosomeCount)
      {
        GenoMerging(child);
        //Поселим новую особь в новую колонию 
        for (gene=1;gene<=GeneCount;gene++) Colony[gene][T]=child[gene];
        //Одно место заняли, счетчик перемотаем вперед
        T++;
        TotalOfChromosomesInHistory++;
      }
      //---------------------------------------------------------------
      break;
      //---------------------
    default:
      //---------------------------Кроссинговер---------------------------
      //Если есть место в новой колонии, создадим новую особь
      if (T<ChromosomeCount)
      {
        CrossingOver(child);
        //Поселим новую особь в новую  колонию
        for (gene=1;gene<=GeneCount;gene++) Colony[gene][T]=child[gene];
        //Одно место заняли, счетчик перемотаем вперед
        T++;
        TotalOfChromosomesInHistory++;
      }
      //---------------------------------------------------------------
 
      break;
      //---------------------
    }
  }//Конец цикла операторов UGA--
 
  //Определим приспособленность каждой особи в колонии потомков
  GetFitness(historyHromosomes);
 
  //Поселим потомков в основную популяцию
  if (PopulChromosCount>=ChromosomeCount)
  {
    border=ChromosomeCount;
    PopulChromosCount=ChromosomeCount*2;
  }
  else
  {
    border=PopulChromosCount;
    PopulChromosCount+=ChromosomeCount;
  }
  for (chromos=0;chromos<ChromosomeCount;chromos++)
    for (gene=0;gene<=GeneCount;gene++)
      Population[gene][chromos+border]=Colony[gene][chromos];
 
  //Подготовим популяцию к следующему размножению
  RemovalDuplicates();
}//конец ф-ии
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Репликация
void Replication
(
double &child[],
double  ReplicationOffset
)
{
  //-----------------------Переменные-------------------------------------
  double C1=0.0,C2=0.0,temp=0.0,Maximum=0.0,Minimum=0.0;
  int address_mama=0,address_papa=0;
  //----------------------------------------------------------------------
  SelectTwoParents(address_mama,address_papa);
  //-------------------Цикл перебора генов--------------------------------
  for (int i=1;i<=GeneCount;i++)
  {
    //----определим откуда мать и отец --------
    C1 = Population[i][address_mama];
    C2 = Population[i][address_papa];
    //------------------------------------------
    
    //------------------------------------------------------------------
    //....определим наибольший и наименьший из них,
    //если С1>C2, поменяем их местами
    if (C1>C2)
    {
      temp = C1; C1=C2; C2 = temp;
    }
    //--------------------------------------------
    if (C2-C1<Precision)
    {
      child[i]=C1; continue;
    }
    //--------------------------------------------
    //Назначим границы создания нового гена
    Minimum = C1-((C2-C1)*ReplicationOffset);
    Maximum = C2+((C2-C1)*ReplicationOffset);
    //--------------------------------------------
    //Обязательная проверка, что бы поиск не вышел из заданного диапазона
    if (Minimum < RangeMinimum) Minimum = RangeMinimum;
    if (Maximum > RangeMaximum) Maximum = RangeMaximum;
    //---------------------------------------------------------------
    temp=RNDfromCI(Minimum,Maximum);
    child[i]=
    SelectInDiscreteSpace(temp,RangeMinimum,RangeMaximum,Precision,3);
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Естественная мутация.
void NaturalMutation
(
double &child[],
double  NMutationProbability
)
{
  //-----------------------Переменные-------------------------------------
  int    address=0;
  //----------------------------------------------------------------------
  
  //-----------------Отбор родителя------------------------
  SelectOneParent(address);
  //---------------------------------------
  for (int i=1;i<=GeneCount;i++)
    if (RNDfromCI(0.0,100.0)<=NMutationProbability)
      child[i]=
      SelectInDiscreteSpace(RNDfromCI(RangeMinimum,RangeMaximum),RangeMinimum,RangeMaximum,Precision,3);
    else
      child[i]=Population[i][address];
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Искусственная мутация.
void ArtificialMutation
(
double &child[],
double  ReplicationOffset
)
{
  //-----------------------Переменные-------------------------------------
  double C1=0.0,C2=0.0,temp=0.0,Maximum=0.0,Minimum=0.0,p=0.0;
  int address_mama=0,address_papa=0;
  //----------------------------------------------------------------------
  //-----------------Отбор родителей------------------------
  SelectTwoParents(address_mama,address_papa);
  //--------------------------------------------------------
  //-------------------Цикл перебора генов------------------------------
  for (int i=1;i<=GeneCount;i++)
  {
    //----определим откуда мать и отец --------
    C1 = Population[i][address_mama];
    C2 = Population[i][address_papa];
    //------------------------------------------
    
    //------------------------------------------------------------------
    //....определим наибольший и наименьший из них,
    //если С1>C2, поменяем их местами
    if (C1>C2)
    {
      temp=C1; C1=C2; C2=temp;
    }
    //--------------------------------------------
    //Назначим границы создания нового гена
    Minimum=C1-((C2-C1)*ReplicationOffset);
    Maximum=C2+((C2-C1)*ReplicationOffset);
    //--------------------------------------------
    //Обязательная проверка, что бы поиск не вышел из заданного диапазона
    if (Minimum < RangeMinimum) Minimum = RangeMinimum;
    if (Maximum > RangeMaximum) Maximum = RangeMaximum;
    //---------------------------------------------------------------
    p=MathRand();
    if (p<16383.5)
    {
      temp=RNDfromCI(RangeMinimum,Minimum);
      child[i]=
      SelectInDiscreteSpace(temp,RangeMinimum,RangeMaximum,Precision,3);
    }
    else
    {
      temp=RNDfromCI(Maximum,RangeMaximum);
      child[i]=
      SelectInDiscreteSpace(temp,RangeMinimum,RangeMaximum,Precision,3);
    }
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Заимствование генов.
void GenoMerging
(
double &child[]
)
{
  //-----------------------Переменные-------------------------------------
  int  address=0;
  //----------------------------------------------------------------------
  for (int i=1;i<=GeneCount;i++)
  {
    //-----------------Отбор родителя------------------------
    SelectOneParent(address);
    //--------------------------------------------------------
    child[i]=Population[i][address];
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Кроссинговер.
void CrossingOver
(
double &child[]
)
{
  //-----------------------Переменные-------------------------------------
  int address_mama=0,address_papa=0;
  //----------------------------------------------------------------------
  //-----------------Отбор родителей------------------------
  SelectTwoParents(address_mama,address_papa);
  //--------------------------------------------------------
  //Определим точку разрыва
  int address_of_gene=(int)MathFloor((GeneCount-1)*(MathRand()/32767.5));
 
  for (int i=1;i<=GeneCount;i++)
  {
    //----копируем гены матери--------
    if (i<=address_of_gene+1)
      child[i]=Population[i][address_mama];
    //----копируем гены отца--------
    else
      child[i]=Population[i][address_papa];
  }
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Отбор двух родителей.
void SelectTwoParents
(
int &address_mama,
int &address_papa
)
{
  //-----------------------Переменные-------------------------------------
  int cnt=1;
  address_mama=0;//адрес материнской особи в популяции
  address_papa=0;//адрес отцовской особи в популяции
  //----------------------------------------------------------------------
  //----------------------------Отбор родителей--------------------------
  //Десять попыток выбрать разных родителей.
  while (cnt<=10)
  {
    //Для материнской особи
    address_mama=NaturalSelection();
    //Для отцовской особи
    address_papa=NaturalSelection();
    if (address_mama!=address_papa)
      break;
    cnt++;
  }
  //---------------------------------------------------------------------
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Отбор одного родителя.
void SelectOneParent
(
int &address//адрес родительской особи в популяции
)
{
  //-----------------------Переменные-------------------------------------
  address=0;
  //----------------------------------------------------------------------
  //----------------------------Отбор родителя--------------------------
  address=NaturalSelection();
  //---------------------------------------------------------------------
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Естественный отбор.
int NaturalSelection()
{
  //-----------------------Переменные-------------------------------------
  int    i=0,u=0;
  double p=0.0,start=0.0;
  double          fit[][2];
  ArrayResize    (fit,PopulChromosCount);
  ArrayInitialize(fit,0.0);
  double delta=(Population[0][0]-Population[0][PopulChromosCount-1])*0.01-Population[0][PopulChromosCount-1];
  //----------------------------------------------------------------------
 
  for (i=0;i<PopulChromosCount;i++)
  {
    fit[i][0]=start;
    fit[i][1]=start+MathAbs(Population[0][i]+delta);
    start=fit[i][1];
  }
  p=RNDfromCI(fit[0][0],fit[PopulChromosCount-1][1]);
 
  for (u=0;u<PopulChromosCount;u++)
    if ((fit[u][0]<=p && p<fit[u][1]) || p==fit[u][1])
      break;
 
  return(u);
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Удаление дубликатов с сортировкой по VFF
void RemovalDuplicates()
{
  //-----------------------Переменные-------------------------------------
  int             chromosomeUnique[1000];//Массив хранит признак уникальности 
                                         //каждой хромосомы: 0-дубликат, 1-уникальная
  ArrayInitialize(chromosomeUnique,1);   //Предположим, что дубликатов нет
  double          PopulationTemp[][1000];
  ArrayResize    (PopulationTemp,GeneCount+1);
  ArrayInitialize(PopulationTemp,0.0);
 
  int Ge =0;                             //Индекс гена
  int Ch =0;                             //Индекс хромосомы
  int Ch2=0;                             //Индекс второй хромосомы
  int cnt=0;                             //Счетчик
  //----------------------------------------------------------------------
 
  //----------------------Удалим дубликаты---------------------------1
  //Выбираем первый из пары для сравнения...
  for (Ch=0;Ch<PopulChromosCount-1;Ch++)
  {
    //Если не дубликат...
    if (chromosomeUnique[Ch]!=0)
    {
      //Выбираем второй из пары...
      for (Ch2=Ch+1;Ch2<PopulChromosCount;Ch2++)
      {
        if (chromosomeUnique[Ch2]!=0)
        {
          //Обнулим счетчик количества идентичных генов
          cnt=0;
          //Сверяем гены, пока попадаются одинаковые гены
          for (Ge=1;Ge<=GeneCount;Ge++)
          {
            if (Population[Ge][Ch]!=Population[Ge][Ch2])
              break;
            else
              cnt++;
          }
          //Если набралось одинаковых генов столько же, сколько всего генов
          //..хромосома признается дубликатом
          if (cnt==GeneCount)
            chromosomeUnique[Ch2]=0;
        }
      }
    }
  }
  //Счетчик посчитает количество уникальных хромосом
  cnt=0;
  //Скопируем уникальные хромосомы во временный масив
  for (Ch=0;Ch<PopulChromosCount;Ch++)
  {
    //Если хромосома уникальна, скопируем её, если нет, перейдем к следующей
    if (chromosomeUnique[Ch]==1)
    {
      for (Ge=0;Ge<=GeneCount;Ge++)
        PopulationTemp[Ge][cnt]=Population[Ge][Ch];
      cnt++;
    }
  }
  //Назначим переменной "Всего хромосом" значение счетчика уникальных хромосом
  PopulChromosCount=cnt;
  //Вернем уникальные хромосомы обратно в массив для временного хранения 
  //..обьединяемых популяций 
  for (Ch=0;Ch<PopulChromosCount;Ch++)
    for (Ge=0;Ge<=GeneCount;Ge++)
      Population[Ge][Ch]=PopulationTemp[Ge][Ch];
  //=================================================================1
 
  //----------------Ранжирование популяции---------------------------2
  PopulationRanking();
  //=================================================================2
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Ранжирование популяции.
void PopulationRanking()
{
  //-----------------------Переменные-------------------------------------
  int cnt=1, i = 0, u = 0;
  double          PopulationTemp[][1000];           //Временная популяция 
  ArrayResize    (PopulationTemp,GeneCount+1);
  ArrayInitialize(PopulationTemp,0.0);
 
  int             Indexes[];                        //Индексы хромосом
  ArrayResize    (Indexes,PopulChromosCount);
  ArrayInitialize(Indexes,0);
  int    t0=0;
  double          ValueOnIndexes[];                 //VFF соответствующих
                                                    //..индексов хромосом
  ArrayResize    (ValueOnIndexes,PopulChromosCount);
  ArrayInitialize(ValueOnIndexes,0.0); double t1=0.0;
  //----------------------------------------------------------------------
 
  //Проставим индексы во временном массиве temp2 и 
  //...скопируем первую строку из сортируемого массива
  for (i=0;i<PopulChromosCount;i++)
  {
    Indexes[i] = i;
    ValueOnIndexes[i] = Population[0][i];
  }
  if (OptimizeMethod==1)
  {
    while (cnt>0)
    {
      cnt=0;
      for (i=0;i<PopulChromosCount-1;i++)
      {
        if (ValueOnIndexes[i]>ValueOnIndexes[i+1])
        {
          //-----------------------
          t0 = Indexes[i+1];
          t1 = ValueOnIndexes[i+1];
          Indexes   [i+1] = Indexes[i];
          ValueOnIndexes   [i+1] = ValueOnIndexes[i];
          Indexes   [i] = t0;
          ValueOnIndexes   [i] = t1;
          //-----------------------
          cnt++;
        }
      }
    }
  }
  else
  {
    while (cnt>0)
    {
      cnt=0;
      for (i=0;i<PopulChromosCount-1;i++)
      {
        if (ValueOnIndexes[i]<ValueOnIndexes[i+1])
        {
          //-----------------------
          t0 = Indexes[i+1];
          t1 = ValueOnIndexes[i+1];
          Indexes   [i+1] = Indexes[i];
          ValueOnIndexes   [i+1] = ValueOnIndexes[i];
          Indexes   [i] = t0;
          ValueOnIndexes   [i] = t1;
          //-----------------------
          cnt++;
        }
      }
    }
  }
  //Создадим отсортированный массив по полученным индексам
  for (i=0;i<GeneCount+1;i++)
    for (u=0;u<PopulChromosCount;u++)
      PopulationTemp[i][u]=Population[i][Indexes[u]];
  //Скопируем отсортированный массив обратно
  for (i=0;i<GeneCount+1;i++)
    for (u=0;u<PopulChromosCount;u++)
      Population[i][u]=PopulationTemp[i][u];
}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Генератор случайных чисел из заданного интервала.
double RNDfromCI(double Minimum,double Maximum) 
{ return(Minimum+((Maximum-Minimum)*MathRand()/32767.5));}
//————————————————————————————————————————————————————————————————————————
 
//————————————————————————————————————————————————————————————————————————
//Выбор в дискретном пространстве.
//Режимы:
//1-ближайшее снизу
//2-ближайшее сверху 
//любое-до ближайшего
double SelectInDiscreteSpace
(
double In, 
double InMin, 
double InMax, 
double step, 
int    RoundMode
)
{
  if (step==0.0)
    return(In);
  // обеспечим правильность границ
  if ( InMax < InMin )
  {
    double temp = InMax; InMax = InMin; InMin = temp;
  }
  // при нарушении - вернем нарушенную границу
  if ( In < InMin ) return( InMin );
  if ( In > InMax ) return( InMax );
  if ( InMax == InMin || step <= 0.0 ) return( InMin );
  // приведем к заданному масштабу
  step = (InMax - InMin) / MathCeil ( (InMax - InMin) / step );
  switch ( RoundMode )
  {
  case 1:  return( InMin + step * MathFloor ( ( In - InMin ) / step ) );
  case 2:  return( InMin + step * MathCeil  ( ( In - InMin ) / step ) );
  default: return( InMin + step * MathRound ( ( In - InMin ) / step ) );
  }
}
//———————————————————————————————————————————————————————————————————————
Для использования библиотеки UGAlib необходимо написать две функции FitnessFunction и ServiceFunction.
Функция FitnessFunction получает на вход индекс хромосомы и рассчитывает для нее значение, по которому ведется оптимизация генов хромосомы.
Функция ServiceFunction может выводить значение фитнес функции и остальные гены эталонной хромосомы при каждом проходе оптимизации.
В качестве примера рассмотрим оптимизацию параметров индикатора, созданного в главе «Создание индикатора на основе модулей торговых сигналов эксперта».
Модифицируем код индикатора таким образом, чтобы рассчитывать виртуальные сделки на покупку и продажу финансового инструмента по сигналам индикатора.
#property indicator_chart_window
 
#include <Expert\ExpertInd.mqh>
#include <Expert\Signal\MySignal\SignalMACDExInd.mqh>
#include <Expert\Signal\MySignal\SignalMAExInd.mqh>
 
#property indicator_buffers 2
#property indicator_plots   1
#property indicator_type1    DRAW_COLOR_LINE
#property indicator_color1  clrBlack,clrRed,clrLawnGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
 
double         InBuffer[];
double         ColorBuffer[];
int    bars_calculated=0;
 
input int                Signal_ThresholdOpen          =20;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose         =20;          // Signal threshold value to close [0...100]
 
input int                Signal_MACD_PeriodFast        =12;          // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
input int                Signal_MACD_PeriodSlow        =24;          // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
input int                Signal_MACD_PeriodSignal      =9;           // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
input ENUM_APPLIED_PRICE Signal_MACD_Applied           =PRICE_CLOSE; // MACD(12,24,9,PRICE_CLOSE) Prices series
input double             Signal_MACD_Weight            =1.0;         // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
input int                Signal_MA_PeriodMA            =12;          // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift               =0;           // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method              =MODE_SMA;    // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied             =PRICE_CLOSE; // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight              =1.0;         // Moving Average(12,0,...) Weight [0...1.0]
 
CExpertInd ExtExpert;
CSignalMAInd *filter0 = new CSignalMAInd;
CSignalMACDInd *filter1 = new CSignalMACDInd;
 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
  
//--- Initializing expert
if(!ExtExpert.Init(Symbol(),Period(),true,100))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Creating signal
   CExpertSignal *signal=new CExpertSignal;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//---
   ExtExpert.InitSignal(signal);  
 
filter0.PeriodMA(Signal_MA_PeriodMA);
filter0.Shift(Signal_MA_Shift);
filter0.Method(Signal_MA_Method);
filter0.Applied(Signal_MA_Applied);
 
filter1.PeriodFast(Signal_MACD_PeriodFast);
filter1.PeriodSlow(Signal_MACD_PeriodSlow);
filter1.PeriodSignal(Signal_MACD_PeriodSignal);
filter1.Applied(Signal_MACD_Applied);
 
signal.AddFilter(filter0);
signal.AddFilter(filter1);
if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- ok
   //--- indicator buffers mapping
   SetIndexBuffer(0,InBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ColorBuffer,INDICATOR_COLOR_INDEX);
   
ArraySetAsSeries(InBuffer,true);   
ArraySetAsSeries(ColorBuffer,true);
 
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- количество копируемых значений из индикатора
   int values_to_copy;
//--- узнаем количество рассчитанных значений в индикаторе
int calculated=MathMin(filter0.BarsCalculatedInd(), filter1.BarsCalculatedInd());
   if(calculated<=0)
     {
      PrintFormat("BarsCalculated() вернул %d, код ошибки %d",calculated,GetLastError());
      return(0);
     }
//--- если это первый запуск вычислений нашего индикатора или изменилось количество значений в индикаторе
//--- или если необходимо рассчитать индикатор для двух или более баров (значит что-то изменилось в истории)
   if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
     {
      //--- если массив больше, чем значений в индикаторе на паре symbol/period, то копируем не все 
      //--- в противном случае копировать будем меньше, чем размер индикаторных буферов
      if(calculated>rates_total) values_to_copy=rates_total;
      else                       values_to_copy=calculated;
     }
   else
     {
      //--- значит наш индикатор рассчитывается не в первый раз и с момента последнего вызова OnCalculate())
      //--- для расчета добавилось не более одного бара
      values_to_copy=(rates_total-prev_calculated)+1;
     }
 
 bars_calculated=calculated;
 
 ArraySetAsSeries(open,true);
  
 if(values_to_copy>1)
{
 
ExtExpert.RefreshInd();
 
//--- флаг покупки
bool flagBuy=false;
//--флаг продажи
bool flagSell=false;
//-- покупка
double priceBuy=0;
double priceStopBuy=0;
double profitBuy=0;
double profitTotalBuy=0;
int countProfitBuyPlus=0;
int countProfitBuyMinus=0;
//--продажа
double priceSell=0;
double priceStopSell=0;
double profitSell=0;
double profitTotalSell=0;
int countProfitSellPlus=0;
int countProfitSellMinus=0;
//--спред
double sp=0.0002;
 
   
for (int i=0; i<values_to_copy; i++){
 
ColorBuffer[i]=0;
InBuffer[i]=open[i];
 
double result0=Signal_MA_Weight*(filter0.LongConditionInd(i)-filter0.ShortConditionInd(i));
double result1=Signal_MACD_Weight*(filter1.LongConditionInd(i)-filter1.ShortConditionInd(i));
double result=(result0+result1)/2;
 
if(result>=Signal_ThresholdOpen)
     {
 ColorBuffer[i]=2;  
  if(flagSell==true){
flagSell=false;
priceStopSell=InBuffer[i];
profitSell=(priceStopSell-priceSell-sp)*10000;
if(profitSell>0){countProfitSellPlus++;}else{countProfitSellMinus++;}
profitTotalSell=profitTotalSell+profitSell;
 }
 if(flagBuy==false){
 flagBuy=true;
 priceBuy=InBuffer[i];
 }   
     } 
     
 if(-result>=Signal_ThresholdOpen){
 ColorBuffer[i]=1;
 if(flagBuy==true){
flagBuy=false;
priceStopBuy=InBuffer[i];
profitBuy=(priceStopBuy-priceBuy-sp)*10000;
if(profitBuy>0){countProfitBuyPlus++;}else{countProfitBuyMinus++;}
profitTotalBuy=profitTotalBuy+profitBuy;
 }
 if(flagSell==false){
priceSell=InBuffer[i];
flagSell=true; 
}
 } 
}
//Print(" ProfitBuy ", profitTotalBuy," countProfitBuyPlus ",countProfitBuyPlus," countProfitBuyMinus ",countProfitBuyMinus);
//Print(" ProfitSell ", profitTotalSell," countProfitSellPlus ",countProfitSellPlus," countProfitSellMinus ",countProfitSellMinus);
 
}
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
Напишем фитнес функцию на основе этого индикатора. Оптимизировать будем весы сигналов MA и MACD для получения максимального профита.
void FitnessFunction(int chromos)
{
 
double _MACD_Weight=0.0;
double _MA_Weight=0.0;
double sum=0.0;
int    cnt=1;
 
while (cnt<=GeneCount)
  {
    _MACD_Weight=Colony[cnt][chromos];
    cnt++;
    _MA_Weight=Colony[cnt][chromos];
    cnt++;
 
int handleInd; 
double BufferInd[]; 
double BufferColorInd[]; 
 
 
handleInd=iCustom(NULL,0,"MAMACDExInd", 
Signal_ThresholdOpen,
Signal_ThresholdClose,
Signal_MACD_PeriodFast,
Signal_MACD_PeriodSlow,
Signal_MACD_PeriodSignal,
Signal_MACD_Applied,
_MACD_Weight,
Signal_MA_PeriodMA,
Signal_MA_Shift,
Signal_MA_Method,
Signal_MA_Applied,
_MA_Weight 
); 
 
ResetLastError();
 
int size=5000;
 
if(CopyBuffer(handleInd,0,0,size,BufferInd)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора 0, код ошибки %d",GetLastError()); 
            
     } 
 
 if(CopyBuffer(handleInd,1,0,size,BufferColorInd)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора 1, код ошибки %d",GetLastError()); 
          
     } 
 ArraySetAsSeries(BufferInd,true);  
 ArraySetAsSeries(BufferColorInd,true);  
 
 //--- флаг покупки
bool flagBuy=false;
//--флаг продажи
bool flagSell=false;
//-- покупка
double priceBuy=0;
double priceStopBuy=0;
double profitBuy=0;
double profitTotalBuy=0;
int countProfitBuyPlus=0;
int countProfitBuyMinus=0;
//--продажа
double priceSell=0;
double priceStopSell=0;
double profitSell=0;
double profitTotalSell=0;
int countProfitSellPlus=0;
int countProfitSellMinus=0;
//--спред
double sp=0.0002;
 
for (int i=0; i<size; i++){
 
if(BufferColorInd[i]==2)
     {
if(flagSell==true){
flagSell=false;
priceStopSell=BufferInd[i];
profitSell=(priceStopSell-priceSell-sp)*10000;
if(profitSell>0){countProfitSellPlus++;}else{countProfitSellMinus++;}
profitTotalSell=profitTotalSell+profitSell;
 }
 if(flagBuy==false){
 flagBuy=true;
 priceBuy=BufferInd[i];
 }
     } 
     
 if(BufferColorInd[i]==1){
if(flagBuy==true){
flagBuy=false;
priceStopBuy=BufferInd[i];
profitBuy=(priceStopBuy-priceBuy-sp)*10000;
if(profitBuy>0){countProfitBuyPlus++;}else{countProfitBuyMinus++;}
profitTotalBuy=profitTotalBuy+profitBuy;
 }
 if(flagSell==false){
priceSell=BufferInd[i];
flagSell=true; 
}
 } 
}
//Print(" ProfitBuy ", profitTotalBuy," countProfitBuyPlus ",countProfitBuyPlus," countProfitBuyMinus ",countProfitBuyMinus);
//Print(" ProfitSell ", profitTotalSell," countProfitSellPlus ",countProfitSellPlus," countProfitSellMinus ",countProfitSellMinus);
   
sum = profitTotalBuy + profitTotalSell;
  }
  AmountStartsFF++;
  Colony[0][chromos]=sum;
}
 
void ServiceFunction()
  
double _MACD_Weight=0.0;
double _MA_Weight=0.0;
int    cnt=1;
 
    while (cnt<=GeneCount)
    {
      _MACD_Weight=Chromosome[cnt];
      cnt++;
      _MA_Weight=Chromosome[cnt];
      cnt++;
    }
 Print("Fitness func =",Chromosome[0],"\n",
            "Полученные значения аргументов:","\n",
            "_MACD_Weight =",Chromosome[1],"\n",
            "_MA_Weight =",Chromosome[2],"\n"
            );
  
}
//———————————————————————————————————————————————————————————————————
Напишем скрипт, который будет оптимизировать параметры индикатора с помощью генетического алгоритма.
#include <Fitness\MAMACDFitness.mqh>
#include <Fitness\UGAlib.mqh>
 
double ReplicationPortion_E  = 100.0; //Доля Репликации.
double NMutationPortion_E    = 10.0;  //Доля Естественной мутации.
double ArtificialMutation_E  = 10.0;  //Доля Искусственной мутации.
double GenoMergingPortion_E  = 20.0;  //Доля Заимствования генов.
double CrossingOverPortion_E = 20.0;  //Доля Кроссинговера.
//---
double ReplicationOffset_E   = 0.5;   //Коэффициент смещения границ интервала
double NMutationProbability_E= 5.0;   //Вероятность мутации каждого гена в %
 
//--- inputs for main signal
input int                Signal_ThresholdOpen    =20;           // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose   =20;           // Signal threshold value to close [0...100]
input double             Signal_PriceLevel       =0.0;          // Price level to execute a deal
input double             Signal_StopLevel        =50.0;         // Stop Loss level (in points)
input double             Signal_TakeLevel        =50.0;         // Take Profit level (in points)
input int                Signal_Expiration       =4;            // Expiration of pending orders (in bars)
input int                Signal_MA_PeriodMA      =12;           // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift         =0;            // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method        =MODE_SMA;     // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied       =PRICE_CLOSE;  // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight        =1.0;          // Moving Average(12,0,...) Weight [0...1.0]
input int                Signal_MACD_PeriodFast  =12;           // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
input int                Signal_MACD_PeriodSlow  =24;           // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
input int                Signal_MACD_PeriodSignal=9;            // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
input ENUM_APPLIED_PRICE Signal_MACD_Applied     =PRICE_CLOSE;  // MACD(12,24,9,PRICE_CLOSE) Prices series
input double             Signal_MACD_Weight      =1.0;          // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
ChromosomeCount     = 10;    //Кол-во хромосом в колонии
GeneCount           = 2;     //Кол-во генов
Epoch               = 50;    //Кол-во эпох без улучшения
//---
RangeMinimum        = 0.0;  //Минимум диапазона поиска
RangeMaximum        = 1.0;   //Максимум диапазона поиска
Precision           = 0.1;//Требуемая точность
OptimizeMethod      = 2;     //Оптим.:1-Min,другое-Max
ArrayResize(Chromosome,GeneCount+1);
ArrayInitialize(Chromosome,0);   
 
 //Локальные переменные
  int time_start=(int)GetTickCount(),time_end=0;
  //----------------------------------------------------------------------
 
  //Запуск главной ф-ии UGA
  UGA
  (
  ReplicationPortion_E, //Доля Репликации.
  NMutationPortion_E,   //Доля Естественной мутации.
  ArtificialMutation_E, //Доля Искусственной мутации.
  GenoMergingPortion_E, //Доля Заимствования генов.
  CrossingOverPortion_E,//Доля Кроссинговера.
  //---
  ReplicationOffset_E,  //Коэффициент смещения границ интервала
  NMutationProbability_E//Вероятность мутации каждого гена в %
  );
  //----------------------------------
  time_end=(int)GetTickCount();
  //----------------------------------
  Print(time_end-time_start," мс - Время исполнения");
  //----------------------------------
  
  }
//+------------------------------------------------------------------+
Запустив скрипт, получим следующий результат:
Fitness func =402.2999999999981
Полученные значения аргументов:
_MACD_Weight =0.1
_MA_Weight =0.4
11498 мс - Время исполнения
Используемый индикатор основан на применении классов CiMA и CiMACD, имеющих проблемы с глубиной истории, потому параметр size в фитнес функции не может быть большим.
Перепишем индикатор, используя более низкоуровневый интерфейс.
#property indicator_chart_window
 
#include <Expert\ExpertInd.mqh>
#include <Expert\Signal\MySignal\SignalMACDInd.mqh>
#include <Expert\Signal\MySignal\SignalMAInd.mqh>
 
#property indicator_buffers 2
#property indicator_plots   1
#property indicator_type1    DRAW_COLOR_LINE
#property indicator_color1  clrBlack,clrRed,clrLawnGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
 
double         InBuffer[];
double         ColorBuffer[];
int    bars_calculated=0;
 
input int                Signal_ThresholdOpen          =20;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose         =20;          // Signal threshold value to close [0...100]
 
input int                Signal_MACD_PeriodFast        =12;          // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
input int                Signal_MACD_PeriodSlow        =24;          // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
input int                Signal_MACD_PeriodSignal      =9;           // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
input ENUM_APPLIED_PRICE Signal_MACD_Applied           =PRICE_CLOSE; // MACD(12,24,9,PRICE_CLOSE) Prices series
input double             Signal_MACD_Weight            =1.0;         // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
input int                Signal_MA_PeriodMA            =12;          // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift               =0;           // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method              =MODE_SMA;    // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied             =PRICE_CLOSE; // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight              =1.0;         // Moving Average(12,0,...) Weight [0...1.0]
 
CExpertInd ExtExpert;
CSignalMAInd *filter0 = new CSignalMAInd;
CSignalMACDInd *filter1 = new CSignalMACDInd;
 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
  
//--- Initializing expert
if(!ExtExpert.Init(Symbol(),Period(),true,100))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Creating signal
   CExpertSignal *signal=new CExpertSignal;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//---
   ExtExpert.InitSignal(signal);  
 
filter0.PeriodMA(Signal_MA_PeriodMA);
filter0.Shift(Signal_MA_Shift);
filter0.Method(Signal_MA_Method);
filter0.Applied(Signal_MA_Applied);
 
filter1.PeriodFast(Signal_MACD_PeriodFast);
filter1.PeriodSlow(Signal_MACD_PeriodSlow);
filter1.PeriodSignal(Signal_MACD_PeriodSignal);
filter1.Applied(Signal_MACD_Applied);
 
signal.AddFilter(filter0);
signal.AddFilter(filter1);
if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- ok
   //--- indicator buffers mapping
   SetIndexBuffer(0,InBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ColorBuffer,INDICATOR_COLOR_INDEX);
   
ArraySetAsSeries(InBuffer,true);   
ArraySetAsSeries(ColorBuffer,true);
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- количество копируемых значений из индикатора
   int values_to_copy;
//--- узнаем количество рассчитанных значений в индикаторе
int calculated=MathMin(filter0.BarsCalculatedInd(), filter1.BarsCalculatedInd());
   if(calculated<=0)
     {
      PrintFormat("BarsCalculated() вернул %d, код ошибки %d",calculated,GetLastError());
      return(0);
     }
//--- если это первый запуск вычислений нашего индикатора или изменилось количество значений в индикаторе
//--- или если необходимо рассчитать индикатор для двух или более баров (значит что-то изменилось в истории)
   if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
     {
      //--- если массив больше, чем значений в индикаторе на паре symbol/period, то копируем не все 
      //--- в противном случае копировать будем меньше, чем размер индикаторных буферов
      if(calculated>rates_total) values_to_copy=rates_total;
      else                       values_to_copy=calculated;
     }
   else
     {
      //--- значит наш индикатор рассчитывается не в первый раз и с момента последнего вызова OnCalculate())
      //--- для расчета добавилось не более одного бара
      values_to_copy=(rates_total-prev_calculated)+1;
     }
 
 bars_calculated=calculated;
 
 ArraySetAsSeries(open,true);
 ArraySetAsSeries(close,true);
 ArraySetAsSeries(low,true);
 ArraySetAsSeries(high,true);
 
 double _low[];
 ArrayCopy(_low,low);
 double _high[];
 ArrayCopy(_high,high);
 
 ArraySetAsSeries(_low,true);
 ArraySetAsSeries(_high,true);
 
 if(values_to_copy>1)
{
 
ExtExpert.RefreshInd();
 
//--- флаг покупки
bool flagBuy=false;
//--флаг продажи
bool flagSell=false;
//-- покупка
double priceBuy=0;
double priceStopBuy=0;
double profitBuy=0;
double profitTotalBuy=0;
int countProfitBuyPlus=0;
int countProfitBuyMinus=0;
//--продажа
double priceSell=0;
double priceStopSell=0;
double profitSell=0;
double profitTotalSell=0;
int countProfitSellPlus=0;
int countProfitSellMinus=0;
//--спред
double sp=0.0002;
 
 
for (int i=0; i<(values_to_copy-2); i++){
 
ColorBuffer[i]=0;
InBuffer[i]=open[i];
 
double result0=Signal_MA_Weight*(filter0.LongConditionInd(i, values_to_copy, close[i], open[i], low[i])-filter0.ShortConditionInd(i, values_to_copy, close[i], open[i], high[i]));
double result1=Signal_MACD_Weight*(filter1.LongConditionInd(i, values_to_copy, _low, _high)-filter1.ShortConditionInd(i, values_to_copy, _low, _high));
double result=(result0+result1)/2;
 
if(result>=Signal_ThresholdOpen)
     {
 ColorBuffer[i]=2; 
 if(flagSell==true){
flagSell=false;
priceStopSell=InBuffer[i];
profitSell=(priceStopSell-priceSell-sp)*10000;
if(profitSell>0){countProfitSellPlus++;}else{countProfitSellMinus++;}
profitTotalSell=profitTotalSell+profitSell;
 }
 if(flagBuy==false){
 flagBuy=true;
 priceBuy=InBuffer[i];
 }  
     } 
     
 if(-result>=Signal_ThresholdOpen){
 ColorBuffer[i]=1;
 if(flagBuy==true){
flagBuy=false;
priceStopBuy=InBuffer[i];
profitBuy=(priceStopBuy-priceBuy-sp)*10000;
if(profitBuy>0){countProfitBuyPlus++;}else{countProfitBuyMinus++;}
profitTotalBuy=profitTotalBuy+profitBuy;
 }
 if(flagSell==false){
priceSell=InBuffer[i];
flagSell=true; 
}
 } 
 
}
 
//Print(" ProfitBuy ", profitTotalBuy," countProfitBuyPlus ",countProfitBuyPlus," countProfitBuyMinus ",countProfitBuyMinus);
//Print(" ProfitSell ", profitTotalSell," countProfitSellPlus ",countProfitSellPlus," countProfitSellMinus ",countProfitSellMinus);
 
}
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
#include <Expert\Signal\SignalMA.mqh>
class CSignalMAInd : public CSignalMA
  {
  public:
  virtual int       BarsCalculatedInd();
  virtual int       LongConditionInd(int ind, int amount, double close, double open, double low);
  virtual int       ShortConditionInd(int ind, int amount, double close, double open, double high);                  
  };
  
//+------------------------------------------------------------------+
//| Refresh indicators.                                               |
//+------------------------------------------------------------------+  
int CSignalMAInd:: BarsCalculatedInd(){
m_ma.Refresh();
int bars = m_ma.BarsCalculated();
return bars;
}
//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalMAInd::LongConditionInd(int idx, int amount, double close, double open, double low)
  {
  int handle=m_ma.Handle();
  double         iMABuffer[]; 
   if(CopyBuffer(handle,0,0,amount,iMABuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     } 
   ArraySetAsSeries(iMABuffer,true); 
  
   int result=0;
   
   double DiffCloseMA = close - iMABuffer[idx];
   double DiffOpenMA = open - iMABuffer[idx];
   double DiffMA = iMABuffer[idx] - iMABuffer[idx+1];
   double DiffLowMA = low - iMABuffer[idx];
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
   if(DiffCloseMA<0.0)
     {
      //--- the close price is below the indicator
      if(IS_PATTERN_USAGE(1) && DiffOpenMA>0.0 && DiffMA>0.0)
        {
         //--- the open price is above the indicator (i.e. there was an intersection), but the indicator is directed upwards
         result=m_pattern_1;
         //--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
         m_base_price=0.0;
        }
     }
   else
     {
      //--- the close price is above the indicator (the indicator has no objections to buying)
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;
      //--- if the indicator is directed upwards
      if(DiffMA>0.0)
        {
         if(DiffOpenMA<0.0)
           {
            //--- if the model 2 is used
            if(IS_PATTERN_USAGE(2))
              {
               //--- the open price is below the indicator (i.e. there was an intersection)
               result=m_pattern_2;
               //--- suggest to enter the market at the "roll back"
               m_base_price=m_symbol.NormalizePrice(iMABuffer[idx]);
              }
           }
         else
           {
            //--- if the model 3 is used and the open price is above the indicator
            if(IS_PATTERN_USAGE(3) && DiffLowMA<0.0)
              {
               //--- the low price is below the indicator
               result=m_pattern_3;
               //--- consider that this is a formed "piercing" and suggest to enter the market at the current price
               m_base_price=0.0;
              }
           }
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalMAInd::ShortConditionInd(int idx, int amount, double close, double open, double high)
  {
  int handle=m_ma.Handle();
  double         iMABuffer[]; 
   if(CopyBuffer(handle,0,0,amount,iMABuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     } 
   ArraySetAsSeries(iMABuffer,true);  
 
   int result=0;
   
   double DiffCloseMA = close - iMABuffer[idx];
   double DiffOpenMA = open - iMABuffer[idx];
   double DiffMA = iMABuffer[idx] - iMABuffer[idx+1];
   double DiffHighMA = high - iMABuffer[idx];
   
//--- analyze positional relationship of the close price and the indicator at the first analyzed bar
   if(DiffCloseMA>0.0)
     {
      //--- the close price is above the indicator
      if(IS_PATTERN_USAGE(1) && DiffOpenMA<0.0 && DiffMA<0.0)
        {
         //--- the open price is below the indicator (i.e. there was an intersection), but the indicator is directed downwards
         result=m_pattern_1;
         //--- consider that this is an unformed "piercing" and suggest to enter the market at the current price
         m_base_price=0.0;
        }
     }
   else
     {
      //--- the close price is below the indicator (the indicator has no objections to buying)
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;
      //--- the indicator is directed downwards
      if(DiffMA<0.0)
        {
         if(DiffOpenMA>0.0)
           {
            //--- if the model 2 is used
            if(IS_PATTERN_USAGE(2))
              {
               //--- the open price is above the indicator (i.e. there was an intersection)
               result=m_pattern_2;
               //--- suggest to enter the market at the "roll back"
               m_base_price=m_symbol.NormalizePrice(iMABuffer[idx]);
              }
           }
         else
           {
            //--- if the model 3 is used and the open price is below the indicator
            if(IS_PATTERN_USAGE(3) && DiffHighMA>0.0)
              {
               //--- the high price is above the indicator
               result=m_pattern_3;
               //--- consider that this is a formed "piercing" and suggest to enter the market at the current price
               m_base_price=0.0;
              }
           }
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
#include <Expert\Signal\SignalMACD.mqh>
 
class CSignalMACDInd : public CSignalMACD
  {
  public: 
  virtual int       BarsCalculatedInd();                  
  virtual int       LongConditionInd(int ind, int amount, double &low[], double &high[]);
  virtual int       ShortConditionInd(int ind, int amount, double &low[], double &high[]);
  protected:
  int               StateMain(int ind,double &Main[]);
  bool              ExtState(int ind, double &Main[], double &low[], double &high[]);
  };
 //+------------------------------------------------------------------+
//| Refresh indicators.                                               |
//+------------------------------------------------------------------+  
int CSignalMACDInd:: BarsCalculatedInd(){
m_MACD.Refresh();
int bars = m_MACD.BarsCalculated();
return bars;
}
 
//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalMACDInd::LongConditionInd(int idx, int amount, double &low[], double &high[])
  {
  
int handle=m_MACD.Handle();
double         MACDBuffer[]; 
double         SignalBuffer[];  
   if(CopyBuffer(handle,0,0,amount,MACDBuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     }
     if(CopyBuffer(handle,1,0,amount,SignalBuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     }  
   ArraySetAsSeries(MACDBuffer,true);
   ArraySetAsSeries(SignalBuffer,true); 
   int result=0;
   
   double DiffMain = MACDBuffer[idx]-MACDBuffer[idx+1];
   double DiffMain_1 = MACDBuffer[idx+1]-MACDBuffer[idx+2];
   double State = MACDBuffer[idx]- SignalBuffer[idx];
   double State_1 = MACDBuffer[idx+1]- SignalBuffer[idx+1];
   
//--- check direction of the main line
   if(DiffMain>0.0)
     {
      //--- the main line is directed upwards, and it confirms the possibility of price growth
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain_1<0.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 is used, look for an intersection of the main and signal line
      if(IS_PATTERN_USAGE(2) && State>0.0 && State_1<0.0)
         result=m_pattern_2;      // signal number 2
      //--- if the model 3 is used, look for an intersection of the main line and the zero level
      if(IS_PATTERN_USAGE(3) && MACDBuffer[idx]>0.0 && MACDBuffer[idx+1]<0.0)
         result=m_pattern_3;      // signal number 3
      //--- if the models 4 or 5 are used and the main line turned upwards below the zero level, look for divergences
      if((IS_PATTERN_USAGE(4) || IS_PATTERN_USAGE(5)) && MACDBuffer[idx]<0.0)
        {
         //--- perform the extended analysis of the oscillator state
         ExtState(idx, MACDBuffer, low, high);
         //--- if the model 4 is used, look for the "divergence" signal
         if(IS_PATTERN_USAGE(4) && CompareMaps(1,1)) // 0000 0001b
            result=m_pattern_4;   // signal number 4
         //--- if the model 5 is used, look for the "double divergence" signal
         if(IS_PATTERN_USAGE(5) && CompareMaps(0x11,2)) // 0001 0001b
            return(m_pattern_5);  // signal number 5
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalMACDInd::ShortConditionInd(int idx, int amount, double &low[], double &high[])
  {
   int handle=m_MACD.Handle();
double         MACDBuffer[]; 
double         SignalBuffer[];  
   if(CopyBuffer(handle,0,0,amount,MACDBuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     }
     if(CopyBuffer(handle,1,0,amount,SignalBuffer)<0) 
     { 
      //--- если копирование не удалось, сообщим код ошибки 
      PrintFormat("Не удалось скопировать данные из индикатора iMA, код ошибки %d",GetLastError()); 
      //--- завершим с нулевым результатом - это означает, что индикатор будет считаться нерассчитанным 
      return(-1); 
     }  
   ArraySetAsSeries(MACDBuffer,true);
   ArraySetAsSeries(SignalBuffer,true); 
   int result=0;
   
   double DiffMain = MACDBuffer[idx]-MACDBuffer[idx+1];
   double DiffMain_1 = MACDBuffer[idx+1]-MACDBuffer[idx+2];
   double State = MACDBuffer[idx]- SignalBuffer[idx];
   double State_1 = MACDBuffer[idx+1]- SignalBuffer[idx+1];
   
//--- check direction of the main line
   if(DiffMain<0.0)
     {
      //--- main line is directed downwards, confirming a possibility of falling of price
      if(IS_PATTERN_USAGE(0))
         result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, look for a reverse of the main line
      if(IS_PATTERN_USAGE(1) && DiffMain_1>0.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 is used, look for an intersection of the main and signal line
      if(IS_PATTERN_USAGE(2) && State<0.0 && State_1>0.0)
         result=m_pattern_2;      // signal number 2
      //--- if the model 3 is used, look for an intersection of the main line and the zero level
      if(IS_PATTERN_USAGE(3) && MACDBuffer[idx]<0.0 && MACDBuffer[idx+1]>0.0)
         result=m_pattern_3;      // signal number 3
      //--- if the models 4 or 5 are used and the main line turned downwards above the zero level, look for divergences
      if((IS_PATTERN_USAGE(4) || IS_PATTERN_USAGE(5)) && MACDBuffer[idx]>0.0)
        {
         //--- perform the extended analysis of the oscillator state
         ExtState(idx, MACDBuffer, low, high);
         //--- if the model 4 is used, look for the "divergence" signal
         if(IS_PATTERN_USAGE(4) && CompareMaps(1,1)) // 0000 0001b
            result=m_pattern_4;   // signal number 4
         //--- if the model 5 is used, look for the "double divergence" signal
         if(IS_PATTERN_USAGE(5) && CompareMaps(0x11,2)) // 0001 0001b
            return(m_pattern_5);  // signal number 5
        }
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+  
//+------------------------------------------------------------------+
//| Check of the oscillator state.                                   |
//+------------------------------------------------------------------+
int CSignalMACDInd::StateMain(int ind, double &Main[])
  {
   int    res=0;
   double var;
//---
   for(int i=ind;;i++)
     {
      if(Main[i+1]==EMPTY_VALUE)
         break;
      var=(Main[i]-Main[i+1]);
      if(res>0)
        {
         if(var<0)
            break;
         res++;
         continue;
        }
      if(res<0)
        {
         if(var>0)
            break;
         res--;
         continue;
        }
      if(var>0)
         res++;
      if(var<0)
         res--;
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Extended check of the oscillator state consists                  |
//| in forming a bit-map according to certain rules,                 |
//| which shows ratios of extremums of the oscillator and price.     |
//+------------------------------------------------------------------+
bool CSignalMACDInd::ExtState(int ind, double &Main[], double &low[], double &high[])
  {
//--- operation of this method results in a bit-map of extremums
//--- practically, the bit-map of extremums is an "array" of 4-bit fields
//--- each "element of the array" definitely describes the ratio
//--- of current extremums of the oscillator and the price with previous ones
//--- purpose of bits of an element of the analyzed bit-map
//--- bit 3 - not used (always 0)
//--- bit 2 - is equal to 1 if the current extremum of the oscillator is "more extreme" than the previous one
//---         (a higher peak or a deeper valley), otherwise - 0
//--- bit 1 - not used (always 0)
//--- bit 0 - is equal to 1 if the current extremum of price is "more extreme" than the previous one
//---         (a higher peak or a deeper valley), otherwise - 0
//--- in addition to them, the following is formed:
//--- array of values of extremums of the oscillator,
//--- array of values of price extremums and
//--- array of "distances" between extremums of the oscillator (in bars)
//--- it should be noted that when using the results of the extended check of state,
//--- you should consider, which extremum of the oscillator (peak or valley)
//--- is the "reference point" (i.e. was detected first during the analysis)
//--- if a peak is detected first then even elements of all arrays
//--- will contain information about peaks, and odd elements will contain information about valleys
//--- if a valley is detected first, then respectively in reverse
   int    pos=ind,off,index;
   uint   map;                 // intermediate bit-map for one extremum
//---
   m_extr_map=0;
   for(int i=0;i<10;i++)
     {
      off=StateMain(pos, Main);
      if(off>0)
        {
         //--- minimum of the oscillator is detected
         pos+=off;
         m_extr_pos[i]=pos;
         m_extr_osc[i]=Main[pos];
         if(i>1)
           {
           index = ArrayMinimum (low,pos-2,5);
            m_extr_pr[i]=low[index];
            //--- form the intermediate bit-map
            map=0;
            if(m_extr_pr[i-2]<m_extr_pr[i])
               map+=1;  // set bit 0
            if(m_extr_osc[i-2]<m_extr_osc[i])
               map+=4;  // set bit 2
            //--- add the result
            m_extr_map+=map<<(4*(i-2));
           }
         else
         index = ArrayMinimum (low,pos-1,4);
            m_extr_pr[i]=low[index];
        }
      else
        {
         //--- maximum of the oscillator is detected
         pos-=off;
         m_extr_pos[i]=pos;
         m_extr_osc[i]=Main[pos];
         if(i>1)
           {
           index = ArrayMaximum (high,pos-2,5);
            m_extr_pr[i]=high[index];
            //--- form the intermediate bit-map
            map=0;
            if(m_extr_pr[i-2]>m_extr_pr[i])
               map+=1;  // set bit 0
            if(m_extr_osc[i-2]>m_extr_osc[i])
               map+=4;  // set bit 2
            //--- add the result
            m_extr_map+=map<<(4*(i-2));
           }
         else
         index = ArrayMaximum (high,pos-1,4);
            m_extr_pr[i]=high[index];
        }
     }
//---
   return(true);
  }
Включим этот индикатор в фитнес функцию.
handleInd=iCustom(NULL,0,"MAMACDIndicator", 
Signal_ThresholdOpen,
Signal_ThresholdClose,
Signal_MACD_PeriodFast,
Signal_MACD_PeriodSlow,
Signal_MACD_PeriodSignal,
Signal_MACD_Applied,
_MACD_Weight,
Signal_MA_PeriodMA,
Signal_MA_Shift,
Signal_MA_Method,
Signal_MA_Applied,
_MA_Weight 
);
И запустим скрипт.
Глубина истории увеличится, но при этом существенно увеличится и время оптимизации – на порядок, при том же самом результате на 1000 барах. Поэтому для самооптимизации советника будем использовать первый вариант индикатора с глубиной истории в 1000 бар.
С помощью мастера MQL5 Wizard сгенерируем код советника на основе сигналов MA и MACD и добавим в него самооптимизацию параметров Signal_MA_Weight и Signal_MACD_Weight с использованием приведенной выше фитнес функции.
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\Signal\SignalMA.mqh>
#include <Expert\Signal\SignalMACD.mqh>
//--- available trailing
#include <Expert\Trailing\TrailingNone.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>
 
#include <Fitness\MAMACDFitness.mqh>
#include <Fitness\UGAlib.mqh>
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string             Expert_Title            ="MAMACDExpert"; // Document name
ulong                    Expert_MagicNumber      =4322;           // 
bool                     Expert_EveryTick        =false;          // 
//--- inputs for main signal
input int                Signal_ThresholdOpen    =20;             // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose   =20;             // Signal threshold value to close [0...100]
input double             Signal_PriceLevel       =0.0;            // Price level to execute a deal
input double             Signal_StopLevel        =50.0;           // Stop Loss level (in points)
input double             Signal_TakeLevel        =50.0;           // Take Profit level (in points)
input int                Signal_Expiration       =4;              // Expiration of pending orders (in bars)
input int                Signal_MA_PeriodMA      =12;             // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift         =0;              // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method        =MODE_SMA;       // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied       =PRICE_CLOSE;    // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight        =1.0;            // Moving Average(12,0,...) Weight [0...1.0]
input int                Signal_MACD_PeriodFast  =12;             // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
input int                Signal_MACD_PeriodSlow  =24;             // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
input int                Signal_MACD_PeriodSignal=9;              // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
input ENUM_APPLIED_PRICE Signal_MACD_Applied     =PRICE_CLOSE;    // MACD(12,24,9,PRICE_CLOSE) Prices series
input double             Signal_MACD_Weight      =1.0;            // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
//--- inputs for money
input double             Money_FixLot_Percent    =10.0;           // Percent
input double             Money_FixLot_Lots       =1.0;            // Fixed volume
//+------------------------------------------------------------------+
//| Global expert object                                             |
//+------------------------------------------------------------------+
CExpert ExtExpert;
CSignalMA *filter0=new CSignalMA;
CSignalMACD *filter1=new CSignalMACD;
 
double ReplicationPortion_E  = 100.0; //Доля Репликации.
double NMutationPortion_E    = 10.0;  //Доля Естественной мутации.
double ArtificialMutation_E  = 10.0;  //Доля Искусственной мутации.
double GenoMergingPortion_E  = 20.0;  //Доля Заимствования генов.
double CrossingOverPortion_E = 20.0;  //Доля Кроссинговера.
//---
double ReplicationOffset_E   = 0.5;   //Коэффициент смещения границ интервала
double NMutationProbability_E= 5.0;   //Вероятность мутации каждого гена в %
 
double balance;
 
//+------------------------------------------------------------------+
//| Initialization function of the expert                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initializing expert
if(!ExtExpert.Init(Symbol(),Period(),Expert_EveryTick,Expert_MagicNumber))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing expert");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Creating signal
   CExpertSignal *signal=new CExpertSignal;
   if(signal==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating signal");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//---
   ExtExpert.InitSignal(signal);
   signal.ThresholdOpen(Signal_ThresholdOpen);
   signal.ThresholdClose(Signal_ThresholdClose);
   signal.PriceLevel(Signal_PriceLevel);
   signal.StopLevel(Signal_StopLevel);
   signal.TakeLevel(Signal_TakeLevel);
   signal.Expiration(Signal_Expiration);
//--- Creating filter CSignalMA
 
if(filter0==NULL)
  {
   //--- failed
   printf(__FUNCTION__+": error creating filter0");
   ExtExpert.Deinit();
   return(INIT_FAILED);
  }
signal.AddFilter(filter0);
//--- Set filter parameters
filter0.PeriodMA(Signal_MA_PeriodMA);
filter0.Shift(Signal_MA_Shift);
filter0.Method(Signal_MA_Method);
filter0.Applied(Signal_MA_Applied);
filter0.Weight(Signal_MA_Weight);
//--- Creating filter CSignalMACD
 
if(filter1==NULL)
  {
   //--- failed
   printf(__FUNCTION__+": error creating filter1");
   ExtExpert.Deinit();
   return(INIT_FAILED);
  }
signal.AddFilter(filter1);
//--- Set filter parameters
filter1.PeriodFast(Signal_MACD_PeriodFast);
filter1.PeriodSlow(Signal_MACD_PeriodSlow);
filter1.PeriodSignal(Signal_MACD_PeriodSignal);
filter1.Applied(Signal_MACD_Applied);
filter1.Weight(Signal_MACD_Weight);
//--- Creation of trailing object
  CTrailingNone *trailing=new CTrailingNone;
   if(trailing==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating trailing");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Add trailing to expert (will be deleted automatically))
   if(!ExtExpert.InitTrailing(trailing))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing trailing");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Set trailing parameters
//--- Creation of money object
CMoneyFixedLot *money=new CMoneyFixedLot;
   if(money==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating money");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Add money to expert (will be deleted automatically))
   if(!ExtExpert.InitMoney(money))
     {
      //--- failed
      printf(__FUNCTION__+": error initializing money");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Set money parameters
money.Percent(Money_FixLot_Percent);
money.Lots(Money_FixLot_Lots);
//--- Check all trading objects parameters
   if(!ExtExpert.ValidationSettings())
     {
      //--- failed
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(INIT_FAILED);
     }
 
ChromosomeCount     = 100;    //Кол-во хромосом в колонии
GeneCount           = 2;     //Кол-во генов
Epoch               = 50;    //Кол-во эпох без улучшения
//---
RangeMinimum        = 0.0;  //Минимум диапазона поиска
RangeMaximum        = 1.0;   //Максимум диапазона поиска
Precision           = 0.1;//Требуемая точность
OptimizeMethod      = 2;     //Оптим.:1-Min,другое-Max
ArrayResize(Chromosome,GeneCount+1);
ArrayInitialize(Chromosome,0);  
 
balance=AccountInfoDouble(ACCOUNT_BALANCE);
  
//--- ok
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinitialization function of the expert                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtExpert.Deinit();
  }
//+------------------------------------------------------------------+
//| "Tick" event handler function                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
  if(AccountInfoDouble(ACCOUNT_BALANCE)>balance) balance=AccountInfoDouble(ACCOUNT_BALANCE);
   double bd = ((balance-AccountInfoDouble(ACCOUNT_BALANCE))/balance)*100;
   
   if(bd>10){
 
   UGA
  (
  ReplicationPortion_E, //Доля Репликации.
  NMutationPortion_E,   //Доля Естественной мутации.
  ArtificialMutation_E, //Доля Искусственной мутации.
  GenoMergingPortion_E, //Доля Заимствования генов.
  CrossingOverPortion_E,//Доля Кроссинговера.
  //---
  ReplicationOffset_E,  //Коэффициент смещения границ интервала
  NMutationProbability_E//Вероятность мутации каждого гена в %
  );
 
double _MACD_Weight=0.0;
double _MA_Weight=0.0;
int    cnt=1;
 
    while (cnt<=GeneCount)
    {
      _MACD_Weight=Chromosome[cnt];
      cnt++;
      _MA_Weight=Chromosome[cnt];
      cnt++;
    }
filter0.Weight(_MA_Weight);
filter1.Weight(_MACD_Weight);   
 
balance=AccountInfoDouble(ACCOUNT_BALANCE);
}
   ExtExpert.OnTick();
  }
//+------------------------------------------------------------------+
//| "Trade" event handler function                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   ExtExpert.OnTrade();
  }
//+------------------------------------------------------------------+
//| "Timer" event handler function                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   ExtExpert.OnTimer();
  }
//+------------------------------------------------------------------+
Здесь в функции OnTick при превышении порога просадки баланса вызывается функция UGA генетического алгоритма, которая оптимизирует веса сигналов.
В фитнес функцию перед копированием буферов индикатора добавим вызов Sleep(1000); для того, чтобы индикатор успел рассчитаться.
При тестировании советника на паре EURUSD на периоде H1 (2016.01.01 - 2016.06.30) без самооптимизации получаем следующий результат:
Чистая прибыль: 26.00 
Абсолютная просадка по балансу: 4 431.00 
Абсолютная просадка по средствам: 4 550.00
Общая прибыль: 79 532.00 
Максимальная просадка по балансу: 5 862.00 (50.08%) 
Максимальная просадка по средствам: 5 956.00 (50.57%)
Общий убыток: -79 506.00 
Относительная просадка по балансу: 50.08% (5 862.00) 
Относительная просадка по средствам: 50.57% (5 956.00)
Прибыльность: 1.00 
Матожидание выигрыша: 0.04 
С включенной самооптимизацией советника при тестировании получаем результат:
Чистая прибыль: 384.00 
Абсолютная просадка по балансу: 504.00 
Абсолютная просадка по средствам: 612.00
Общая прибыль: 4 004.00 
Максимальная просадка по балансу: 1 276.00 (10.94%) 
Максимальная просадка по средствам: 1 341.00 (11.44%)
Общий убыток: -3 620.00 
Относительная просадка по балансу: 10.94% (1 276.00) 
Относительная просадка по средствам: 12.43% (1 333.00)
Прибыльность: 1.11 
Матожидание выигрыша: 24.00