Использование библиотеки FANN2MQL в MetaTrader

10 декабря 2009, 07:29
Julien
11
3 827

Для начала пожалуйста установите библиотеку Fann2MQL. Она нам понадобится в дальнейшем. Ее можно загрузить здесь.

Введение

До настоящего времени был опубликован только один пример использования библиотеки Fann2MQL (Используем нейронные сети в MetaTrader), которая позволяет трейдерам использовать библиотеку "FANN" (также доступную в виде исходных кодов) в программах на MQL. Но этот пример, предложенный создателем библиотеки Fann2MQL, не так просто понять новичкам.

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

В этом примере мы собираемся обучить простую нейронную сеть распознавать простейший паттерн.

Мы обучим сеть распознавать паттерн такого типа: он содержит 3 числа: a, b и с, значения выходного параметра output следующие:

если (a < b) и (b < c), то output = 1
если (a < b) и (b > c), то output = 0
если (a > b) и (b > c), то output = 0
если (a > b) и (b < c), то output = 1

Можно представить эти числа как координаты вектора, с которым можно ассоциировать направление движения рынка.

В данном случае, паттерн может быть интерпретирован как:

ВВЕРХ ВВЕРХ = ВВЕРХ

ВВЕРХ ВНИЗ = ВНИЗ

ВНИЗ ВНИЗ = ВНИЗ

ВНИЗ ВВЕРХ = ВВЕРХ

Для начала мы создадим нейронную сеть.

Затем мы покажем нейросети несколько примеров паттернов, чтобы она смогла обучиться и вывести правила.

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

Код с комментариями:

// Включаем библиотеку Fann2MQl
#include <Fann2MQL.mqh>

#property copyright "Copyright © 2009, Julien Loutre"
#property link      "http://www.forexcomm.com"

#property  indicator_separate_window
#property  indicator_buffers 0

// полное число слоев. в данном случае 1 входной слой, 2 скрытых слоя, 
// и один выходной слой - всего 4 слоя
int nn_layer = 4;
int nn_input = 3;   // число нейронов входного слоя. Наш паттерн состоит из 3 чисел
                    // это значит у нас 3 нейрона во входном слое
int nn_hidden1 = 8; // число нейронов в первом скрытом слое
int nn_hidden2 = 5; // число нейронов во втором скрытом слое
int nn_output = 1;  // число нейронов выходном слое

// массив trainingData[][] будет содержать примеры, которые
// мы собираемся использовать для обучения нейронной нейросети
double      trainingData[][4];  // ВАЖНО! size = nn_input + nn_output


int maxTraining = 500;    // максимальное число итераций 
                          // для обучения нейросети на примерах
double targetMSE = 0.002; // среднеквадратичная ошибка (Mean-Square Error) нейронов
                          // которую мы должны получить (далее станет понятно)

int ann; // эта переменная будет идентификатором нейронной сети

// когда индикатор снимается с графика, 
// мы удаляем все нейронные сети из памяти.
int deinit() {
   f2M_destroy_all_anns();
   return(0);
}

int init() {
   int i;
   double MSE;
   
   Print("=================================== НАЧАЛО ВЫПОЛНЕНИЯ ================================");
   
   IndicatorBuffers(0);
   IndicatorDigits(6);
   
   // изменяем размер массива trainingData, чтобы использовать его далее.
   // мы собираемся изменить его размер один раз.
 
   ArrayResize(trainingData,1);
   
   Print("##### ИНИЦИАЛИЗАЦИЯ #####");
   
   // Cоздаем нейронную сеть
   ann = f2M_create_standard(nn_layer, nn_input, nn_hidden1, nn_hidden2, nn_output);
   
   // проверяем успешность ее создания: 0 = OK, -1 = ошибка
   debug("f2M_create_standard()",ann);
   
      // Задаем активационную функцию. 
        f2M_set_act_function_hidden (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
        f2M_set_act_function_output (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
        
     // Опыт показывает, что наилучшие результаты получаются, если для начальных весов 
     // используются случайные числа в этом диапазоне, однако вы можете его изменить
     // и посмотреть станут ли они лучше или хуже
        f2M_randomize_weights (ann, -0.77, 0.77);
        
   // Здесь я просто вывожу на консоль число входных и выходных нейронов
   // Это проверка для отладки.
   debug("f2M_get_num_input(ann)",f2M_get_num_input(ann));
   debug("f2M_get_num_output(ann)",f2M_get_num_output(ann));
        
   
   Print("##### ПОДГОТОВКА ДАННЫХ #####");

   // Теперь мы подготовим некоторые примеры данных (с известным результатом)
   // и добавим их в обучаемый набор

   // Поскольку мы добавили все примеры которые нужно,
   // мы собираемся предоставить этот обучаемый набор нейронам нейросети, чтобы они могли обучиться 

   // У функция prepareData() есть несколько аргументов:
   // - действие, которое нужно выполнить - обучение(train) или расчет(compute)
   // - данные (в нашем случае каждый набор содержит 3 числа)
   // - последний аргумент - это переменная для выходного значения нейросети.

   // В данном случае эта функция принимает набор данных и выходное значение,
   // и добавляет его в обучаемое множество.

   // См. комментарии к функции.
   //
   // В нашем случае паттерн который мы собираемся обучать следующий:
   // Есть 3 числа. Пусть это будут a, b и c.
   // Эти числа можно представить как координаты некоторого вектора,
   // с которым ассоциируется направление рынка
   // например (рыночное направление вверх или вниз)
   // если (a < b) и (b < c), то output = 1
   // если (a < b) и (b > c), то output = 0
   // если (a > b) и (b > c), то output = 0
   // если (a > b) и (b < c), то output = 1

   
   // ВВЕРХ ВВЕРХ = ВВЕРХ / если (a < b) и (b < c), то output = 1
   prepareData("train",1,2,3,1);
   prepareData("train",8,12,20,1);
   prepareData("train",4,6,8,1);
   prepareData("train",0,5,11,1);

   // ВВЕРХ ВНИЗ = ВНИЗ / если (a < b) и (b > c), то output = 0
   prepareData("train",1,2,1,0);
   prepareData("train",8,10,7,0);
   prepareData("train",7,10,7,0);
   prepareData("train",2,3,1,0);

   // ВНИЗ ВНИЗ = ВНИЗ / если (a > b) и (b > c), то output = 0
   prepareData("train",8,7,6,0);
   prepareData("train",20,10,1,0);
   prepareData("train",3,2,1,0);
   prepareData("train",9,4,3,0);
   prepareData("train",7,6,5,0);

   // ВНИЗ ВВЕРХ = ВВЕРХ / если (a > b) и (b < c), то output = 1
   prepareData("train",5,4,5,1);
   prepareData("train",2,1,6,1);
   prepareData("train",20,12,18,1);
   prepareData("train",8,2,10,1);
   
   // Теперь для проверки выведем на консоль все обучаемые примеры.
   // Это для отладки.

   printDataArray();
   
   
   Print("##### ОБУЧЕНИЕ #####");
   
   // Нам нужно обучить нейроны много раз так, чтобы результат был такой как требуется.

   // Здесь я буду обучать нейросеть с одними и теми же данными (нашими примерами) снова и снова, 
   // пока она полностью не научится правилам обучения либо
   // пока число обучений 'maxTraining' не превысит максимально заданное нами
   // (в нашем случае maxTraining = 500)

   // Чем лучше нейросеть обучена, тем меньше будет среднеквадратичная ошибка.
   // Функция teach() возвращает сренеквадратичную ошибку (MSE, Mean-Square Error)
   // 0.1 или меньше достаточно для простых правил
   // 0.02 или меньше лучше использовать для сложных правил вроде нашего примера 
   // мы пытаемся обучить нейросеть этим правилам (ведь распознавание паттернов непростая задача)

   for (i=0;i<maxTraining;i++) {
      MSE = teach(); // каждый раз в цикле вызываем функцию обучения
                     // детали см. комментарии к фунции teach().
      if (MSE < targetMSE) { 
   // если среднеквадратичное отклонение (MSE) меньше чем задано (было задано targetMSE = 0.02)
         debug("training finished. Trainings ",i+1); // то выведем на консоль сообщение 
                                                     // о количестве итераций за которое обучились
         i = maxTraining; // и выходим из цикла
      }
   }
   
   // выводим значение среднеквадратичного отклонения (MSE) после окончания обучения
   debug("MSE",f2M_get_MSE(ann));
   
   
   Print("##### ЗАПУСК #####");

   // А теперь мы можем попросить нейросеть проанализировать данные,
   // которые она никогда не видела. Распознает ли она их правильно?.

   // Как видно, здесь я использовал ту же функцию prepareData(), 
   // но с первым аргументом "compute".

   // Последний аргумент - это выходное значение, мы уже использовали его
   // выше для обучения, когда предоставляли правильные ответы
   // Теперь он не используется, поэтому оставим его нулевым.

   // Если хотите, вы можете напрямую вызывать функцию compute().
   // В нашем случае, она имеет вид: compute(inputVector[]);

   // Так что вместо вызова prepareData("compute",1,3,1,0); вы можете сделать следующее:
   // описываем новый массив
   //    double inputVector[];
   // изменяем размерность массива
   //    ArrayResize(inputVector,f2M_get_num_input(ann)); 
   // добавляем в массив данные
   //    inputVector[0] = 1;
   //    inputVector[1] = 3;
   //    inputVector[2] = 1;
   // вызываем функцию compute() с входными параметрами, которые в массиве InputVector
   //    result = compute(inputVector); 

   // Функция prepareData() вызывает функцию compute(), 
   // которая выводит результат на консоль, 
   // что дает возможность проследить работу.

   debug("1,3,1 = ВВЕРХ DOWN = DOWN. Правильный output 0.","");
   prepareData("compute",1,3,1,0);
   
   debug("1,2,3 = ВВЕРХ ВВЕРХ = ВВЕРХ. Правильный output 1.","");
   prepareData("compute",1,2,3,0);
   
   debug("3,2,1 = ВНИЗ ВНИЗ = ВНИЗ. Правильный output 0.","");
   prepareData("compute",3,2,1,0);
   
   debug("45,2,89 = ВНИЗ ВВЕРХ = ВВЕРХ. Правильный output 1.","");
   prepareData("compute",45,2,89,0);
   
   debug("1,3,23 = ВВЕРХ ВВЕРХ = ВВЕРХ. Правильный output 1.","");
   prepareData("compute",1,3,23,0);
   
   debug("7,5,6 = ВНИЗ ВВЕРХ = ВВЕРХ. Правильный output 1.","");
   prepareData("compute",7,5,6,0);
   
   debug("2,8,9 = ВВЕРХ ВВЕРХ = ВВЕРХ. Правильный output 1.","");
   prepareData("compute",2,8,9,0);
   
   Print("=================================== КОНЕЦ РАБОТЫ ================================");
   return(0);
}

int start() {
   return(0);
}

/*************************
** Функция printDataArray()
** Выводит данные, используемые для обучения нейронов
** Она не используется, сделана для отладки.
*************************/
void printDataArray() {
   int i,j;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   string lineBuffer = "";
   for (i=0;i<bufferSize;i++) {
      for (j=0;j<(f2M_get_num_input(ann)+f2M_get_num_output(ann));j++) {
         lineBuffer = StringConcatenate(lineBuffer, trainingData[i][j], ",");
      }
      debug("DataArray["+i+"]", lineBuffer);
      lineBuffer = "";
   }
}


/*************************
** Функция prepareData()
** Подготавливает данные для обучения либо для вычисления.
** Берет данные и заносит их в массив, 
** затем отдает их нейросети для обучения или для вычисления
** Мы можете модифицировать ее под вашу задачу.
*************************/
void prepareData(string action, double a, double b, double c, double output) {
   double inputVector[];
   double outputVector[];
   // we resize the arrays to the right size
   ArrayResize(inputVector,f2M_get_num_input(ann));
   ArrayResize(outputVector,f2M_get_num_output(ann));
   
   inputVector[0] = a;
   inputVector[1] = b;
   inputVector[2] = c;
   outputVector[0] = output;
   if (action == "train") {
      addTrainingData(inputVector,outputVector);
   }
   if (action == "compute") {
      compute(inputVector);
   }
   // если в вашей задаче входных данных больше чем 3,
   // просто измените струкутру этой функции
}


/*************************
** Функция addTrainingData()
** Добавляет образец для обучения в обучаемый набор
** (пример данных + правильный ответ) в глобальный набор обучаемого множества
*************************/
void addTrainingData(double inputArray[], double outputArray[]) {
   int j;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   
   // записываем входные данные 
   for (j=0;j<f2M_get_num_input(ann);j++) {
      trainingData[bufferSize][j] = inputArray[j];
   }
   // записываем выходные данные 
   for (j=0;j<f2M_get_num_output(ann);j++) {
      trainingData[bufferSize][f2M_get_num_input(ann)+j] = outputArray[j];
   }
   
   ArrayResize(trainingData,bufferSize+2);
}


/*************************
** Функция teach()
** Берет все обучаемые данные и использует их для однократного обучения
** Для того, чтобы обучить нейроны как полагается, вы должны запускать
** эту функцию много раз, пока значение среднеквадратичной ошибки (MSE)
** уменьшится до заданного значения.
*************************/
double teach() {
   int i,j;
   double MSE;
   double inputVector[];
   double outputVector[];
   ArrayResize(inputVector,f2M_get_num_input(ann));
   ArrayResize(outputVector,f2M_get_num_output(ann));
   int call;
   int bufferSize = ArraySize(trainingData)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
   for (i=0;i<bufferSize;i++) {
      for (j=0;j<f2M_get_num_input(ann);j++) {
         inputVector[j] = trainingData[i][j];
      }
      outputVector[0] = trainingData[i][3];
      //f2M_train() однократно показывает нейросети один пример.
      call = f2M_train(ann, inputVector, outputVector);
   }
   // Мы показали нейросети пример, 
   // то, как хорошо она обучилась можно проверить при помощи MSE (среднеквадратичная ошибка). 
   // Если она небольшая, значит сеть неплохо обучилась!
   MSE = f2M_get_MSE(ann);
   return(MSE);
}


/*************************
** Функция compute()
** Выдает результат вычисления нейросети
** на основе предоставленных входных данных (InputVector)
*************************/
double compute(double inputVector[]) {
   int j;
   int out;
   double output;
   ArrayResize(inputVector,f2M_get_num_input(ann));
   
   // Даем нейросети новые входные данные
   out = f2M_run(ann, inputVector);
   // и спрашиваем нейросеть о результате используя f2M_get_output().
   output = f2M_get_output(ann, 0);
   debug("Computing()",MathRound(output));
   return(output);
}


/*************************
** Функция debug()
** Печатает данные
*************************/
void debug(string a, string b) {
   Print(a+" ==> "+b);
}

Результат


Рис 1. Результат работы программы.

Выводы

Также можно почитать статью "Используем нейронные сети в MetaTrader", написанную Mariusz Woloszyn, автором библиотеки Fann2MQL.

Мне понадобилось 4 дня на то, чтобы разобраться, как использовать библиотеку Fann в MetaTrader, при этом я использовал информацию отсюда и результаты поиска Google.

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

Если у вас есть вопросы, пожалуйста задавайте и я отвечу.

Перевод с английского произведен MetaQuotes Software Corp.
Оригинальная статья: https://www.mql5.com/en/articles/1574

Прикрепленные файлы |
ASNN_1_learner.mq4 (11.74 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (11)
Serg16
Serg16 | 11 янв 2011 в 02:14

Прочитал и ужаснулся. Кому нужна такая нейросеть, либо пример выбран отвратительно. Мои доводы, когда сеть будет работать, она будет выдавать результат с какой-то ошибкой, пусть даже незначительной, но не проще было написать в примере if (a < b) и (b < c), то output = 1; if (a < b) и (b > c), то output = 0; if (a > b) и (b > c), то output = 0 ; if (a > b) и (b < c), то output = 1 - меньше кода - вероятность ошибки = 0,0.

Viktor Vasilyuk
Viktor Vasilyuk | 27 янв 2011 в 17:47

Автор слишком далеко зашел, тот кто не видит сути описанного автором либо слеп, либо верит в обыкновенные АТС.

По поводу количества циклов обучения - так их количество определяет сама НС исходя из полученного Mean-Square Error и размера ошибки введенного пользователем.

Тот кто хочет - найдет полезное.

MQL4 Comments
MQL4 Comments | 11 апр 2011 в 22:31

Привет всем !

Прочитал про Нер Сет и придлогаемый советник и понял что не зню с чего начать как обучать и вобще мне бы для начала понять как первый щаг делать

Прошу скинуть мне инфо или Адрес пошагового начала Библиотеку на диск поставил

Теперь не знаю как ее наполнять знаниями ???

Одним словом помогите кто меня понял !!!

Vitaliy Vershinin
Vitaliy Vershinin | 30 янв 2014 в 04:31
Ребят!!! Да вы что критикуете труд парня??? Скажу по себе, я достаточно долго не мог найти понимание задачи для нейро сети которую бы нельзя было бы описать любой другой логикой поведения советника... Всегда есть альтернатива подобным способам понимания рынка... Но отсюда отнюдь статья не кажется не понятной или недостойной для сосуществования... Скажу на против, она дает понимание реализации довольно простой задачи и описывает пример работы с библиотекой fann2mql... Простые данные, простые условия и что не мение главно, что сеть в итоге дает правильные ответы.... А автору хотелось бы выразить благодарнось за попытку привлечения молодых программистов к пониманию логики работы нейросети и примеру ее реализации.... Хотелось бы так же пожелать критикам написать и предоставить публики довольно различные примеры работы ANN, а также что не мение важно, продемонстрировать примеры реализации задач... К сведению в библиотеке статей данного сервиса существует всего лишь два примера работы с fann...
Alexey Shevchenko
Alexey Shevchenko | 26 апр 2015 в 12:29

Написано же русским языком:

Цель статьи - показать, как использовать библиотеку FANN2MQL для программирования нейронных сетей в MetaTrader на простом примере: обучение и распознавание простейших паттернов.

Что Вы хотите от этой статьи? Чтобы был исходник реального прибыльного нейронного советника?

Целью было не написание советника, а показать как и зачем пользоваться библиотекой.

Критиковать все могут! Если Вы считаете, что автор написал недостойную статью, то напишите лучше! 

Используем нейронные сети в MetaTrader Используем нейронные сети в MetaTrader

В статье показано как применять нейронные сети в программах на MQL, используя свободно распространяемую библиотеку FANN.На примере стратегии с использованием индикатора MACD построен эксперт, использующий нейросетевую фильтрацию сделок, которая привела к улучшению характеристик торговой системы.

Alert и Comment для внешних индикаторов. Мультивалютный анализ посредством внешнего сканирования Alert и Comment для внешних индикаторов. Мультивалютный анализ посредством внешнего сканирования

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

Проверка некоторых мифов: "Как торгуется азиатская сессия, так весь день и пойдет торговля" Проверка некоторых мифов: "Как торгуется азиатская сессия, так весь день и пойдет торговля"

Предлагается проверить некоторые распространенные утверждения - в данном случае проверяется утверждение о том, что "как торгуется азиатская сессия, так и идут торги весь день".

Техника оптимизации (тестирования) и некоторые критерии выбора рабочих параметров эксперта Техника оптимизации (тестирования) и некоторые критерии выбора рабочих параметров эксперта

Тестерный "Грааль" получить очень легко и просто, а вот избавиться от этого - гораздо сложнее. В статье рассмотрен вариант выбора рабочих параметров эксперта с автоматизированной групповой обработкой результатов оптимизации и тестирования, с максимальным использованием возможностей терминала и минимальной нагрузкой на конечного пользователя.