利用 MQL5 向导和 Hlaiman EA 生成器创建神经网络 EA

Ivan Negreshniy | 25 三月, 2014

简介

几乎每一位交易者都知道神经网络的存在。但对于大多数人来讲,它还是一个“黑盒子”,仅为外界所知的,就是神经网络识别形态、生成解决方案的联想搜索和学习能力,以及有效预测市场行为和自动交易的事实。通常侧重神经网络应用的许多信息来源都大谈其难度,强调必须投入大量的时间和精力才能学好这门学科,才能在未来很好地使用神经网络。

本文则旨在驳斥这些论断,并证实高级自动化方法会让交易者的神经网络工作容易上手,并避免了漫长的学习过程。积累自己的神经网络相关经验并非难事。它肯定要比技术分析简单。

我们将带着这个想法,利用 MQL5 向导Hlaiman EA 生成器,讲述自动生成 MetaTrader 5 神经网络 EA 的一种方法。

解决这一手头任务所用的工具绝非随机选择的:

  1. MQL5 向导 是一种高效、且迄今为止最快速的自动化 MQL5 代码生成机制,允许您利用附加模块周密细致地调整生成的代码。
  2. Hlaiman EA 生成器是一种神经网络引擎,有灵活的对象集成机制,且可直接在 EA 交易的 MQL5 代码中编程。

EA 交易的名称前故意加上缩写 “EA”,因为在神经网络 EA 中,与识别和学习关联的类人属性占据支配地位,这点与其它情况(如在名称中使用 “EA”则通常都会产生误导且未体现出内容的真实属性)不同。


综述

出于本文宗旨所讲的原因,这里没有神经网络方面的任何理论知识、分类和结构,或是金融市场相关的调查数据。该等信息请查询其它资料。本文中,我们刻意将神经网络的概念限制为:一种基于图形价格形态识别、拥有联想思维及预测入市能力的黑盒子。出于同样的原因,我们还将最简单的形态概念,作为可盈利价格变动之前某交易工具图表中的一系列连续柱。

说一说解决问题的工具。与 Hlaiman 不同,MQL5 向导通常都是各种文章和文档的主题,而且像 MetaTrader 5 一样,无需介绍。面向社会的 Hlaiman 项目旨在以插件形式开发和推广多用途软件,而 EA 生成器即为其中之一。前面提到过,功能多样的 EA 生成器代表一个带有集成机制的神经网络引擎。

Hlaiman EA 生成器包括 <hlaim.exe> - 一个代表带有多文档界面和插件(动态加载组件库形式)的 Windows GUI 应用程序的壳程序。该系统还有多种手动与算法调整,以及标准与可加载型组件控制方法,作为插件的一部分提供。系统操作期间,您可以利用“对象检视器”和自动化软件工具(比如脚本)创建复杂的对象层级,实现对于其方法和属性的灵活掌控。

Hlaiman EA 生成器与 MQL5 的集成,会牵涉到“对象 Pascal”脚本解释程序,同时源代码通过命名管道传递。将一个多层感知器 (MLP) 作为主神经网络组件使用。

利用一个信号库模块 - SignalHNN.mqh,将 Hlaiman EA 生成器与 MQL5 向导集成。自动生成后,即可训练 EA 交易针对任何数量的金融工具和时间框架进行交易。在 МetaТrader 5 终端中,指示信号的箭头,都可以或利用箭头图形对象于价格图表中手动绘制,或是利用 TeachHNN.mq5 脚本自动完成,同时启动训练 EA 交易的过程。

理论描述到此结束。现在,我们继续实践环节,该环节分为两个部分 - 操作原则与实施。

第二部分面向软件开发人员,而在这里提供的主要是出于对该网站的尊重。因此,它可供选择,尤其是那些对获取编程技能没什么兴趣、却想创建神经网络 EA 并评估其交易效率或作用的交易者。


操作原则

MQL5.community 中,很可能就没有必要再提醒需要 MetaТrader 5 终端才能继续了。如您没有该终端,则进行下载并安装。您还要下载并安装 Hlaiman EA 生成器的一个试用版本

启动 МetaТrader 5 终端,打开 MetaEditor。打开 MQL5 向导。为此,您可以利用 Standard (标准)工具栏中或 File (文件)菜单下的 "New" (新建)选项 ,或是使用 "Ctrl+N" 热键。

在 MQL5 向导窗口中,选择 "Expert Advisor (generate)" (EA 交易(生成))并单击 "Next" (下一步)。

图 1. 在 MQL5 向导中创建“EA 交易”

图 1. 在 MQL5 向导中创建“EA 交易”

指定 EA 交易的位置和名称,比如 'Experts\SampleHNN',并单击 "Next"。

图 2.“EA 交易”的一般属性

图 2. “EA 交易”的一般属性

单击 "Add" (添加)按钮。您会看到 "Parameters of Signal Module" (信号模块的参数)窗口,从下拉列表中选择 'Signals of patterns Hlaiman Neural Network EA generator' (Hlaiman 神经网络 EA 生成器形态的信号)并单击 "OK" (确定)。

图 3. 选择 Hlaiman 神经网络 EA 生成器的交易信号模块

图 3. 选择 Hlaiman 神经网络 EA 生成器的交易信号模块

如无额外实施要求,MQL5 向导的其余所有步骤,您可以一路点击 "Next"。必要时,可通过选择额外选项来强化 EA 交易。

代码生成完毕后,单击 "Compile" (编译)并关闭 MetaEditor 窗口。生成的 EA 交易就会出现于 MetaTrader 5 终端 Navigator (导航器)面板的 "Expert Advisors" 下。

图 4. SampleHNN EA 交易

图 4. SampleHNN EA 交易

在我们继续训练生成的 EA 交易之前,需要在终端打开一个带有所需交易品种的图表和时间框架。Hlaiman EA 生成器应用程序必须开始运作。

图 5. 神经网络训练预备

图 5. 神经网络训练预备

要训练 EA 交易,请在终端选择 Navigator 面板中 'Scripts' 下的 'TeachHNN',并为指定的图表激活它。

运行 'TeachHNN' 脚本之前,您要确保所有设置均已妥当。它拥有下述参数:

图 6. TeachHNN 脚本参数

图 6. TeachHNN 脚本参数

如一切就绪,则单击 'OK' 以启动训练 EA 交易的过程。这样就会启动图表中每个可用信号图形形态的自动生成。

相关信息显示于终端 'Toolbox' 面板的 'Experts' (专家)选项卡中,而相应的对象则显示于 Hlaiman EA 生成器窗口中。

形态生成后,训练过程即开始。它显示于出现在屏幕上的训练进度条中。

训练 Hlaiman EA 生成器

图 7. 训练进度板

直至该过程完成。右键单击训练进度条,并在上下文菜单中选择相应选项,即可在训练过程完成之前予以中止。

完成训练过程和脚本运行后,相关消息就会被添加到 'Experts' 选项卡的日志中,比如 'Neural net create success!(神经网络创建成功)On 431 patterns' 就表明已利用 431 个信号成功完成了 EA 交易的训练。

此类信息会显示出训练过程中涉及到多少种形态,并找出此类形态的数量。特别是买入和卖出,均利用下述类型的消息确定:'Sell signal detected at pattern #211'(在形态 #211 处探测到卖出信号)。

图 8. 训练过程中的 TeachHNN 脚本消息

图 8. 训练过程中的 TeachHNN 脚本消息

训练 EA 交易的过程可能一开始就有错误,原因如下:

  1. 启动前,Hlaiman 应用程序未按要求运行。这种情况下,就会出现下述错误消息 "CSignalHNN::InitHNN:Error! initializing pipe server (possible reason:HLAIMAN APPLICATION IS NOT RUNNING!)"
  2. 禁用信号自动生成后,图表中指示信号的箭头缺失(SignalsCreate 变量 = false)。这种情况下的错误消息显示如下:"OnStart:error, orders arrow not found!"如已启用信号的自动生成(SignalsCreate 变量 = true),则可能因图表中存在其它图形对象而导致错误,因为程序中的自定义标记不应该混用。因此,为了自动生成信号,建议分别打开所有图表。

完成 EA 交易的训练后,您可以切换到 GUI Hlaiman 并选择相应的对象和可视化面板,以查看相关结果。

图 9. Hlaiman 应用程序的 'Text' 选项卡

图 9. Hlaiman 应用程序的 'Text' 选项卡


图 10. Hlaiman 应用程序的 'Graph' 选项卡

图 10. Hlaiman 应用程序的 'Graph' 选项卡

至少在一个交易工具上成功完成 EA 交易的训练后,我们就可以继续测试和(或)优化了。

为此,请在策略测试程序中选择完成训练的 EA 交易的名称、交易品种、时间框架、间隔以及其它测试参数。必要时设置外部变量并运行测试。

图 11. SampleHNN EA 交易的回测设置

图 11. SampleHNN EA 交易的回测设置

图 12. SampleHNN EA 交易的外部参数可行修改

图 12. SampleHNN EA 交易的外部参数可行修改

下面就是策略测试程序中 EA 交易运行报告的一个示例。该 EA 交易采用自动生成的信号训练,且训练脚本的所有外部参数都是默认设置。训练周期:01.01.2010-01.07.2013,工具:EURUSD H4。


策略测试报告

EA 交易:SampleHNN
交易品种: EURUSD
期段: H4 (2010.01.01-2013.07.12)
货币: USD
初始存入: 10,000.00
杠杆率: 0,111111111
回测
历史质量: 100%
柱: 5497
净利润: 9,159.58
毛利: 29,735.97
净损失: -20,576.39
获利系数: 1.45
回收系数: 12.81
AHPR: 1.0005 (0.05%)
GHPR: 1.0005 (0.05%)
总交易: 1417
总成交: 2246
价格变动: 60211228
余额亏损绝对值: 0.00
余额亏损最大值: 679.98 (3.81%)
余额亏损相对值: 4.00% (715.08)
预计获利: 6.46
夏普比率: 0.16
LR 相关性: 0.98
LR 标准错误: 595.06
短线交易(获利%) 703 (56.61%)
盈利交易(总交易的%): 793 (55.96%)
最大获利交易: 53.00
平均获利交易: 37.50
最大连续盈利: 9 (450.38)
最大连续获利: 450.38 (9)
平均连续盈利: 2
交易品种: 1
市值亏损绝对值: 6.60
市值亏损最大值: 715.08 (4.00%)
市值亏损相对值: 4.00% (715.08)
保证金水平: 6929.24%
Z 帐户: -1.24 (78.50%)
OnTester 结果: 0
长线交易(获利%) 714 (55.32%)
亏损交易(总交易的%): 624 (44.04%)
最大亏损交易: -53.30
平均亏损交易: -32.97
最大连续亏损: 9 (-234.00)
最大连续亏损: -276.67 (7)
平均连续亏损: 2

图 13. SampleHNN EA 交易回测结果

图 13. SampleHNN EA 交易回测结果


图 14. SampleHNN EA 交易入市统计资料

图 14. SampleHNN EA 交易入市统计资料


图 15. 利润与 SampleHNN EA 交易 MFE/MAE 的相关性

图 15. 利润与 SampleHNN EA 交易 MFE/MAE 的相关性


图 16. SampleHNN EA 交易持仓时间统计资料

图 16. SampleHNN EA 交易持仓时间统计资料

实施

MQL5 实施的主要组件,即 SignalHNN.mqh 信号模块中描述的 CSignalHNN 类。该类由 CExpertSignal 基类继承而来,包含 Hlaiman 操作、集成的所有必要数据字段和方法,以及利用 MQL5 向导生成的 EA 交易的处理。

类模板如下:

//+------------------------------------------------------------------+
//| Class CSignalHNN.                                                |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'Hlaiman EA Generator Neural Net' indicator.        |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+
class CSignalHNN :public CExpertSignal
  {
protected:
   //--- variables
   int               m_hnn;                   // handle of HNN connect
   string            hnn_path;                // MT5 Terminal data path
   string            hnn_fil;                 // HNN file w neural net 
   string            hnn_nam;                 // Expert name
   string            hnn_sym;                 // Symbol name
   string            hnn_per;                 // Period name
   ENUM_TIMEFRAMES   hnn_period;              // Period timeframe
   int               hnn_index;               // Index ext multinet
   int               hnn_bar;                 // index of last bar
   int               hnn_in;                  // input layer 
   int               hnn_out;                 // output layer
   int               hnn_layers;              // layers count
   int               hnn_neurons;             // neurons count
   int               hnn_epoch;               // learn epoch
   double            hnn_signal;              // value of last signal
   double            pattern[];               // values of the pattern
   bool              hnn_norm;                // normalize pattern

public:
                     CSignalHNN(void);        // class constructor
                    ~CSignalHNN(void);        // class destructor
   //--- methods of setting adjustable parameters
   void              PatternBarsCount(int value) { hnn_in = value; ArrayResize(pattern, value + 1);  }
   void              LayersCount(int value)      { hnn_layers = value;  }
   void              NeuronsCount(int value)     { hnn_neurons = value;  }
   void              EpochCount(int value)       { hnn_epoch = value;  }
   void              Normalize(bool value)       { hnn_norm = value;  }
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking conditions of entering the market
   virtual double    Direction(void);

   bool              FillPattern(datetime tim = 0);      // prepare pattern
   bool              AddPattern(string name, int ptype);  // add new pattern
   bool              TeachHNN(void);                     // learn neural net
   bool              SaveFileHNN(void);                  // neural net file
   double            CalculateHNN(void);                 // calc neural signal

                                                        //protected:
   //--- method of initialization of the Hlaiman Application
   bool              InitHNN(bool openn);                // Hlaiman App Init
   void              FreeHNN(void)
     {                     // Hlaiman App Deinit
      if(m_hnn!=0 && m_hnn!=INVALID_HANDLE)
        {
         FileClose(m_hnn);
         m_hnn=0;
        }
     };
  };

利用构造函数创建此类实例后,该对象可在两种主要模式下工作:

  1. 训练模式:此模式与市场形态的采集和神经网络的训练相关联。
  2. 指标模式:在此模式下,神经网络信号是利用当前形态计算。

利用布尔型参数 openn 调用 InitHNN 初始化模式后,此模式即确认。在指标模式 (2)中,此参数的 true 值会初始化训练神经网络的搜索和打开、加载和运行以及相关数据文件。此模式被视为操作模式,用于 EA 交易中的交易。

与训练模式 (1) 利用 openn=false 调用 InitHNN 方法时初始化不同,指标模式是 EA 交易的筹备,且用于训练脚本的操作。

初始化方法实施如下:

//+------------------------------------------------------------------+
//| Initialize HNN                                                   |
//+------------------------------------------------------------------+
bool CSignalHNN::InitHNN(bool openn)
  {
//--- initialize Hlaiman Application
   int num=0;
   ulong res=0;
   if(m_symbol!=NULL)
     {
      hnn_sym=m_symbol.Name();
      hnn_period=m_period;
        } else {
      hnn_sym=_Symbol;
      hnn_period=_Period;
     }
   hnn_per = string(PeriodSeconds(hnn_period) / 60);
   hnn_fil = hnn_nam + NAME_DELIM + hnn_sym + hnn_per + NAME_DELIM + string(hnn_index) + TYPE_NEURO;
   if(m_hnn== 0|| m_hnn == INVALID_HANDLE)
      m_hnn=FileOpen(HLAIMAN_PIPE,FILE_READ|FILE_WRITE|FILE_BIN);
   if(m_hnn!=0 && m_hnn!=INVALID_HANDLE)
     {
      string source,result="";
      if(openn==true)
        {
         result=CON_OPENN+CON_TRUE;
         if(!FileIsExist(hnn_fil,FILE_READ))
           {
            if(FileIsExist(hnn_fil,FILE_READ|FILE_COMMON))
               hnn_fil=TerminalInfoString(TERMINAL_COMMONDATA_PATH)+PATH_FILES+hnn_fil;
            else
              {
               //              hnn_fil = hnn_path + PATH_MQL5 + PATH_FILES + hnn_fil;
               hnn_fil=TerminalInfoString(TERMINAL_DATA_PATH)+PATH_MQL5+PATH_FILES+hnn_fil;
              }
           }
         else hnn_fil=TerminalInfoString(TERMINAL_DATA_PATH)+PATH_MQL5+PATH_FILES+hnn_fil;
           } else {
         result=CON_OPENN+CON_FALSE;
         hnn_fil=TerminalInfoString(TERMINAL_DATA_PATH)+PATH_MQL5+PATH_FILES+hnn_fil;
        }
      source="unit InitHNN; Interface "+result+" var libr, term, exp, sym: TObject;"
             " Implementation function main: integer;\n\r" // Line #1
             " begin"
             " Result := 0;"
             " libr := Open('mt45.dll');\n\r" // Line #2
             " if (libr <> nil) then"
             " begin"
             " term := Open('"+hnn_path+"');\n\r" // Line #3
             " if (term <> nil) then"
             " begin"
             " exp := term.ObjectOfName('"+hnn_nam+"');"
             " if (exp = nil) then exp := term.AddObject('TMT45Expert');\n\r" // Line #5
             " if (exp <> nil) then"
             " begin"
             " if (exp.Name <> '"+hnn_nam+"') then exp.Name := '"+hnn_nam+"';\n\r" // Line #6
             " sym := exp.ObjectOfName('"+hnn_sym+hnn_per+"');"
             " if (sym = nil) then sym := exp.AddObject('TMT45Symbol');"
             " if (sym <> nil) then"
             " begin"
             " sym.Log.Add('"+hnn_sym+hnn_per+"');\n\r"
             " if (sym.Name <> '"+hnn_sym+hnn_per+"') then sym.Name := '"+hnn_sym+hnn_per+"';"
             " if (sym.Period <> "+hnn_per+") then sym.Period := "+hnn_per+";"
             " if (openn = true) then"
             " begin"
             //                   " sym.Log.Add('" + hnn_fil + "');"
             " if (sym.Open('"+hnn_fil+"')) then Result := sym.TeachInput;\n\r" // ret input Line #8
             " end else"
             " begin"
             " sym.TeachInput := "+IntegerToString(hnn_in)+";"
             " sym.TeachOutput := "+IntegerToString(hnn_out)+";"
             " sym.TeachLayer := "+IntegerToString(hnn_layers)+";"
             " sym.TeachNeurons := "+IntegerToString(hnn_neurons)+";"
             " sym.TeachEpoch := "+IntegerToString(hnn_epoch)+";"
             " sym.FileName := '"+hnn_fil+"';"
             " Result := sym.TeachInput;\n\r" // ret input Line #9
             " end;"
             " end;"
             " end;"
             " end;"
             " end;"
             " end; end.";
      FileWriteString(m_hnn,source,StringLen(source));
      FileFlush(m_hnn);
      while(res<=0 && (MQL5InfoInteger(MQL5_TESTER) || num<WAIT_TIMES))
        {
         Sleep(SLEEP_TIM);
         res=FileSize(m_hnn);
         num++;
        }
      if(res>0)
        {
         result=FileReadString(m_hnn,int(res/2));
         res=StringToInteger(result);
         if(res<=RES_OK)
            printf(__FUNCTION__+": Error! Initialization data(possible reason: FILE NOT EXIST OR CORRUPTED "+hnn_fil);
         else
           {
            printf(__FUNCTION__+": Initialization successful! NEURAL PATTERN "+string(res));
            ArrayResize(pattern,int(res+1));
            return(true);
           }
        }
      else
         printf(__FUNCTION__+": Error! pipe server not responding(possible elimination: RESTART HLAIMAN APPLICATION)");
     }
   else
      printf(__FUNCTION__+": Error! initializing pipe server (possible reason: HLAIMAN APPLICATION IS NOT RUNNING!)");
//--- ok
   return(false);
  }

从代码中可以看出,第一个初始化步骤中包括打开一个连接 Hlaiman 应用程序命名管道的一次尝试。如果尝试失败(比如 <hlaim.exe> 未运行),则执行退出、且为负状态。第二步(第一步成功完成并运行指标模式后),就是在终端的本地与共用文件夹中,利用神经网络数据搜索所需文件名。第三步,是处理 ObjectPascal (Delphi) 中代码的准备工作,以便直接在 Hlaiman 应用程序中初始化。

之后,代码的文本被移至源字符串。为了格式化方便,利用 '\n\r' 将其分解成多个子字符串,并包含 Hlaiman 对象属性和方法的调用(参见注释)。正如文本中的定义,基于对象的 MetaTrader 5 Hlaiman 插件环境呈树状架构,插件对象则位于根部。

МetaТrader 5 终端对象位于下一层级,之后则是 EA 交易和交易品种对象。如已成功转换、且源代码的执行已通过命名管道传递,那么返回的 Result 值将包含神经网络输入向量的元素数量。正如此代码所示,该值用于初始化形态数组,而且方法的执行也是正状态完成。

CSignalHNN 类中还包括 CalculateHNN、AddPattern 和 TeachHNN 等关键方法。第一种会在指标模式下返回神经网络计算的结果。另两种方法则用于训练模式-分别是采集形态和初始化神经网络训练过程。

上述方法在 <SignalHNN.mqh> 中实施如下:

//+------------------------------------------------------------------+
//| Calculate HNN signal                                             |
//+------------------------------------------------------------------+
double CSignalHNN::CalculateHNN(void)
  {
   if(m_hnn==0 || m_hnn==INVALID_HANDLE) return(0.0);
   int num = 0;
   ulong siz = 0;
   double res=0.0;
   string source,result="";
   if(FillPattern(0)==true)
     {
      result=CON_START;
      for(int i=1; i<(ArraySize(pattern)-1); i++)
         result= result+DoubleToString(pattern[i])+CON_ADD;
      result = result + DoubleToString(pattern[ArraySize(pattern) - 1]) + CON_END;
      source = "unit CalcHNN; Interface " + result + " var i: integer; libr, term, exp, sym, lst: TObject;"
              " Implementation function main: double;\n\r" // Line #1
              " begin"
              " Result := 0.0;"
              " libr := Open('mt45.dll');\n\r" // Line #2
              " if (libr <> nil) then"
              " begin"
              " term := Open('"+hnn_path+"');\n\r" // Line #3
              " if (term <> nil) then"
              " begin"
              " exp := term.ObjectOfName('"+hnn_nam+"');\n\r" // Line #4
              " if (exp <> nil) then"
              " begin"
              " sym := exp.ObjectOfName('"+hnn_sym+hnn_per+"');\n\r" // Line #5
              " if (sym <> nil) then"
              " begin"
              " lst := TStringList.Create;"
              " if (lst <> nil) then"
              " begin"
              " lst.Text := cons;"
              " if (lst.Count >= sym.NetInputs.Count) then"
              " begin"
              " for i := 0 to sym.NetInputs.Count - 1 do"
              " begin"
              " sym.NetInputs.Objects[i].NetValue := StrToFloat(lst[i]);\n\r" // Line #6
              //                    " sym.Log.Add('Input ' + IntToStr(i) + ' = ' + lst[i]);"              
              " end;"
              " sym.Computed := true;"
              " Result := sym.NetOutputs.Objects[0].NetValue;\n\r" // ret input Line #7
              " end;"
              " lst.Free;"
              " end;"
              " end;"
              " end;"
              " end;"
              " end;"
              " end; end.";
      FileWriteString(m_hnn,source,StringLen(source));
      FileFlush(m_hnn);
      while(siz<=0 && (MQL5InfoInteger(MQL5_TESTER) || num<WAIT_TIMES))
        {
         Sleep(SLEEP_TIM);
         siz=FileSize(m_hnn);
         num++;
        }
      if(siz>0)
        {
         result=FileReadString(m_hnn,int(siz/2));
         res=StringToDouble(result);
        }
     } //else Print("fill pattern error!");
   return(res);
  }
//+------------------------------------------------------------------+
//| AddPattern                                                       |
//+------------------------------------------------------------------+
bool CSignalHNN::AddPattern(string name,int ptype)
  {
   int num=0;
   long res=0;
   ulong siz=0;
   string result,source,nam=name;
   if(m_hnn!=0 || m_hnn!=INVALID_HANDLE)
     {
      pattern[0]=ptype;
      result=CON_START;
      for(int i=0; i<(ArraySize(pattern)-1); i++)
         result= result+DoubleToString(pattern[i])+CON_ADD;
      result = result + DoubleToString(pattern[ArraySize(pattern) - 1]) + CON_END;
      source = "unit AddPatternHNN; Interface " + result + " Implementation function main: integer;"
              " var i: integer; out: double; onam: string;"
              " libr, term, exp, sym, ord, tck, lst: TObject;\n\r" // Line #1
              " begin"
              " Result := 0;"
              " libr := Open('mt45.dll');\n\r" // Line #2
              " if (libr <> nil) then"
              " begin"
              " term := Open('"+hnn_path+"');\n\r" // Line #3
              " if (term <> nil) then"
              " begin"
              " exp := term.ObjectOfName('"+hnn_nam+"');\n\r" // Line #4
              " if (exp <> nil) then"
              " begin"
              " sym := exp.ObjectOfName('"+hnn_sym+hnn_per+"');\n\r" // Line #5
              " if (sym <> nil) then"
              " begin"
              " lst := TStringList.Create;"
              " if (lst <> nil) then"
              " begin"
              " lst.Text := cons;"
              " if (lst.Count >= (sym.TeachInput + sym.TeachOutput)) then"
              " begin"
              " out := StrToFloat(lst[0]);"
              " if(out >= 0) then onam := 'BUY-"+nam+"'"
              " else onam := 'SELL-"+nam+"';"
              " ord := sym.ObjectOfName(onam);"
              " if (ord = nil) then ord := sym.AddObject('TMT45Order');\n\r" // Line #6                    
              " if (ord <> nil) then"
              " begin"
              " if (ord.Name <> onam) then ord.Name := onam;\n\r" // Line #7
              " if (out >= 0) then ord.OrderType := 0 else ord.OrderType := 1;"
              " if (ord.NetOutput <> out) then ord.NetOutput := out;\n\r" // Line #8
              " for i := 1 to sym.TeachInput do"
              " begin"
              " if(i <= ord.Count) then tck := ord.Items[i - 1] else"
              " tck := ord.AddObject('TMT45Tick');\n\r" // Line #10                    
              " if (tck <> nil) then"
              " begin"
              " tck.x := i;"
              " tck.y := StrToFloat(lst[i]);\n\r" // Line #11
              " end;"
              " end;"
              " end;"
              " Result := sym.Count;\n\r" // ret input Line #12
              " end;"
              " lst.Free;"
              " end;"
              " end;"
              " end;"
              " end;"
              " end;"
              " end; end.";
      FileWriteString(m_hnn,source,StringLen(source));
      FileFlush(m_hnn);
      while(siz<=0 && (MQL5InfoInteger(MQL5_TESTER) || num<WAIT_TIMES))
        {
         Sleep(SLEEP_TIM);
         siz=FileSize(m_hnn);
         num++;
        }
      if(siz>0)
        {
         result=FileReadString(m_hnn,int(siz/2));
         res=StringToInteger(result);
        }
     }
   return(res>0);
  }
//+------------------------------------------------------------------+
//| TeachHNN                                                         |
//+------------------------------------------------------------------+
bool CSignalHNN::TeachHNN(void)
  {
   int num=0;
   long res=0;
   ulong siz=0;
   string result,source;
   if(m_hnn!=0 || m_hnn!=INVALID_HANDLE)
     {
      source="unit TeachHNN; Interface const WAIT_TIM = 100; WAIT_CNT = 100;"
             "  var i: integer; libr, term, exp, sym: TObject;"
             " Implementation function main: integer;\n\r" // Line #1
             " begin"
             " Result := 0;"
             " libr := Open('mt45.dll');\n\r" // Line #2
             " if (libr <> nil) then"
             " begin"
             " term := Open('"+hnn_path+"');\n\r" // Line #3
             " if (term <> nil) then"
             " begin"
             " exp := term.ObjectOfName('"+hnn_nam+"');\n\r" // Line #4
             " if (exp <> nil) then"
             " begin"
             " sym := exp.ObjectOfName('"+hnn_sym+hnn_per+"');\n\r" // Line #5
             " if (sym <> nil) then"
             " begin"
             " if (sym.Teached) then sym.Teached := false;\n\r" // Line #6
             " sym.Teached := true;\n\r" // Line #7
             " Result := sym.Count;\n\r" // ret input Line #8
             " end;"
             " end;"
             " end;"
             " end;"
             " end; end.";
      FileWriteString(m_hnn,source,StringLen(source));
      FileFlush(m_hnn);
      while(siz<=0)
        {// && (MQL5InfoInteger(MQL5_TESTER) || num < WAIT_TIMES)) {
         Sleep(SLEEP_TIM);
         siz=FileSize(m_hnn);
         num++;
        }
      if(siz>0)
        {
         result=FileReadString(m_hnn,int(siz/2));
         res=StringToInteger(result);
        }
     }
   return(res>0);
  }

从代码中可以看出,该方法主体由源线构成,其文本的排列与上文讲述的 InitHNN 方法很相似。唯一的区别在于,基于对象插件层级的形态呈现多两个级别 - 订单和价格变动。此外,代码中还包含其他对象属性和方法。比如说,神经网络计算开始时,要以 'symbol' 对象“Computed”标志为标志,而 "Teached" 标志则是在初始化训练过程时使用。

CalculateHNN 与其它方法的不同,还在于前者函数返回的 'main' 值是 'double' 型的。该值为神经网络输出 - 信号,其中的买入水平在 0..1 范围之间,而卖出水平则在 0..-1 之间。此信号用于 EA 交易做出有关交易仓位建仓平仓的决定,且由 Direction 方法控制。此方法会在有新柱的情况下执行重新计算,并返回其以百分比形式表示的值。

//+------------------------------------------------------------------+
//| Check conditions for trading signals.                            |
//+------------------------------------------------------------------+
double CSignalHNN::Direction(void)
  {
   if( m_hnn == 0 || m_hnn == INVALID_HANDLE) return(EMPTY_VALUE);
//--- check new bar condition
   int cur_bar = Bars(hnn_sym, hnn_period);
   if (hnn_bar != cur_bar) {
//--- condition OK
      hnn_signal = CalculateHNN() * 100;
      hnn_bar = cur_bar;
   }
   return(hnn_signal);
  }

要设置 EA 交易建、平交易仓位的信号响应阈值,您可以使用下述外部变量:

实际上,信号水平取决于神经网络训练质量和强度,而一般来说,后者又可以通过监控训练过程中指标中所显示的计算错误动态的减少,实施可视化评估。


总结

为与 MQL5 集成,Hlaiman EA 生成器提供了多种组件和一种面向对象环境的透明控制,借此:

  1. MQL5 向导界面得到了一种基于信号和形态识别的额外类型,以及生成神经网络 EA 的可能性。
  2. 除了快速生成神经网络 EA 的能力外,您还可以快速调整以令其适应不断变化的市场行为,并针对不同的交易工具和时间框架重复训练它们。
  3. 鉴于 MQL5 向导可以启用多信号模块,您可以创建复杂的多货币神经网络 EA 和(或)复合的、基于指标的神经网络 EA。它们还可以与各种各样的其他过滤器结合,比如时间过滤器。
  4. 而且最终,神经网络模块本身亦可作为一种附加过滤器使用,从而提高即用型 EA 交易的效率。利用原始 EA 交易测试结果可视化图表即可训练神经网络,所以这是很有可能的。

使用脚本解释程序会导致集成的计算系统看起来不太高效,这也可以说是上述实施的缺点之一。但是,首先要注意的是,脚本代码解释程序以及 Hlaiman 插件的运行,都不与 EX5 同步,也就是说,我们要并行处理任务。第二,要加快耗时计算的速度(比如处理大型神经网络时),MetaTrader 5 和 Hlaiman 可在不同的计算机上运行,并通过命名网络管道连接。在独立的计算机上启动交易终端,不仅有性能方面的增益,安全性也会有所改善。

客观来看,我们可以深入开发交易过程中能够自学的 EA 交易。目前来讲,做成此事最简单的方式,就是将 EA 交易的代码与训练脚本代码综合起来,因为两者都是采用相同的 CSignalHNN 类提供需要的功能。如有人感兴趣,此材料可以成为后续文章的主题,也可以构成一篇全新文章的基础。

Hlaiman EA 生成器可于下载

附件: