English 中文 Español Deutsch 日本語 Português
preview
Эксперименты с нейросетями (Часть 2): Хитрая оптимизация нейросети

Эксперименты с нейросетями (Часть 2): Хитрая оптимизация нейросети

MetaTrader 5Тестер | 17 июля 2022, 18:20
1 775 12
Roman Poshtar
Roman Poshtar

Введение

Добрый день, уважаемые пользователи сообщества MQL5. В прошлой статье Эксперименты с нейросетями (Часть 1): Вспоминая геометрию я поделился с вами своими наблюдениями и экспериментами с нейросетями. Были затронуты вопросы, какие же именно передавать данные в нейросеть и рассмотрен простой пример с использованием перцептрона для получения прибыльной торговой системы. В некоторых моментах мы преуспели, но в ходе работы возникли некоторые сложности, которые мы постараемся решить в этой части. Также мы перейдем к более сложным нейросетям. Для этого воспользуемся библиотекой из статьи Пишем глубокую нейронную сеть с нуля на языке MQL. Автор очень подробно описал принципы ее работы, поэтому мы остановимся только на главных аспектах. Основной задачей данной статьи будет разработка полноценного торгового робота, работающего на нейросети средствами MetaTrader 5 без использования стороннего программного обеспечения.


Основы и примеры

Автор вышеупомянутой библиотеки использует глубокую нейросеть, я же предлагаю для начала уменьшить аппетит и построить сеть со структурой 4-4-3. Всего нужно (4 * 4) + 4 + (4 * 3) + 3 = 35 веса и значения смещения.

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

Значения весов и смещений:

input double w0=1.0;
input double w1=1.0;
input double w2=1.0;
input double w3=1.0;
input double w4=1.0;
input double w5=1.0;
input double w6=1.0;
input double w7=1.0;
input double w8=1.0;
input double w9=1.0;
input double w10=1.0;
input double w11=1.0;
input double w12=1.0;
input double w13=1.0;
input double w14=1.0;
input double w15=1.0;

input double b0=1.0;
input double b1=1.0;
input double b2=1.0;
input double b3=1.0;

input double x0=1.0;
input double x1=1.0;
input double x2=1.0;
input double x3=1.0;
input double x4=1.0;
input double x5=1.0;
input double x6=1.0;
input double x7=1.0;
input double x8=1.0;
input double x9=1.0;
input double x10=1.0;
input double x11=1.0;
input double x12=1.0;
input double x13=1.0;
input double x14=1.0;
input double x15=1.0;

input double s0=1.0;
input double s1=1.0;
input double s2=1.0;

W и X – веса, B и S – параметры смещения.

Подключаем библиотеку нейросети:

#include <DeepNeuralNetwork2.mqh> 

int numInput=4;
int numHiddenA = 4;
int numOutput=3;

DeepNeuralNetwork dnn(numInput,numHiddenA,numOutput);

Далее мы будем рассматривать два примера из моей предыдущей статьи. Одну фигуру и один паттерн с углами наклона. Также посмотрим на результаты стратегии автора библиотеки. И наконец, проверим все это на разных нейросетях -  4-4-3 и 4-4-4-3.  То есть разрабатываем сразу шесть советников.

Передаем фигуру бабочка (конверт). Советник Figure EA: 

r1


int error=CandlePatterns(ind_In1[1], ind_In1[10], ind_In2[1], ind_In2[10], _xValues);//Вызов функции

int CandlePatterns(double v1,double v2,double v3,double v4,double &xInputs[])//Функция
  {

   xInputs[0] = (v1-v2)/Point();
   xInputs[1] = (v3-v4)/Point();
   xInputs[2] = (v1-v4)/Point();
   xInputs[3] = (v3-v2)/Point();
   
   return(1);

  }

Передаем паттерн с углами наклона (4 углa наклона индикатора MA 1 и MA 24). Советник Angle EA:

r2


int error=CandlePatterns(ind_In1[1], ind_In1[5], ind_In1[10], ind_In2[1], ind_In2[5], ind_In2[10], _xValues);//Вызов функции

int CandlePatterns(double v1,double v2,double v3,double v4,double v5,double v6,double &xInputs[])//Функция
  {

   xInputs[0] = (((v1-v2)/Point())/5);
   xInputs[1] = (((v1-v3)/Point())/10);
   xInputs[2] = (((v4-v5)/Point())/5);
   xInputs[3] = (((v4-v6)/Point())/50);
   
   return(1);

  }

Воспользовавшись тестером стратегий, устанавливаем значения оптимизации весов и смещений от -1 до 1 с шагом 0.1. Получаем результат 3.68597592780611e+51 проходов. Плавно переходим к следующему разделу.

r3


Решение проблем оптимизации

При использовании советника в виде, изложенном выше, тестер стратегий проведет чуть больше 10 000 проходов в режиме (Медленная (полный перебор параметров)), что в нашем случае слишком мало для оптимизации нейросети. Использовать режим (Быстрая (генетический алгоритм)) считаю не целесообразным.

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

Функция генератора случайных чисел:

double RNDfromCI(double minp, double maxp) 
{ 
  if(minp == maxp) 
    return (minp); 
  double Minp, Maxp; 
  if(minp > maxp) 
  {
    Minp = maxp; 
    Maxp = minp;
  }
  else 
  {
    Minp = minp; 
    Maxp = maxp;
  }
  return (NormalizeDouble(double(Minp + ((Maxp - Minp) * (double)MathRand() / 32767.0)),1));
}

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

Вносим одну переменную для оптимизации:

input int Passages = 10000;

Оптимизируем параметр Passages от 1 до максимума с шагом 1. Опытным путем узнаем максимальное количество проходов в режиме с одной переменной. Оно составило 100 000 000.  Думаю, нам вполне хватит.

r4

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

input bool Optimization = true;

Основной код для оптимизации размещен в функции инициализации советника OnInit():

if (Optimization==true){
int r=0;
int q=0;

 while(q!=1 && !IsStopped())
 {

string str;
 while(r!=1 && !IsStopped())
 {
   sw0=DoubleToString(RNDfromCI(Min, Max),1);
   sw1=DoubleToString(RNDfromCI(Min, Max),1);
   sw2=DoubleToString(RNDfromCI(Min, Max),1);
   sw3=DoubleToString(RNDfromCI(Min, Max),1);
   sw4=DoubleToString(RNDfromCI(Min, Max),1);
   sw5=DoubleToString(RNDfromCI(Min, Max),1);
   sw6=DoubleToString(RNDfromCI(Min, Max),1);
   sw7=DoubleToString(RNDfromCI(Min, Max),1);
   sw8=DoubleToString(RNDfromCI(Min, Max),1);
   sw9=DoubleToString(RNDfromCI(Min, Max),1);
   sw10=DoubleToString(RNDfromCI(Min, Max),1);
   sw11=DoubleToString(RNDfromCI(Min, Max),1);
   sw12=DoubleToString(RNDfromCI(Min, Max),1);
   sw13=DoubleToString(RNDfromCI(Min, Max),1);
   sw14=DoubleToString(RNDfromCI(Min, Max),1);
   sw15=DoubleToString(RNDfromCI(Min, Max),1);
   
   sb0=DoubleToString(RNDfromCI(Min, Max),1);
   sb1=DoubleToString(RNDfromCI(Min, Max),1);
   sb2=DoubleToString(RNDfromCI(Min, Max),1);
   sb3=DoubleToString(RNDfromCI(Min, Max),1);
   
   sx0=DoubleToString(RNDfromCI(Min, Max),1);
   sx1=DoubleToString(RNDfromCI(Min, Max),1);
   sx2=DoubleToString(RNDfromCI(Min, Max),1);
   sx3=DoubleToString(RNDfromCI(Min, Max),1);
   sx4=DoubleToString(RNDfromCI(Min, Max),1);
   sx5=DoubleToString(RNDfromCI(Min, Max),1);
   sx6=DoubleToString(RNDfromCI(Min, Max),1);
   sx7=DoubleToString(RNDfromCI(Min, Max),1);
   sx8=DoubleToString(RNDfromCI(Min, Max),1);
   sx9=DoubleToString(RNDfromCI(Min, Max),1);
   sx10=DoubleToString(RNDfromCI(Min, Max),1);
   sx11=DoubleToString(RNDfromCI(Min, Max),1);
   
   ss0=DoubleToString(RNDfromCI(Min, Max),1);
   ss1=DoubleToString(RNDfromCI(Min, Max),1);
   ss2=DoubleToString(RNDfromCI(Min, Max),1);
   
   if(StringFind(sw0,".", 0) == -1 ){sw0=sw0+".0";}
   if(StringFind(sw1,".", 0) == -1 ){sw1=sw1+".0";}
   if(StringFind(sw2,".", 0) == -1 ){sw2=sw2+".0";}
   if(StringFind(sw3,".", 0) == -1 ){sw3=sw3+".0";}
   if(StringFind(sw4,".", 0) == -1 ){sw4=sw4+".0";}
   if(StringFind(sw5,".", 0) == -1 ){sw5=sw5+".0";}
   if(StringFind(sw6,".", 0) == -1 ){sw6=sw6+".0";}
   if(StringFind(sw7,".", 0) == -1 ){sw7=sw7+".0";}
   if(StringFind(sw8,".", 0) == -1 ){sw8=sw8+".0";}
   if(StringFind(sw9,".", 0) == -1 ){sw9=sw9+".0";}
   if(StringFind(sw10,".", 0) == -1 ){sw10=sw10+".0";}
   if(StringFind(sw11,".", 0) == -1 ){sw11=sw11+".0";}
   if(StringFind(sw12,".", 0) == -1 ){sw12=sw12+".0";}
   if(StringFind(sw13,".", 0) == -1 ){sw13=sw13+".0";}
   if(StringFind(sw14,".", 0) == -1 ){sw14=sw14+".0";}
   if(StringFind(sw15,".", 0) == -1 ){sw15=sw15+".0";}

   if(StringFind(sb0,".", 0) == -1 ){sb0=sb0+".0";}
   if(StringFind(sb1,".", 0) == -1 ){sb1=sb1+".0";}
   if(StringFind(sb2,".", 0) == -1 ){sb2=sb2+".0";}
   if(StringFind(sb3,".", 0) == -1 ){sb3=sb3+".0";}
   
   if(StringFind(sx0,".", 0) == -1 ){sx0=sx0+".0";}
   if(StringFind(sx1,".", 0) == -1 ){sx1=sx1+".0";}
   if(StringFind(sx2,".", 0) == -1 ){sx2=sx2+".0";}
   if(StringFind(sx3,".", 0) == -1 ){sx3=sx3+".0";}
   if(StringFind(sx4,".", 0) == -1 ){sx4=sx4+".0";}
   if(StringFind(sx5,".", 0) == -1 ){sx5=sx5+".0";}
   if(StringFind(sx6,".", 0) == -1 ){sx6=sx6+".0";}
   if(StringFind(sx7,".", 0) == -1 ){sx7=sx7+".0";}
   if(StringFind(sx8,".", 0) == -1 ){sx8=sx8+".0";}
   if(StringFind(sx9,".", 0) == -1 ){sx9=sx9+".0";}
   if(StringFind(sx10,".", 0) == -1 ){sx10=sx10+".0";}
   if(StringFind(sx11,".", 0) == -1 ){sx11=sx11+".0";}
   
   if(StringFind(ss0,".", 0) == -1 ){ss0=ss0+".0";}
   if(StringFind(ss1,".", 0) == -1 ){ss1=ss1+".0";}
   if(StringFind(ss2,".", 0) == -1 ){ss2=ss2+".0";}
   
   str=sw0+","+sw1+","+sw2+","+sw3+","+sw4+","+sw5+","+sw6+","+sw7+","+sw8+","+sw9+","+sw10+","+sw11+","+sw12+","+sw13+","+sw14+","+sw15
   +","+sb0+","+sb1+","+sb2+","+sb3
   +","+sx0+","+sx1+","+sx2+","+sx3+","+sx4+","+sx5+","+sx6+","+sx7+","+sx8+","+sx9+","+sx10+","+sx11
   +","+ss0+","+ss1+","+ss2;
   if (VerifyPassagesInFile(str)==true)
   {
      ResetLastError();
      filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
      if(filehandle!=INVALID_HANDLE)
       {
        Print("FileOpen OK");
        FileSeek(filehandle, 0, SEEK_END);
        FileWrite(filehandle,Passages,sw0,sw1,sw2,sw3,sw4,sw5,sw6,sw7,sw8,sw9,sw10,sw11,sw12,sw13,sw14,sw15,
        sb0,sb1,sb2,sb3,
        sx0,sx1,sx2,sx3,sx4,sx5,sx6,sx7,sx8,sx9,sx10,sx11,
        ss0,ss1,ss2);
        FileClose(filehandle); 
        
   weight[0]=StringToDouble(sw0);
   weight[1]=StringToDouble(sw1);
   weight[2]=StringToDouble(sw2);
   weight[3]=StringToDouble(sw3);
   weight[4]=StringToDouble(sw4);
   weight[5]=StringToDouble(sw5);
   weight[6]=StringToDouble(sw6);
   weight[7]=StringToDouble(sw7);
   weight[8]=StringToDouble(sw8);
   weight[9]=StringToDouble(sw9);
   weight[10]=StringToDouble(sw10);
   weight[11]=StringToDouble(sw11);
   weight[12]=StringToDouble(sw12);
   weight[13]=StringToDouble(sw13);
   weight[14]=StringToDouble(sw14);
   weight[15]=StringToDouble(sw15);
   
   weight[16]=StringToDouble(sb0);
   weight[17]=StringToDouble(sb1);
   weight[18]=StringToDouble(sb2);
   weight[19]=StringToDouble(sb3);
   
   weight[20]=StringToDouble(sx0);
   weight[21]=StringToDouble(sx1);
   weight[22]=StringToDouble(sx2);
   weight[23]=StringToDouble(sx3);
   weight[24]=StringToDouble(sx4);
   weight[25]=StringToDouble(sx5);
   weight[26]=StringToDouble(sx6);
   weight[27]=StringToDouble(sx7);
   weight[28]=StringToDouble(sx8);
   weight[29]=StringToDouble(sx9);
   weight[30]=StringToDouble(sx10);
   weight[31]=StringToDouble(sx11);
   
   weight[32]=StringToDouble(ss0);
   weight[33]=StringToDouble(ss1);
   weight[34]=StringToDouble(ss2);
        
        r=1;
        q=1;
       }
      else 
       {
       Print("FileOpen ERROR, error ",GetLastError());
       q=0;
       }
   }
 }
 
 }
}

Немного пробежимся по коду с объяснением. Интересной задачей стало научить советник ожидать своей очереди на открытие файла. В режиме оптимизации одновременно на нескольких ядрах процессора я столкнулся с проблемой одновременного открытия одного файла. Эта проблема была решена в первом цикле while. То есть советник не выйдет из функции OnInit() пока не откроет файл для записи. Получилось так, что советники во время оптимизации запускаются по очереди.

Пока на одном ядре идет тестирование, на втором советник может открыть файл для записи. Далее в коде присваиваем всем весам и смещениям случайные параметры в диапазоне Min и Max. Если число получилось круглым, дописываем к нему .0. Складываем все значения в одну строку str. Проверяем эту строку функцией VerifyPassagesInFile(str) — нет ли в файле такой же строки. Если нет, то записываем ее в конец файла и заполняем наши массивы weight[] полученными случайными значениями весов и смещений.

Код функции проверки параметров на идентичность с предыдущими:

bool VerifyPassagesInFile(string st){
string str="";
string str1="";
string str2="";
string str3="";
string str4="";
string str5="";
string str6="";
string str7="";
string str8="";
string str9="";
string str10="";
string str11="";
string str12="";
string str13="";
string str14="";
string str15="";
string str16="";
string str17="";
string str18="";
string str19="";
string str20="";
string str21="";
string str22="";
string str23="";
string str24="";
string str25="";
string str26="";
string str27="";
string str28="";
string str29="";
string str30="";
string str31="";
string str32="";
string str33="";
string str34="";
string str35="";
string str36="";

if (FileIsExist(OptimizationFileName)==
true ){
   ResetLastError();
   filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
   if(filehandle!=INVALID_HANDLE)
     {
      Print("FileCreate OK");
     }
   else Print("FileCreate ERROR, error ",GetLastError());
   FileClose(filehandle); 
}

   ResetLastError();
   filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
   if(filehandle!=INVALID_HANDLE)
     {
      Print("FileOpen OK");
     }
   else Print("FileOpen ERROR, error ",GetLastError());
   
//--- прочитаем данные из файла
 while(!FileIsEnding(filehandle) && !IsStopped())
 {
  str1=FileReadString(filehandle);
  str2=FileReadString(filehandle);
  str3=FileReadString(filehandle);
  str4=FileReadString(filehandle);
  str5=FileReadString(filehandle);
  str6=FileReadString(filehandle);
  str7=FileReadString(filehandle);
  str8=FileReadString(filehandle);
  str9=FileReadString(filehandle);
  str10=FileReadString(filehandle);
  str11=FileReadString(filehandle);
  str12=FileReadString(filehandle);
  str13=FileReadString(filehandle);
  str14=FileReadString(filehandle);
  str15=FileReadString(filehandle);
  str16=FileReadString(filehandle);
  str17=FileReadString(filehandle);
  str18=FileReadString(filehandle);
  str19=FileReadString(filehandle);
  str20=FileReadString(filehandle);
  str21=FileReadString(filehandle);
  str22=FileReadString(filehandle);
  str23=FileReadString(filehandle);
  str24=FileReadString(filehandle);
  str25=FileReadString(filehandle);
  str26=FileReadString(filehandle);
  str27=FileReadString(filehandle);
  str28=FileReadString(filehandle);
  str29=FileReadString(filehandle);
  str30=FileReadString(filehandle);
  str31=FileReadString(filehandle);
  str32=FileReadString(filehandle);
  str33=FileReadString(filehandle);
  str34=FileReadString(filehandle);
  str35=FileReadString(filehandle);
  str36=FileReadString(filehandle);
    
    str=str2+","+str3+","+str4+","+str5+","+str6+","+str7+","+str8+","+str9+","+str10+","+str11
    +","+str12+","+str13+","+str14+","+str15+","+str16+","+str17+","+str18+","+str19+","+str20+","+str21
    +","+str22+","+str23+","+str24+","+str25+","+str26+","+str27+","+str28+","+str29+","+str30+","+str31+","+str32+","+str33+","+str34+","+str35+","+str36;
  if (str==st){FileClose(filehandle); return(false);}
 }

FileClose(filehandle); 
 
return(true); 
}

Тут первым делом проверяем, существует ли наш файл FileIsExist(OptimizationFileName). Если нет, создаем его. Далее читаем строку в цикле while(!FileIsEnding(filehandle) && !IsStopped()) в переменные str1- strN. Складываем все вместе в переменную str. Сравниваем каждую строку str с пришедшей строкой st на вход функции. В конце возвращаем результат, есть совпадение или нет.

Параметры получившихся советников:

"------------Open settings----------------";

Optimization - переключатель режимов, оптимизируем или торгуем;

Min - минимальное значение параметров весов и смещений; 

Max - максимальное значение параметров весов и смещений; 

OptimizationFileName - имя файла для записи параметров в процессе оптимизации и чтения в режиме торговли;

Passages - значение проходов. Параметр для оптимизации от 1 до 100 000 000 максимум с шагом 1;

LL - функция Softmax выдает 3 результата на основе суммы 100%. 0.6 равно значение выше 60%;

"------------Lots settings----------------";

SetFixLotOrPercent - выбор, лот или процент от депозита;

LotsOrPercent - лот или процент в зависимости от SetFixLotOrPercent;

"------------Other settings---------------";

MaxSpread - максимальный спред для открытия ордера;

Slippage - проскальзывание;

Magic - магический номер;

EAComment - комментарий к ордерам;

Для тех, кто не знает. По умолчанию MetaTrader 5 создает файлы в своей песочнице. 

Путь к файлам:

C:\Users\Ваше имя пользователя\AppData\Roaming\MetaQuotes\Terminal\Common\Files


Оптимизация

Проведем оптимизацию. Период с 2018.07.12 по 2021.07.12. Валютная пара EURUSD. Таймфрейм H1, по ценам открытия. Количество проходов (Passages) 10 000.

Режим (Максимум комплексного критерия). Советник Angle EA 4-4-3

a1

Режим (Максимум комплексного критерия). Советник Angle EA 4-4-4-3

a2

Режим (Максимум комплексного критерия). Советник Figure EA 4-4-3

f1

Режим (Максимум комплексного критерия). Советник Figure EA 4-4-4-3

f2

Режим (Максимум комплексного критерия). Советник Original EA 4-4-3. Стратегия автора библиотеки.

o1

Режим (Максимум комплексного критерия). Советник Original EA 4-4-4-3. Стратегия автора библиотеки.

o2

После оптимизации, например в 10 000 проходов, мы можем продолжить наполнение нашего файла новыми параметрами, запустив новый процесс оптимизации. Не забываем предварительно сохранить отчет оптимизации тестера стратегий с необходимыми параметрами Passages. Также необходимо очистить историю терминала, иначе тестер выдаст результаты уже пройденной оптимизации. Я делаю это с помощью скрипта типа .bat.

del C:\Users\Ваше имя пользователя\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*.* /q /f /s

for /d %%i in (C:\Users\Ваше имя пользователя\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\cache\*) do rd /s /q "%%i"


del C:\Users\Ваше имя пользователя\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*.* /q /f /s

for /d %%i in (C:\Users\Ваше имя пользователя\AppData\Roaming\MetaQuotes\Terminal\36A64B8C79A6163D85E6173B54096685\Tester\logs\*) do rd /s /q "%%i"

Замените (Ваше имя пользователя) и (36A64B8C79A6163D85E6173B54096685) на свои. Открыть можно обычным текстовым редактором. Скрипт очистки я добавлю во вложении.


Использование результатов оптимизации

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

Код загрузки параметров весов и смещений для тестирования:

if (FileIsExist(OptimizationFileName)==false){
int id = 0;
int i = 0;
string f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15,f16,f17,f18,f19,f20,f21,f22,f23,f24,f25,f26,f27,f28,f29,f30,f31,f32,f33,f34,f35,f36;
 int handle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";");
  if(handle!=INVALID_HANDLE)
   {Print("Loading optimization file.");
   
  while(!FileIsEnding(handle) && !IsStopped())
   {
   f1=FileReadString(handle);
   f2=FileReadString(handle);
   f3=FileReadString(handle);
   f4=FileReadString(handle);
   f5=FileReadString(handle);
   f6=FileReadString(handle);
   f7=FileReadString(handle);
   f8=FileReadString(handle);
   f9=FileReadString(handle);
   f10=FileReadString(handle);
   f11=FileReadString(handle);
   f12=FileReadString(handle);
   f13=FileReadString(handle);
   f14=FileReadString(handle);
   f15=FileReadString(handle);
   f16=FileReadString(handle);
   f17=FileReadString(handle);
   f18=FileReadString(handle);
   f19=FileReadString(handle);
   f20=FileReadString(handle);
   f21=FileReadString(handle);
   f22=FileReadString(handle);
   f23=FileReadString(handle);
   f24=FileReadString(handle);
   f25=FileReadString(handle);
   f26=FileReadString(handle);
   f27=FileReadString(handle);
   f28=FileReadString(handle);
   f29=FileReadString(handle);
   f30=FileReadString(handle);
   f31=FileReadString(handle);
   f32=FileReadString(handle);
   f33=FileReadString(handle);
   f34=FileReadString(handle);
   f35=FileReadString(handle);
   f36=FileReadString(handle);
 
   if (StringToInteger(f1)==Passages){
   weight[0]=StringToDouble(f2);
   Print(weight[0]);
   weight[1]=StringToDouble(f3);
   Print(weight[1]);
   weight[2]=StringToDouble(f4);
   Print(weight[2]);
   weight[3]=StringToDouble(f5);
   weight[4]=StringToDouble(f6);
   weight[5]=StringToDouble(f7);
   weight[6]=StringToDouble(f8);
   weight[7]=StringToDouble(f9);
   weight[8]=StringToDouble(f10);
   weight[9]=StringToDouble(f11);
   weight[10]=StringToDouble(f12);
   weight[11]=StringToDouble(f13);
   weight[12]=StringToDouble(f14);
   weight[13]=StringToDouble(f15);
   weight[14]=StringToDouble(f16);
   weight[15]=StringToDouble(f17);
   
   weight[16]=StringToDouble(f18);
   weight[17]=StringToDouble(f19);
   weight[18]=StringToDouble(f20);
   weight[19]=StringToDouble(f21);
   
   weight[20]=StringToDouble(f22);
   weight[21]=StringToDouble(f23);
   weight[22]=StringToDouble(f24);
   weight[23]=StringToDouble(f25);
   weight[24]=StringToDouble(f26);
   weight[25]=StringToDouble(f27);
   weight[26]=StringToDouble(f28);
   weight[27]=StringToDouble(f29);
   weight[28]=StringToDouble(f30);
   weight[29]=StringToDouble(f31);
   weight[30]=StringToDouble(f32);
   weight[31]=StringToDouble(f33);
   
   weight[32]=StringToDouble(f34);
   weight[33]=StringToDouble(f35);
   weight[34]=StringToDouble(f36);
   
   FileClose(handle);  
   break; 
   }
  
   }  
 FileClose(handle);
   }
   else{
   PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError());
   return(INIT_FAILED);
   }
}else{
   PrintFormat("Could not open file %s, error code = %d",OptimizationFileName,GetLastError());
   return(INIT_FAILED);
   }

Когда отключен переключатель Optimization, считываем наш файл. Сравниваем значение первого столбика со значением параметра Passages. Если находим совпадение, то присваиваем значения параметров весов и смещений нашим массивам weight[]. Таким образом можем провести тестирование лучших результатов.

Проведем форвард-тестирование полученных результатов, первые три лучших. Критерии отбора в моем случае - это максимальный профит-фактор и количество сделок больше 100:

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Angle EA 4-4-3.

Тест 1:

t1

Тест 2:

t2

Тест 3:

t3

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Angle EA 4-4-4-3.

Тест 1:

t4

Тест 2:

t5

Тест 3:

t6

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Figure EA 4-4-3.

Тест 1:

t7

Тест 2:

t8

Тест 3:

t9

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Figure EA 4-4-4-3.

Тест 1:

t10

Тест 2:

t11

Тест 3:

t12

Далее протестируем оригинальную стратегию автора библиотеки в нейросетях 4-4-3 и 4-4-4-3.

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Original EA 4-4-3.

Тест 1:

t13

Тест 2:

t14

Тест 3:

t15

  • Дата тестирования с 2021.07.12 по 2022.07.12;
  • Режим (Каждый тик на основе реальных тиков);
  • Начальний депозит 10 000;
  • Период H1;
  • Лот фиксированный 0.01;
  • Советник Original EA 4-4-4-3.

Тест 1:

t16

Тест 2:

t17

Тест 3:

t18

Подводя итоги, хочется отметить, что стратегии Angle EA 4-4-3 и Angle EA 4-4-4-3 отработали лучше, чем Figure EA 4-4-3 и Figure EA 4-4-4-3. Думаю, потому что используют нестандартные подходы к анализу рынка. Оптимизация на 2 ядрах занимает порядка 20 минут на периоде в 3 года. Также после проведенных экспериментов напрашивается ряд вопросов, которые необходимо решить:

  1. Проводить оптимизацию на большем периоде.
  2. Увеличить количество проходов. Само собой.
  3. Определиться с лучшей стратегией для дальнейшей работы.
  4. Разработать алгоритм одновременной работы с некой базой данных результатов оптимизации для торговли, над которым я уже начал думать. 
  5. Разработать алгоритм одновременной оптимизации и торговли.
  6. Использовать разные таймфреймы для определения лучшего результата.
  7. Совместить две и более нейросетей с разными наборами данных в одном советнике.

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


    Заключение

    Сегодня мы перешли к более сложным нейросетям. Проведена большая работа по выявлению необходимых данных для передачи в нейросеть. Но на этом возможности не закончились. Нужно попробовать передавать данные с большего количества индикаторов, использовать сложные связки. Я надеюсь, это приведет нас к новым успехам в разработке прибыльного торгового робота. Как оказалось, MetaTrader 5 вполне может обойтись без стороннего ПО. К тому же мы разработали очень интересный алгоритм оптимизации, который расширит наши возможности. 

    По традиции, проведение более глубокой оптимизации и форвард-тестирования оставлю за вами.

    Содержимое вложения:

    Angle EA 4-4-3 - советник, стратегия наклон углов индикаторов МА1 и МА24, нейросеть 4-4-3.
    
    Angle EA 4-4-4-3 - советник, стратегия наклон углов индикаторов МА1 и МА24, нейросеть 4-4-4-3.
    
    Figure EA 4-4-3 - советник, стратегия фигура бабочка (конверт) индикаторов МА1 и МА24, нейросеть 4-4-3.
    
    Figure EA 4-4-4-3 - советник, стратегия фигура бабочка (конверт) индикаторов МА1 и МА24, нейросеть 4-4-4-3.
    
    Original EA 4-4-3 - советник, стратегия какой процент от размера свечи составляет каждая из ее частей, нейросеть 4-4-3.
    
    Original EA 4-4-4-3 - советник, стратегия какой процент от размера свечи составляет каждая из ее частей, нейросеть 4-4-4-3.
    
    Очистить.bat - скрипт очистки файлов терминала (Лог и Кеш).
    
    DeepNeuralNetwork – библиотека для нейросети 4-4-4-3.
    
    DeepNeuralNetwork2 – библиотека для нейросети 4-4-3.


    Прикрепленные файлы |
    EA.zip (612.12 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (12)
    Roman Poshtar
    Roman Poshtar | 19 янв. 2023 в 11:16
    Отдел аналитики и трейдинга #:
    Подскажите, вот пытаюсь прогонять в тестере Original но он почему открывает только первую сделку и не закрывает ее.

    Использую каждый тик на основе реальных тиков, настройки такие же как в приложенных тут файлах. 

    Как это исправить и сделать нормальный тест?

    На демосчете например сделки открываются и закрываются нормально, но в тестере с этими же настройками робот не торгует по сути, открывает первую сделку и держит до конца 3 года пока тест не закончится

    Добрый день. Проверил, у меня все работает.

    Vladislav Cherniak
    Vladislav Cherniak | 3 февр. 2023 в 22:31

    А можете подсказать какие настройки использовали когда тестировали Original 4-4-4-3, с настройками например который скачал отсюда стандартный в тестере вообще не открывает сделки, хотя на обычном счете в реальном времени все ок.

    уже на двух терминалах разных брокеров проверил....

    Roman Poshtar
    Roman Poshtar | 4 февр. 2023 в 11:38
    Отдел аналитики и трейдинга #:

    А можете подсказать какие настройки использовали когда тестировали Original 4-4-4-3, с настройками например который скачал отсюда стандартный в тестере вообще не открывает сделки, хотя на обычном счете в реальном времени все ок.

    уже на двух терминалах разных брокеров проверил....

    Добрый день. Отключите параметр Optimization. Причитайте 3 часть, там все изложил.

    Aleksey Vyazmikin
    Aleksey Vyazmikin | 9 февр. 2023 в 19:59

    Я правильно понял, что веса устанавливаются рандомно и их значение запоминается?

    Почему не используете фрейма для передачи значений весов и их сохранения?

    Roman Poshtar
    Roman Poshtar | 10 февр. 2023 в 05:38
    Aleksey Vyazmikin #:

    Я правильно понял, что веса устанавливаются рандомно и их значение запоминается?

    Почему не используете фрейма для передачи значений весов и их сохранения?

    Добрый день. Веса рандомно но значения записываются в файл и потом проверяются на одинаковые.

    Разработка торговой системы на основе индикатора ATR Разработка торговой системы на основе индикатора ATR
    В этой статье мы изучим новый технический инструмент, который можно использовать в торговле. Это продолжение серии, в которой мы учимся проектировать простые торговые системы. В этот раз мы будем работать с еще одним популярным техническим индикатором — Средний истинный диапазон (Average True Range, ATR).
    DoEasy. Элементы управления (Часть 11): WinForms-объекты — группы, WinForms-объект CheckedListBox DoEasy. Элементы управления (Часть 11): WinForms-объекты — группы, WinForms-объект CheckedListBox
    В статье рассмотрим группирование WinForms-объектов и создадим объект-список объектов CheckBox.
    Разработка торговой системы на основе индикатора Parabolic SAR Разработка торговой системы на основе индикатора Parabolic SAR
    Это продолжение серии статей, в которых мы учимся строить торговые системы с использованием самых популярных индикаторов. В этой статье мы будем изучать индикатор Parabolic SAR. Также мы разработаем торговую систему для работы в платформе MetaTrader 5, используя несколько простых стратегий.
    Видео: Настройка MetaTrader 5 и MQL5 для простой автоматизированной торговли Видео: Настройка MetaTrader 5 и MQL5 для простой автоматизированной торговли
    В этом небольшом видеокурсе вы узнаете, как скачать, установить и настроить MetaTrader 5 для автоматизированной торговли. Вы также узнаете, как настроить график и параметры автоматизированной торговли. Вы проведете свое первое тестирование на истории и узнаете, как импортировать советника, который может самостоятельно торговать 24 часа в сутки 7 дней в неделю, избавляя вас от необходимости сидеть перед экраном.