Подключение к MetaTrader нейросетей от NeuroSolutions

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

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

Да, это возможно. Некоторые нейросетевые программы имеют необходимые программные интерфейсы. Одна из таких программ называется NeuroSolutions. Последняя ее версия - 6-я, но она еще есть не у всех, а вот версией 5 пользуются многие. Поэтому в статье будет рассмотрено взаимодействие именно с 5-й версией. Нужен полный дистрибутив программы, туда входит Custom Solution Wizard, он нам понадобится.

 

Придумываем стратегию

Стратегия для нашего тестового примера будет простой. Назовем ее WeekPattern. На таймфрейме D1 с помощью нейросети она будет в начале текущего бара прогнозировать цену его закрытия. И в зависимости от этого будет открывать BUY или SELL и держать эту сделку весь день. Прогноз цены будет основываться на OHLC-значениях предыдущих 5-ти баров. Для повышения точности работы нейросети мы будем подавать в нее не сами цены, а лишь их изменения относительно цены открытия текущего (нулевого) бара.

Готовим обучающие данные

Прежде чем приступить к созданию сети, напишем MQL5-скрипт, который выгрузит из терминала котировки в нужном нам виде. Эти данные понадобятся чтобы обучить нейросеть. Выгрузка будет проводиться в текстовый файл. В первой его строке через запятую перечислим названия полей. А в следующих строках пойдут данные, разделенные запятой. Каждая строка - это одна комбинация входов и выходов нейросети. В нашем случае скрипт на каждой строке будет сдвигаться по ценовой истории в прошлое на один бар и записывать в строку OHLC-значения 6-ти баров (5 баров из прошлого - это входы, и один бар текущий - это выход).

Этот скрипт WeekPattern-Export.mq5 нужно запускать на нужном таймфрейме нужной валютной пары (в нашем примере это D1 EURUSD). В настройках указывается имя файла и сколько строк нужно (для D1 260 строк - это история примерно за 1 год). Полный код скрипта:

#property script_show_inputs
//+------------------------------------------------------------------+
input string    Export_FileName = "NeuroSolutions\\data.csv"; // Файл для экспорта (в папке "MQL5\Files")
input int       Export_Bars     = 260; // Кол-во строк данных для экспорта
//+------------------------------------------------------------------+
void OnStart()
  {

   // Создадим файл
   int file = FileOpen(Export_FileName, FILE_WRITE|FILE_CSV|FILE_ANSI, ',');

   if (file != INVALID_HANDLE)
     {
      // Запишем заголовок данных

      string row="";
      for (int i=0; i<=5; i++)
        {
         if (StringLen(row)) row += ",";
         row += "Open"+i+",High"+i+",Low"+i+",Close"+i;
        }
      FileWrite(file, row);

      // Скопируем все нужные данные из истории

      MqlRates rates[], rate;
      int count = Export_Bars + 5;
      if (CopyRates(Symbol(), Period(), 1, count, rates) < count)
        {
         Print("Ошибка! Недостаточный размер истории для экспорта нужных данных.");
         return;
        }
      ArraySetAsSeries(rates, true);

      // Запишем данные      

      for (int bar=0; bar<Export_Bars; bar++)
        {
         row="";
         double zlevel=0;
         for (int i=0; i<=5; i++)
           {
            if (StringLen(row)) row += ",";
            rate = rates[bar+i];
            if (i==0) zlevel = rate.open; // уровень отсчета цен
            row += NormalizeDouble(rate.open -zlevel, Digits()) + ","
                 + NormalizeDouble(rate.high -zlevel, Digits()) + ","
                 + NormalizeDouble(rate.low  -zlevel, Digits()) + ","
                 + NormalizeDouble(rate.close-zlevel, Digits());
           }
         FileWrite(file, row);
        }

      FileClose(file);
      Print("Экспорт данных завершен успешно.");
     }
   else Print("Ошибка! Не удалось создать файл для экспорта данных. ", GetLastError());
  }
//+------------------------------------------------------------------+

После выгрузки данных получим файл data.csv, первые строки (для примера) выглядят примерно так:

Open0,High0,Low0,Close0,Open1,High1,Low1,Close1,Open2,High2,Low2,Close2,Open3,High3,Low3,Close3,Open4,High4,Low4,Close4,Open5,High5,Low5,Close5
0,0.00463,-0.0041,0.00274,-0.00518,0.00182,-0.00721,-6e-005,0.00561,0.00749,-0.00413,-0.00402,0.02038,0.02242,0.00377,0.00565,0.03642,0.0379,0.01798,0.02028,0.0405,0.04873,0.03462,0.03647
0,0.007,-0.00203,0.00512,0.01079,0.01267,0.00105,0.00116,0.02556,0.0276,0.00895,0.01083,0.0416,0.04308,0.02316,0.02546,0.04568,0.05391,0.0398,0.04165,0.04504,0.05006,0.03562,0.0456
0,0.00188,-0.00974,-0.00963,0.01477,0.01681,-0.00184,4e-005,0.03081,0.03229,0.01237,0.01467,0.03489,0.04312,0.02901,0.03086,0.03425,0.03927,0.02483,0.03481,0.02883,0.04205,0.02845,0.03809

Это формат, который NeuroSolutions понимает. Теперь мы можем приступить к построению и обучению сети.

 

Создаем нейросеть

В NeuroSolutions можно быстро создать нейросеть, даже видя эту программу в первый раз и мало разбираясь в нейросетях. Для этого при старте программы выбираем визард для новичков - NeuralExpert (Beginner):

Укажем ему тип задачи, который должна решать наша сеть:

Дадим ему файл с обучающими данными, который мы создали в прошлой главе:

В качестве входов сети пометим все поля файла, кроме полей 0-го бара:

Текстовых полей у нас нет - ничего не отмечаем:

Снова даем наш файл с данными:

Помечаем один-единственный выход нашей сети:

По умолчанию визард предлагает построить наиболее простую сеть. Так и сделаем:

Визард завершил свою работу, создав нам сеть (еще не обученную, просто структуру):

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

Если кликнуть на кнопку Test, то можно посмотреть, как необученная сеть справится с нашей задачей. Отвечаем на вопросы визарда тестирования:

Тестируем на данных всё из того же файла:

Тестирование завершилось. В окошке "Output vs. Desired Plot" появились графики, показывающие, какие значения выдает на нашей истории сеть (красный цвет), и какие там были значения на самом деле (синий цвет). Видно, что они сильно различаются:

Теперь сеть обучим. Для этого нужно кликнуть зеленую кнопку Start на панели под меню. Обучение завершится за несколько секунд и красный график изменится:

Теперь по графикам видно, что сеть выдает "что-то, похожее на правду". Можно теперь использовать ее для торговли. Сохраним сеть под именем WeekPattern.

 

Экспортируем нейросеть в DLL

Не выходя из NeuroSolutions, кликнем вверху кнопку CSW, которая запустит Custom Solution Wizard. Он нужен, чтобы сгенерировать из текущей нейросети DLL.

Визард может генерировать DLL для разных программ. Как я понял, для компиляции DLL ему в любом случае понадобится Visual C++ одной из версий: 5.0/6.0/7.0 (.NET 2002)/7.1 (.NET 2003)/8.0 (.NET 2005). Версии Express он почему-то не использует (я это проверял).

MetaTrader в списке целевых программ нет. Поэтому выберем Visual C++.

Куда сохранить результат:

Если всё прошло успешно, визард об этом сообщит:

И в папке, указанной при работе визарда, появится много файлов. Самые нужные нам: WeekPattern.dll, в нем находится наша нейросеть с программным интерфейсом к ней; и файл WeekPattern.nsw с настройками весов сети после обучения. Среди остальных файлов есть пример работы с этой DLL-нейросетью. В данном случае это проект на Visual C++ 6.

Подключаем DLL-нейросеть к MetaTrader

Созданная в предыдущей главе DLL-нейросеть предназначена для использования в проектах Visual C++. Она оперирует объектами сложной структуры, которые было бы тяжело или даже невозможно описать на MQL5. Поэтому подключать эту DLL напрямую к MetaTrader мы не будем. Вместо этого создадим небольшой DLL-переходник. В этом переходнике будет содержаться одна-единственная простая функция работы с нейросетью. Она будет создавать сеть, передавать ей данные на вход и возвращать данные с выходов.

К этому переходнику легко можно будет обращаться из MetaTrader 5. А переходник уже будет обращаться к DLL-нейросети от NeuroSolutions. Так как переходник будет написан на Visual C++, у него не будет проблем с объектами этой DLL.

 

 

Вам нет нужды создавать DLL-переходник самим. К статье приложена готовая DLL. Переходник работает с любыми DLL-нейросетями от NeuroSolutions. Эту главу можно не читать дальше.

Ну а если вы имеете опыт программирования на C++ и вам интересно, как создать такой переходник, читайте главу до конца. Возможно, в будущем вы захотите его усовершенствовать - ведь из DLL-нейросети можно экспортировать еще какие-нибудь функции. Например, функцию обучения (чтобы советник мог  сам адаптироваться к меняющемуся рынку, автоматически переобучая сеть). Полный перечень функций вы можете узнать, изучив пример, который в предыдущей главе сгенерировал Custom Solution Wizard.

Нам пригодятся от этого примера некоторые файлы.

В Visual C++ (той же версии, которую использовал Custom Solution Wizard) создадим пустой проект DLL с именем NeuroSolutionsAdapter и скопируем в него из примера файлы NSNetwork.h, NSNetwork.cpp, StdAfx.h. Также создадим пустой файл main.cpp:

В main.cpp напишем следующий код:

#include "stdafx.h"
#include "NSNetwork.h"

extern "C" __declspec(dllexport) int __stdcall CalcNeuralNet(
                LPCWSTR dllPath_u, LPCWSTR weightsPath_u,
                double* inputs, double* outputs)
{      
    // Преобразуем строки из Unicode в обычные
    CString dllPath     (dllPath_u);
    CString weightsPath (weightsPath_u);

    // Создание сети
    NSRecallNetwork nn(dllPath);
    if (!nn.IsLoaded()) return (1);

    // Загрузка весов
    if (nn.LoadWeights(weightsPath) != 0) return (2);

    // Подача входных данных и расчет выходов
    if (nn.GetResponse(1, inputs, outputs) != 0) return (3);

    return 0;
}

Build. DLL-переходник готова!

 

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

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

Файл Описание Куда положить (внутри папки терминала)
WeekPattern.dll наша DLL-нейросеть от NeuroSolutions MQL5\Files\NeuroSolutions\
WeekPattern.nsw настройки весов нашей нейросети MQL5\Files\NeuroSolutions\
NeuroSolutionsAdapter.dll универсальный DLL-переходник для любой DLL-нейросети MQL5\Libraries\

 

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

input double    Lots = 0.1;
//+------------------------------------------------------------------+
// Подключаем DLL-переходник, через которую будем использовать DLL-нейросеть от NeuroSolutions
#import "NeuroSolutionsAdapter.dll"
int CalcNeuralNet(string dllPath, string weightsPath, double& inputs[], double& outputs[]);
#import
//+------------------------------------------------------------------+
class CNeuroSolutionsNeuralNet
{
private:
   string dllPath;     // Путь к DLL-нейрости, созданной в NeuroSolutions
   string weightsPath; // Путь к файлу весов сети
public:
   double in[20]; // Входы сети - OHLC 5 баров
   double out[1]; // Выход сети - Close текущего бара

   CNeuroSolutionsNeuralNet();
   bool Calc();
};
//+------------------------------------------------------------------+
void CNeuroSolutionsNeuralNet::CNeuroSolutionsNeuralNet()
{
   string terminal = TerminalInfoString(TERMINAL_PATH);
   dllPath     = terminal + "\\MQL5\\Files\\NeuroSolutions\\WeekPattern.dll";
   weightsPath = terminal + "\\MQL5\\Files\\NeuroSolutions\\WeekPattern.nsw";
}
//+------------------------------------------------------------------+
bool CNeuroSolutionsNeuralNet::Calc()
  {
   // Получим текущие котировки для нейросети
   MqlRates rates[], rate;
   CopyRates(Symbol(), Period(), 0, 6, rates);
   ArraySetAsSeries(rates, true);

   // Заполним массив входных данных нейросети
   double zlevel=0;  
   for (int bar=0; bar<=5; bar++)
     {
      rate = rates[bar];
      // 0-й бар на вход сети не идет
      if (bar==0) zlevel=rate.open; // уровень отсчета цен
      // 1-5 бары идут на вход сети
      else
        {
         int i=(bar-1)*4; // номер входа
         in[i  ] = rate.open -zlevel;
         in[i+1] = rate.high -zlevel;
         in[i+2] = rate.low  -zlevel;
         in[i+3] = rate.close-zlevel;
        }
     }

   // Проведем расчет сети в DLL NeuroSolutions (через DLL-переходник)
   int res = CalcNeuralNet(dllPath, weightsPath, in, out);
   switch (res)
     {
      case 1: Print("Ошибка создания нейросети из DLL \"", dllPath, "\""); return (false);
      case 2: Print("Ошибка загрузки весов в нейросеть из файла \"", weightsPath, "\""); return (false);
      case 3: Print("Ошибка расчетов в нейросети");  return (false);
     }

   // Выход нейрости появился в массиве out, с ним ничего делать не надо

   return (true);
  }
//+------------------------------------------------------------------+

CNeuroSolutionsNeuralNet NN;
double Prognoze;

//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
void OnTick()
  {
   // Получим ценовой прогноз от нейросети
   if (NN.Calc()) Prognoze = NN.out[0];
   else           Prognoze = 0;

   // Осуществим необходимые торговые действия
   Trade();
  }
//+------------------------------------------------------------------+
void Trade()
  {

   // Закроем открытую позицию, если она против прогноза

   if(PositionSelect(_Symbol))
     {
      long type=PositionGetInteger(POSITION_TYPE);
      bool close=false;
      if((type == POSITION_TYPE_BUY)  && (Prognoze <= 0)) close = true;
      if((type == POSITION_TYPE_SELL) && (Prognoze >= 0)) close = true;
      if(close)
        {
         CTrade trade;
         trade.PositionClose(_Symbol);
        }
     }

   // Если позиций нет, то откроем по прогнозу

   if((Prognoze!=0) && (!PositionSelect(_Symbol)))
     {
      CTrade trade;
      if(Prognoze > 0) trade.Buy (Lots);
      if(Prognoze < 0) trade.Sell(Lots);
     }
  }
//+------------------------------------------------------------------+

 

Хороший способ проверить, правильно ли мы подключили нейросеть, это прогнать советник в тестере на том же участке истории, где сеть обучалась.

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

Проверим это. В нашем случае получился вот такой красивый график:

Значит, мы всё подключили правильно.

Ну и для статистики другие отчеты о тестировании советника:

 

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

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

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

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

 

Заключение

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

Both comments and pings are currently closed.

Comments are closed.

Форекс рейтинг