English 中文 Español Deutsch 日本語 Português
preview
Практическое применение нейросетей в трейдинге. Переходим к практике

Практическое применение нейросетей в трейдинге. Переходим к практике

MetaTrader 5Примеры | 18 мая 2020, 14:20
13 344 9
Andrey Dibrov
Andrey Dibrov

Введение

В предыдущей статье "Практическое применение нейросетей в трейдинге" я описал общие моменты построения системы торговли с использованием нейросетевых модулей (НСМ). В этой статье я хочу предложить на практике попробовать, что собой представляют НСМ. А также на их основе построить автоматическую торговую систему.

В статье мы будем работать с валютной парой EURUSD. Однако сам нейросетевой модуль, как программа, универсальный и может работать по различным инструментам и их комбинации. То есть, как вариант, можно использовать один НСМ, в который будут интегрированы нейронные сети, обученные для разных валютных пар.

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

Также отмечу, что НСМ является полностью готовым к ведению торгов. Для ознакомления с комплексом в рамках сжатого изложения для данной статьи мне пришлось его несколько модернизировать таким образом, чтобы в одной программе совместить несколько функций НСМ. И в эту же программу (НСМ) перенести определенные условия совершения сделок из торгового эксперта. Однако перенос условий относится для части НСМ, которая предназначена для реальной торговли. Что же касается тестирования нейронной сети и оптимизации ее откликов в режиме оффлайн — естественно эти функции и условия для сделок я оставил соответствующим экспертам. Хотя для меня предпочтительнее нейросетевой модуль, в котором имеется только функция ведения торгов. Я исхожу из тех соображений, что для торгов НСМ должен быть как можно проще без каких либо дополнительных функций. А сами функции выносить за пределы торгового комплекса. В данном варианте — функции обучения, тестирования, оптимизации. А то касается условий совершения сделок, то их лучше иметь в НСМ для того, чтобы от него получать сигналы в бинарном виде. Хотя все варианты исполнения НСМ на практике подтвердили свою жизнеспособность.

Технологию подготовки и обучения НСМ на платформе Matlab я более полно предполагаю раскрыть в следующей статье.

Также в дальнейшем планирую перевести систему на Python. Короткое видео на эту тему вы можете просмотреть в конце статьи.

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

Нейросетевой модуль EURUSD_MT5

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

НСМ EURUSD_MT5

  1. Блок "Online" предназначен для запуска и остановки нейронных сетей при реальной работе и тестировании в режиме визуализации.
  2. Информационные поля условий пересечения линии отклика нейросетей сигнальной линией при задействовании блока "Online". 
  3. Блок "Train" — демонстрационный блок, который предназначен для обучения и "дообучения!!?" нейронных сетей.
  4. Поля вывода значений отклика нейронных сетей. Левые — отклик нейросетей, правые — сигнальная линия. Нижние — текущий бар, верхние — предыдущий.
  5. Блок "Offline" предназначен для вывода в файл массива откликов нейронных сетей по тестовой выборке.
  6. Окно ввода значения усреднения линии отклика нейронных сетей при задействовании блока "Online". (Период сигнальной линии). Изменяемое значение.
  7. Блоки "Net1,2,3" — три подмодуля сетей, обученных на разных участках временного ряда. Каждый блок включает в себя по две нейронные сети.
  8. Дата окончания работы НСМ при задействовании блока "Online".
  9. Окно ввода периода работы НСМ в часах при задействовании блока "Online". Изменяемое значение.
  10. Отсчет прошедшего времени с момента начала работы НСМ при задействовании блока "Online".

Приложения

  1. MyAppInstaller_mcr.exe — файл установки.
  2. EURUSD_MT5.exe — непосредственно сам нейросетевой модуль.
  3. EURUSD_MT5var.exe — вариант нейросетевого модуля.
  4. net1-net6.mat — нейронные сети трех подмодулей Net1-Net3. Для использования в качестве примера их обучения и тестирования. 
  1. ExpertMatlabPodkach_MT5.mq5 и ExpertMatlab_MT5.mq5 — два эксперта необходимых для подготовки исторических данных для оффлайн тестирования НСМ.
  2. NWI.mq5 — индикатор визуального представления откликов НСМ.
  3. Matlab_MT5.mq5 — эксперт для тестирования и оптимизации отклика НСМ в тестере стратегий.
  4. ExpertMatlabReal_MT5.mq5 — эксперт для онлайн работы на реальном либо демонстрационном счетах, а также для тестирования в режиме визуализации.
  5. EURUSDData.csv — файл с обучающими данными.

Установка программы



Перед первоначальным использованием приложений, скомпилированных Matlab, необходимо воспользоваться MyAppInstaller_mcr.exe. С помощью этого приложения мы установим MATLAB Runtime и непосредственно нейросетевой модуль.

После установки программы ярлык EURUSD_MT5.exe поместим в папку ...\Common\Files  каталога данных. Это мы делаем для удобства в момент запуска системы. Все эксперты и НСМ пишут файлы в эту папку и при запуске НСМ предлагает искать файлы из каталога, где размещен ярлык. 

Файл EURUSD_MT5

1. Ярлык EURUSD_MT5

Назначим рабочую папку.

Пропишем путь

2. Пропишем путь


Практическое использование

Далее у нас есть четыре варианта использования НСМ:

  1. Ведение торгов в онлайн режиме на demo или реальном счетах.
  2. Тестирование НСМ в режиме визуализации.
  3. Обучение нейросетей.
  4. Получение откликов от нейросетевых блоков для оптимизации торговой стратегии.

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

Рассмотрим эти варианты подробнее.

1. В онлайн режиме на demo или реальном счетах


Как мы видим, время запуска системы занимает менее пяти минут.

Предварительно необходимо настроить Excel. Настройка заключается в том, чтобы данные от скриптов и экспертов записывались в ячейки в числовом формате. Иначе Matlab будет эти данные читать некорректно. Для этого нам необходимо установить разделителем целой и дробной части чисел точку. Либо через "системный разделитель", либо непосредственно.

Параметры Excel

3. Параметры Excel

Перед первоначальным запуском НСМ, в первую очередь, нам необходимо с помощью эксперта создать файл подкачки истории. В тестере стратегий запустим эксперт ExpertMatlabPodkach_MT5.ex5. 


    График

Запустим ExpertMatlabPodkach_MT5

4. Запустим ExpertMatlabPodkach_MT5.ex5

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

Мы получим файл EURUSDTestPodkach.csv.


Файл EURUSDPodkach_MT5

5. Файл EURUSDTestPodkach.csv


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

Удалим строки

6. Удалим строки


Теперь мы можем запустить эксперт ExpertMatlabReal_MT5.ex5.

#include<Trade\Trade.mqh>
//--- объект для проведения торговых операций
CTrade  trade;

input int LossBuy;
input int ProfitBuy;
input int LossSell;
input int ProfitSell;

int BarMax;
int BarMin;
int handleInput;
int handleInputPodkach;
int handleBar;
int Con;
int Bar;

double DibMax;
double DibMin;

double in[32];

int Order01;
int Order1;

ulong TicketBuy1;
ulong TicketSell0;

bool send1;
bool send0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   handleInputPodkach=FileOpen("EURUSDTestPodkach.csv",FILE_READ|FILE_CSV|FILE_ANSI|FILE_COMMON,";");
   if(handleInputPodkach==INVALID_HANDLE)
      Alert("Отсутствует файл EURUSDTestPodkach.csv");

   in[0]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[1]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[2]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[3]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[4]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[5]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[6]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[7]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[8]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[9]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[10]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[11]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[12]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[13]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[14]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[15]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[16]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[17]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[18]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[19]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[20]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[21]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[22]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[23]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[24]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[25]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[26]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[27]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[28]=1/StringToDouble(FileReadString(handleInputPodkach))-1;
   in[29]=1/StringToDouble(FileReadString(handleInputPodkach))-1;

   FileClose(handleInputPodkach);

//--- зададим MagicNumber для идентификации своих ордеров
   int MagicNumber=123456;
   trade.SetExpertMagicNumber(MagicNumber);
//--- установим допустимое проскальзывание в пунктах при совершении покупки/продажи
   int deviation=10;
   trade.SetDeviationInPoints(deviation);
//--- режим заполнения ордера, нужно использовать тот режим, который разрешается сервером
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
//--- какую функцию использовать для торговли: true - OrderSendAsync(), false - OrderSend()
   trade.SetAsyncMode(true);

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   FileClose(handleInput);
   FileClose(handleBar);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   MqlDateTime stm;
   TimeToStruct(TimeCurrent(),stm);

   if(stm.hour==1)
      DibMax=iHigh(NULL,PERIOD_H1,1);
   if(stm.hour>0)
     {
      if(iHigh(NULL,PERIOD_H1,1)>DibMax && iTime(NULL,PERIOD_H1,0)>1)
        {
         in[20]=iOpen(NULL,PERIOD_D1,0)-iLow(NULL,PERIOD_H1,1);
         in[21]=iHigh(NULL,PERIOD_H1,1)-iOpen(NULL,PERIOD_D1,0);
         in[22]=iHigh(NULL,PERIOD_D1,1)-iLow(NULL,PERIOD_D1,1);
         in[23]=iHigh(NULL,PERIOD_D1,1)-iOpen(NULL,PERIOD_H1,0);
         in[24]=iOpen(NULL,PERIOD_H1,0)-iLow(NULL,PERIOD_D1,1);
        }
     }
   if(iHigh(NULL,PERIOD_H1,1)>DibMax)
      DibMax=iHigh(NULL,PERIOD_H1,1);
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
   if(stm.hour==1)
      DibMin=iLow(NULL,PERIOD_H1,1);
   if(stm.hour>0)
     {
      if(iLow(NULL,PERIOD_H1,1)<DibMin && iTime(NULL,PERIOD_H1,0)>1)
        {
         in[25]=iOpen(NULL,PERIOD_D1,0)-iLow(NULL,PERIOD_H1,1);
         in[26]=iHigh(NULL,PERIOD_H1,1)-iOpen(NULL,PERIOD_D1,0);
         in[27]=iHigh(NULL,PERIOD_D1,1)-iLow(NULL,PERIOD_D1,1);
         in[28]=iHigh(NULL,PERIOD_D1,1)-iOpen(NULL,PERIOD_H1,0);
         in[29]=iOpen(NULL,PERIOD_H1,0)-iLow(NULL,PERIOD_D1,1);
        }
     }
   if(iLow(NULL,PERIOD_H1,1)<DibMin)
      DibMin=iLow(NULL,PERIOD_H1,1);

   in[30]=iHigh(NULL,PERIOD_D1,1)-iOpen(NULL,PERIOD_H1,0);
   in[31]=iOpen(NULL,PERIOD_H1,0)-iLow(NULL,PERIOD_D1,1);

   if(Bar<Bars(NULL,PERIOD_H1)&& stm.hour==0)
     {
      for(int i=19; i>=10; i--)
        {
         in[i-10]=in[i];
        }

      for(int i=29; i>=20; i--)
        {
         in[i-10]=in[i];
        }
     }


   handleInput=FileOpen("Input_mat.txt",FILE_TXT|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ|FILE_COMMON,";");

   FileWrite(handleInput,

             1/(in[0]+1),1/(in[1]+1),1/(in[2]+1),1/(in[3]+1),1/(in[4]+1),1/(in[5]+1),1/(in[6]+1),1/(in[7]+1),1/(in[8]+1),1/(in[9]+1),1/(in[10]+1),1/(in[11]+1),1/(in[12]+1),1/(in[13]+1),1/(in[14]+1),1/(in[15]+1),
             1/(in[16]+1),1/(in[17]+1),1/(in[18]+1),1/(in[19]+1),1/(in[20]+1),1/(in[21]+1),1/(in[22]+1),1/(in[23]+1),1/(in[24]+1),1/(in[25]+1),1/(in[26]+1),1/(in[27]+1),1/(in[28]+1),1/(in[29]+1),1/(in[30]+1),1/(in[31]+1));

   FileClose(handleInput);

   handleBar=FileOpen("Bar.txt",FILE_TXT|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ|FILE_COMMON,";");

   FileWrite(handleBar,stm.hour);

   FileClose(handleBar);

   Order01=FileOpen("Open1.txt",FILE_CSV|FILE_READ|FILE_ANSI|FILE_SHARE_READ|FILE_COMMON," ");

   Order1=StringToInteger(FileReadString(Order01));
   
   FileClose(Order01);

   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
   double PriceAsk=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   double PriceBid=SymbolInfoDouble(_Symbol,SYMBOL_BID);

   double SL1=NormalizeDouble(PriceBid-LossBuy*point,digits);
   double TP1=NormalizeDouble(PriceAsk+ProfitBuy*point,digits);
   double SL0=NormalizeDouble(PriceAsk+LossSell*point,digits);
   double TP0=NormalizeDouble(PriceBid-ProfitSell*point,digits);

   if(Bar<Bars(NULL,PERIOD_H1))
      Con=0;

   Comment(Order1,"  ",Con);

   if(LossBuy==0)
      SL1=0;

   if(ProfitBuy==0)
      TP1=0;

   if(LossSell==0)
      SL0=0;

   if(ProfitSell==0)
      TP0=0;

   if(Order1==0 && Bar<Bars(NULL,PERIOD_H1) && Con==0)
      send0=true;

   if(Order1==1 && Bar<Bars(NULL,PERIOD_H1) && Con==0)
      send1=true;


//---------Buy0

   if(send1==false  &&  Bar==Bars(NULL,PERIOD_H1) && Order1==1 && Con>=1 && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2) && stm.hour>15 && stm.hour<20)
     {
      send1=trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,PriceAsk,SL1,TP1);
      TicketBuy1 = trade.ResultDeal();
     }

   if(send1==true &&  Bar==Bars(NULL,PERIOD_H1) && Order1==0 && Con>=1 && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketBuy1);
      send1=false;
     }
//---------Sell0
   if(send0==false  &&  Bar==Bars(NULL,PERIOD_H1) && Order1==0 && Con>=1 && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2) && stm.hour>
        11 && stm.hour<14)
     {
      send0=trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,1,PriceBid,SL0,TP0);
      TicketSell0 = trade.ResultDeal();
     }

   if(send0==true &&  Bar==Bars(NULL,PERIOD_H1) && Order1==1 && Con>=1 && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketSell0);
      send0=false;
     }
//-----------------------------------------------------------------------
   Bar=Bars(NULL,PERIOD_H1);
   Con++;
  }
//------------------------------------------------------------------------

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

ExpertMatlabReal_MT5

7. Запустим ExpertMatlabReal_MT5.ex5

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

Matlab_MT5.ex5

7.1 Matlab_MT5.ex5

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

Предупреждение

8. Предупреждение


В папку ...\Common\Files советник запишет два файла. В файле Input_mat.txt находятся входные данные для НСМ.

Отклик эксперта

9. Отклик эксперта

Запустим нейросетевой модуль EURUSD_MT5.exe. Подождем, пока появится рабочая область.

Рабочая область

10. Рабочая область НСМ


Изменяемые параметры блоков Net2 и Net3 в данном варианте программы не могут быть изменены.

Нажмем кнопку "Start" и выберем файл net1.m (либо другой файл нейронной сети).

Нажмем кнопку "Start"

11. Нажмем кнопку "Start"

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

Нейросетевой модуль начнет свою работу.

НСМ торгует

12. НСМ в состоянии ведения торгов


Во время работы НСМ ни один изменяемый параметр поменять мы не можем.

В верхнем левом углу графика мы видим числа, которые эксперт получил от НСМ 1.0, -1.0 и 0.0. Это значит, что эксперт получил от Net1 сигнал на покупку (продажу). От Net2 — оставаться вне рынка. И от Net3 — сигнал на продажу (покупку). Поскольку данная версия программы ознакомительная, то от блоков Net1 и Net2 изменяемые сигналы мы получать не будем.

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

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

Контроль информации

13. Контроль информации

На рисунке представлен вариант нейросетевого модуля, который передает в этот эксперт только отклик от net1, net2 и net3. А условия открытия позиций прописаны в эксперте. Отмечу, что я предпочитаю такой вариант. В нашем же случае НСМ передает уже готовый сигнал, а эксперт торгует исходя только лишь из временного ограничения совершения сделок.

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

Вариант НСМ

14. Вариант НСМ

На рисунке выше представлен еще один вариант нейросетевого модуля. В таком варианте нейронные сети целесообразно интегрировать непосредственно в исполняемый файл. При нажатии зеленой кнопки нам надо лишь выбрать файл Input_mat.txt. Для работы я бы рекомендовал использовать подобную модель. В таком исполнении оставлен только блок для ведения торгов, а блоки для обучения и тестирования исключены. Я хочу подчеркнуть, что видимая сложность системы на этапе подготовки в период торговли оборачивается завидной простотой. Основной анализ рынка у нас происходит в НСМ, причем за счет нейронных сетей мгновенно, а торговому роботу, если не использовать других условий оптимизации, останется только интерпретировать две цифры. 

 if(send1==false && Order1==1)
     {
      send1=trade.Buy(1);
      TicketBuy1 = trade.ResultDeal();
     }

 if(send1==true && Order1==0)
     {
      trade.PositionClose(TicketBuy1);
      send1=false;
     }

В заключении хочу остановиться на некотором отличии запуска эксперта ExpertMatlabReal_MT4.ex4 для работы через терминал MetaTrader 4. Это связано с особенностями тестеров стратегий, то — как они определяют конечные точки тестов. В MetaTrader 5 окончанием теста есть последний бар предшествующего дня, а в MetaTrader 4 - текущий бар. Поэтому в эксперте для MetaTrader 4 я ввел внешнюю переменную "Hours".

Внешняя переменная "Hours"

14.1 Внешняя переменная "Hours"

С помощью эксперта ExpertMatlabPodkach_MT4.ex4 мы формируем файл подкачки со строкой на текущий бар, поэтому при первом запуске в переменной "Hours" выставляем время текущего бара. После запуска эксперта заходим в его свойства и возвращаем переменную в 0. Это необходимо сделать для того, чтобы последующие сдвиги данных осуществлялись в 00:00 часов.

Hours-0

14.2 Hours -0

2. Тестирование НСМ в режиме визуализации


С помощью этого теста мы заканчиваем подготовку, окончательно определяя, что нейросетевой модуль дает нам тот результат, которого мы достигли, подготавливая данные для обучения нейросетей, непосредственно их обучения, получение массива откликов, визуализируя их и далее оптимизируя. Для этого мы повторим  действия из первого пункта — то есть запустим НСМ как для ведения торгов. А в тестере стратегий запустим эксперт ExpertMatlabReal_MT5.ex5 с использованием визуализации. В MetaTrader 5 включим моделирование "OHLC на M1". А в MetaTrader 4 — "Контрольные точки". Эти модели необходимо использовать для получения достоверного результата тестов, так как сделки у нас при использовании НСМ будут совершаться на следующем тике после открытия бара. И если использовать модель "Только цены открытия", то при тестировании открытие позиций будет запаздывать на один бар. В таком режиме запаздывание отклика удобно рассмотреть с помощью модуля EURUSD_MT5var.exe. При реальной работе это, конечно же, исключено. Хочу отметить, что данная система показывает практически идентичные результаты при всех видах тестирования. И это еще раз подтверждает ее жизнеспособность.

Визуализация

Визуализация

15. Визуальный режим


3. Обучение нейросетей


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

 Так, например, для обучения двух нейросетей для подмодуля Net1, нам необходимо иметь в каталоге ...\Common\Files непосредственно сами нейросети net1-2 и файл EURUSDData.csv с обучающими данными.

Data

16. Файл с данными

Обучающие данные реальные и непосредственно те, которые используются для подготовки системы торговли с помощью блока "Online". Здесь я еще раз хочу отметить преимущество использования нейронных сетей для оценки рыночной ситуации. В таблице EURUSDData.csv имеется 90 колонок, являющихся набором последовательных значений входных данных нейросети. Проще говоря — входы. Если взять упрощенно, то представим, что каждая колонка — это отдельный индикатор, который предварительно рассчитывается экспертом и потом извлекается в качестве значений для обучения нейросети. Да, но ведь это мы делаем в оффлайн режиме при подготовке торговой системы. А теперь представьте, что анализировать этот значительный набор информации мы бы взялись непосредственно в рабочем эксперте во время торгов...

В процессе написания статьи я, для лучшего понимания порядка действий, изменил названия кнопок.

Измененные названия кнопок

17. Новые названия кнопок


То есть нажмем кнопку "Train net1" и, открыв файл net1.mat, мы обучим нейросеть net1 и нейросеть net2 для блока Net1. И так далее — net3,4 для Net2 и net5,6 для Net3.

Train net1

18. Train net1


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

4. Получение откликов от нейросетевых блоков для оптимизации торговой стратегии


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

Как было описано ранее, в папке ...\AppData\Roaming\MetaQuotes\Terminal\Common\Files подготовим файл EURUSDTestPodkach_MT5.csv. Для этого в тестере стратегий запустим эксперт ExpertMatlabPodkach_MT5. Естественно, только с даты предшествующей дате начала теста (обратите внимание на то, чтобы был закачан необходимый объем истории).

Например:

Podkach

19. Запустим ExpertMatlabPodkach_MT5.ex5


И в полученном файле удалим все строки кроме одной.

EURUSDTestPodkach

20. Оставим одну строку


Для того чтобы сформировать файл с данными за тестовый период, запустим в тестере стратегий эксперт ExpertMatlab_MT5.ex5.

ExpertMatlab

21. Запустим ExpertMatlab_MT5.ex5

Обратите внимание, как мы выбираем дату начала периода.


EURUSDTest, EURUSDDate

22. Получим EURUSDTest.csv и EURUSDDate.csv


ExpertMatlab_MT5.ex5 сформирует два тестовых файла EURUSDDate.csv и EURUSDTest.csv. Запустим нейросетевой модуль, нажмем кнопку "Test net1" и выберем net1.mat.

Test net1

23. Нажмем кнопку "Test net1"

Нам надо некоторое время подождать, пока не будет сформирован файл откликов Indicator1.csv.

Indicator1

24. Получим файл откликов Indicator1.csv


Сохраним Indicator1 как Indicator. Причем, если "Indicator1.csv" нейросетевым блоком сохранен в "Режиме совместимости", то его мы должны сохранить в формате "csv" через вкладку "Сохранить как". 

Indicator

25. Сохраним Indicator1.csv как Indicator.csv


Посмотрим, что собой представляют отклики НСМ в визуальном представлении на графике EURUSD H1. Для этого воспользуемся индикатором NWI.ex5.

NWI

26. Запустим индикатор NWI.ex5

По умолчанию период имеет значение 5. Сразу оговорюсь, что на данный момент этот период сигнальной линии по моим экспериментам для EURUSD H1 является наиболее результативным. Впрочем, вы можете поэкспериментировать.

NWI

27. Отображение NWI


С помощью эксперта Matlab_MT5.ex5 мы можем протестировать и далее оптимизировать полученные отклики.

#include<Trade\Trade.mqh>

CTrade  trade;

input int Период=5;
input int H1;
input int H2;
input int H3;
input int H4;

input int LossBuy;
input int ProfitBuy;
input int LossSell;
input int ProfitSell;

ulong TicketBuy1;
ulong TicketSell0;

datetime Count;

double Per;
double Buf_0[];
double Buf_1[];

bool send1;
bool send0;

int h=4;
int k;
int K;
int bars;
int Handle;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");

   while(!FileIsEnding(Handle)&& !IsStopped())
     {
      StringToTime(FileReadString(Handle));
      bars++;
     }
   FileClose(Handle);

   ArrayResize(Buf_0,bars);
   ArrayResize(Buf_1,bars);

   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");

   while(!FileIsEnding(Handle)&& !IsStopped())
     {
      Count=StringToTime(FileReadString(Handle));
      Buf_0[k]=StringToDouble(FileReadString(Handle));
      h=Период-1;
      if(k>=h)
        {
         while(h>=0)
           {
            Buf_1[k]=Buf_1[k]+Buf_0[k-h];
            h--;
           }
         Buf_1[k]=Buf_1[k]/Период;
        }
      k++;
     }
   FileClose(Handle);

   int deviation=10;
   trade.SetDeviationInPoints(deviation);
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
   trade.SetAsyncMode(true);

//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   MqlDateTime stm;
   TimeToStruct(TimeCurrent(),stm);

   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
   double PriceAsk=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   double PriceBid=SymbolInfoDouble(_Symbol,SYMBOL_BID);

   double SL1=NormalizeDouble(PriceBid-LossBuy*point,digits);
   double TP1=NormalizeDouble(PriceAsk+ProfitBuy*point,digits);
   double SL0=NormalizeDouble(PriceAsk+LossSell*point,digits);
   double TP0=NormalizeDouble(PriceBid-ProfitSell*point,digits);

   if(LossBuy==0)
      SL1=0;

   if(ProfitBuy==0)
      TP1=0;

   if(LossSell==0)
      SL0=0;

   if(ProfitSell==0)
      TP0=0;

//---------Buy1
   if(send1==false && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2) && stm.hour>H1 && stm.hour<H2 && H1<H2)
     {
      send1=trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,PriceAsk,SL1,TP1);
      TicketBuy1 = trade.ResultDeal();
     }

   if(send1==true && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketBuy1);
      send1=false;
     }

//---------Sell0

   if(send0==false && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2) && stm.hour>H3 && stm.hour<H4 && H3<H4)
     {
      send0=trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,1,PriceBid,SL0,TP0);
      TicketSell0 = trade.ResultDeal();
     }

   if(send0==true && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2))
     {
      trade.PositionClose(TicketSell0);
      send0=false;
     }
   K++;
  }

//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//---
   double ret=0.0;
//---

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

Matlab_MT5

Matlab_MT5

28. Запустим Matlab_MT5.ex5

Баланс

29. Баланс

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

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

Optim

Optim

Optim

30. Проведем оптимизацию

До красной линии 2011-2013 гг оптимизации. После нее 2013-2016 гг. тестирования. Понятно, что на таком длительном периоде после оптимизации мы торговать не будем и нейросети надо периодически переобучать. Из практики — в течение месяца. На графике период между красной линией и синей — 4 месяца.

Давайте протестируем этот период отдельно.

Test

Test

Эталонный тест Эталонный тест

Эталонный тест

31. Эталонный тест

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

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

5. Работа над ошибками

Работая над этим пунктом, я решил вернуться в начало и пояснить, почему он выходит таким громоздким? Дело в том, что "Работа над ошибками" проводилась, можно сказать, в режиме реального времени, в процессе написания данного раздела. В прочем, как и предыдущего. Для MetaTrader 4 все это уже отработано, а вот для MetaTrader 5 мне пришлось проводить работу в таком режиме. Лично для меня это оказалось достаточно полезным... А ваше внимание я хочу еще раз обратить на то, что к подготовке ведения торгов нужно подходить очень скрупулезно и системно.

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

И получили мы вот такой результат.

Отчет

32. Неудачный тест

Хоть результат и положительный, но он совершенно отличается от предыдущего. А это значит, что где-то в программном коде у нас ошибка и нам ее надо найти. И в этом случае нам поможет то, что мы все этапы передачи информации можем проследить.

Сравним отклики в полях вывода значений(4) под-модуля Net1 и отклики НСМ, полученных с помощью эксперта ExpertMtatlab_MT5.ex5. Естественно, на один и тот же временной бар.

Сравним отклики НСМ

33. Сравним отклики НСМ

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

Сравним идентичность информации в файлах EURUSDTest.csv и Input_mat.txt выбрав какой-либо бар во время работы НСМ.

Input_mat.txt

34. Сравним идентичность информации


Мы оказались правы, и информация действительно отличается друг от друга. Значит, нам надо проверить программный код экспертов ExpertMtatlab_MT5.ex5 и ExpertMatlabReal_MT5.ex5. 

ExpertMtatlab_MT5.ex5
if(stm.hour==0)
     {
      for(int i=19; i>=10; i--)
        {
         in[i-10]=in[i];
        }

      for(int i=29; i>=20; i--)
        {
         in[i-10]=in[i];
        }
     }
ExpertMatlabReal_MT5.ex5
if(Bar<Bars(NULL,PERIOD_H1) && stm.hour==0)
     {
      for(int i=19; i>=10; i--)
        {
         in[i-10]=in[i];
        }

      for(int i=29; i>=20; i--)
        {
         in[i-10]=in[i];
        }
     }

В результате наших действий мы нашли ошибку в коде эксперта ExpertMatlabReal_MT5.ex5. У нас отсутствовало основное условие сдвига данных на 00 часов. И при реальной работе наш НСМ получал бы информацию из терминала несоответствующую той, которую сети должны были бы получать. Вследствие чего они бы давали "ложный отклик". Кавычки я поставил потому, что на самом деле нейросеть сама по себе отклик дает нормальный, но для нас он будет ложным, поскольку мы ей предоставили информацию не ту, которую должны были исходя из наших соображений.

Снова сравним отклики в полях вывода значений(4) подмодуля Net1 и отклики НСМ, полученных с помощью эксперта ExpertMtatlab_MT5.ex5. 

Сравним отклики НСМ

35. Сравним отклики НСМ

Мы делаем вывод, что теперь информация, поступающая в НСМ, корректна.

Исправили данную ошибку и провели очередной тест в режиме визуализации:


Test

36. Тест отличается от эталонного

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

В результате проверки мы нашли ошибку в условиях закрытия позиции на продажу непосредственно в эксперте ExpertMatlabReal_MT5.mq5. Проведем очередное тестирование в режиме визуализации по OHLC на M1.

Без ожидания тика

Без ожидания тика Без ожидания тика

Без ожидания тика

37. Тест без ожидания тика

Результат еще не соответствует, скажем так, эталонному.

Эталонный тест

38. Эталон

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

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

 if(Order1==0 && Bar<Bars(NULL,PERIOD_H1) && Con==0)
      send0=true;
   
 if(Order1==1 && Bar<Bars(NULL,PERIOD_H1) && Con==0)
      send1=true;

//---------Buy

Тест будем проводить по всем тикам и на максимальной скорости визуализации. Процесс можно посмотреть на видео.

Тест с условиями

Тест с условиями Тест с условиями

Тест с условиями

39. Приблизились к эталонному тесту


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


Рассмотрим, почему же остановился НСМ. Я мог бы на этом вопросе и не останавливаться. Но хочу отметить то, что мы данный "баг" в программе выявили в режиме тестирования, носит положительный характер. Ведь с этой проблемой мы бы могли столкнуться в период ведения торгов. Дело в том, что если нейронные сети в НСМ будут не обучены, либо мы запустим модуль до запуска торгового эксперта и у нас еще не будет сформирован файл Input_mat.txt, то при нажатии кнопки "Start" НСМ не будет нам давать откликов. И я запрограммировал появление окна предупреждения с принудительным выходом из внутреннего таймера. Эту логическую ошибку я удалил, а предупреждение оставил. Однако если окошко выскакивает в процессе работы — это не страшно. Просто надо нажать на "OK", чтобы окно исчезло. Программа же в этом случае работает, не прерываясь.

Окно ошибки

40. Окно ошибки

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

Индикатор МТ5

41. Индикатор NWI в МТ5

Индикатор МТ5

42. Наблюдаем временной сдвиг

Для MetaTrader 4 эта проблема отсутствует.

Индикатор МТ4

43. Индикатор 1_MT4 в MetaTrader 4

Индикатор МТ4

44. Сдвиг отсутствует

На рисунке ниже показано, почему это происходит.

23:59

45. Находим причину сдвига

Заключение

Хочу остановиться на таком вопросе: почему же все-таки нейронные сети?

  1. Нейронные сети позволяют нам обрабатывать значительный объем информации вне рынка.
  2. Вследствие того, что информацию мы обрабатываем в режиме оффлайн, это дает нам возможность не бежать за рынком, а идти впереди него. Или, по крайней мере, в ногу.
  3. И как результат этого — оперативно учитывать изменения рыночной ситуации не только в период ведения торгов, а и на стадии подготовки к торгам. То есть не нести крупных потерь во время смены трендов, либо влияния на рынки каких либо факторов.
  4. Что же касается конкретной нейронной сети, представленной в статье, и способе ее обучения, то несомненным достоинством этого симбиоза является, то, что оптимизацию мы можем проводить по откликам, полученным на временном отрезке на котором проводилось обучение. Если нейронные сети других архитектур дают замечательный результат на периоде обучения, а на тестовом отрезке результаты значительно отличаются, то здесь результаты примерно одинаковы. Это нам дает возможность обучать сеть в плотную к сегодняшнему дню. И учитывать все рыночные факторы доступные на истории. Этот момент в статье я не раскрывал, но вы можете его исследовать самостоятельно.

Во время работы над статьей изначально я хотел в НСМ интегрировать лишь нейросети для блока Net1, которые обучены на время выхода статьи. Однако по завершению решил интегрировать для Net2 нейросети обученные по 31.12.2019 года. И для Net3 — по 31.12.2010 года. Интересно их протестировать в нынешних рыночных условиях. Для всех остальных видов работы в НСМ будут использованы только нейросети для Net1.

После скачивания файлов

Извиняюсь за "нерасторопное видео" — процесс снимал практически на "одном кадре".


Python


Прикрепленные файлы |
EURUSDData.csv (8009.95 KB)
NWI.mq5 (5.02 KB)
Matlab_MT5.mq5 (9.36 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (9)
secret
secret | 19 мая 2020 в 18:33
Denis Kirichenko:
Один Матлаб, без доп.пакетов, стОит столько:

Напрасная трата денег же)

Denis Dementiev
Denis Dementiev | 24 мая 2020 в 18:09
А откуда можно скачать все файлы? Или автор не предоставляет их? Спасибо
Alexsandr San
Alexsandr San | 24 мая 2020 в 18:57
Denis Dementiev:
А откуда можно скачать все файлы? Или автор не предоставляет их? Спасибо

вот

EURUSDData.csv(8009.95 KB)

NWI.mq5(5.02 KB)

Matlab_MT5.mq5(9.36 KB)

Снимок

Andrey Dibrov
Andrey Dibrov | 24 мая 2020 в 21:03
Файлы можно скачать под видео)))
Gladiator WXT
Gladiator WXT | 6 июл. 2020 в 22:37

Статья достаточно слабая - автор не приводит ни описания архитектуры НС, ни арзхитектуры связанного приложения, ни современного способа организации взаимодействия MT5<->Matlab через Matlab Engine API.

Вроде как создается впечатление что ценность статьи в представленных MT5-советниках, но на самом деле ценность их нулевая - представленный способ обмена информацией через файлы неуклюжий, древний и тормозной. Возможно он и подходит для торговли ТФ Н1 и выше, но на даже на таких ТФ практике вызывает появление случаев запаздывания сигналов, о которых автор откровенно и пишет в разделе "Работа над ошибками". Но ирония в том что основная ошибка в появлении лагов не исправлена - файловый интерфейс оставлен прежним.

Вычисление математических выражений (Часть 1). Парсеры рекурсивного спуска Вычисление математических выражений (Часть 1). Парсеры рекурсивного спуска
В статье рассматриваются базовые принципы разбора и вычисления математических выражений, реализованы парсеры рекурсивного спуска, работающие в режимах интерпретатора и быстрых расчетов на основе предварительно построенного синтаксического дерева.
Инструментарий для быстрой ручной торговли: Базовый функционал Инструментарий для быстрой ручной торговли: Базовый функционал
На текущий момент всё больше трейдеров переходят на автоматические системы торговли, которые либо требуют начальную настройку, либо часть из них уже полностью автоматизированы. Тем не менее остается немалая часть трейдеров, которые торгуют руками по старинке. В данной статье создадим набор инструментов для быстрой ручной торговли с помощью горячих клавиш и выполнения типичных торговых действий в один клик.
Инструментарий для быстрой ручной торговли: Работа с открытыми и отложенными ордерами Инструментарий для быстрой ручной торговли: Работа с открытыми и отложенными ордерами
В этой статье расширим возможности инструментария, добавим в него возможности закрыть торговых позиций по условиям, а также создадим таблицы учета рыночных и отложенных ордеров с возможностью их редактирования.
О методах поиска зон перекупленности/перепроданности. Часть I О методах поиска зон перекупленности/перепроданности. Часть I
Зоны перекупленности/перепроданности характеризуют определённое состояние рынка, отличающееся ослаблением динамики цен финансовых инструментов. Причём такое негативное изменение динамики наиболее выражено в заключительной стадии развития тренда любого масштаба. А так как величина прибыли при трейдинге напрямую зависит от возможности охвата максимальной амплитуды тренда, то точность выявления таких зон является важнейшей задачей при торговле любым финансовым инструментом.