Книга: Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5
Назад: Создание индикатора на основе модулей торговых сигналов эксперта
На главную: Предисловие

Генетические алгоритмы

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

Так как сам советник работает на текущем баре и не использует историю символа, код оптимизации параметров советника должен опираться на код индикатора, который в свою очередь создан на основе кода советника.

Для оптимизации параметров советника, или теперь уже параметров индикатора, совпадающих с параметрами советника, можно использовать полный перебор, однако для сокращения времени оптимизации можно применить генетический алгоритм.

Обратимся к терминологии.

Хромосома состоит из набора генов – набора случайно выбранных параметров советника в допустимых диапазонах.

Поколение это набор хромосом.

Фитнес функция 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

Назад: Создание индикатора на основе модулей торговых сигналов эксперта
На главную: Предисловие