如何构建和使用 MetaTrader 4的策略测试器来测试二元期权策略

Martin Amiri | 30 一月, 2017


目录

1. 简介
2. 安装
3. 二元期权策略实例
3.1. 定义二元期权策略
3.2. 创建二元期权策略
3.2.1. 输入参数
3.2.2. 包含 Binary-Options-Strategy-Library
3.2.3. 加上 CallStrategy()
3.2.4. 实现 CheckMyRules() 和辅助函数
3.2.5. 打印出调试的数值
3.2.6. 使用外部指标 (ex4 文件)
3.3. 完整的代码
4. 运行回溯测试 (视频)
5. 运行前瞻测试
6. 常见问题
7. 杂项


1. 简介

本文展示了如何使用 Binary-Options-Strategy-Tester 工具构建和在 MetaTrader 4的策略测试器中测试一个二元期权策略。默认条件下,MetaTrader 4的策略测试器可以在历史上测试EA交易和指标,但是它无法使用过期时间处理二元期权。因为我需要一种功能可以在 MetaTrader 4的策略测试器中测试二元期权策略,就构建了 Binary-Options-Strategy-Tester 作为工具来满足这些需求。

概念包含了以下部分:

二元期权策略测试器(Binary Options Strategy Tester)的概念

这是一个分步的实例,关于如何构建一个保存在指标中的二元期权策略(上图中用红色标记)来通过 Binary-Options-Strategy-Library(上图中以绿色标记)和 Binary-Options-Strategy-Tester (上图中以蓝色标记)进行通信, 设置虚拟订单并通过回溯测试和前瞻测试统计结果。

请记住: 使用历史数据做回溯测试永远不能代表真实的未来,但是它可能给您一个近似值以使您的策略更加可靠。
您回溯测试的质量将依赖于您的历史数据,所以强烈推荐使用一组高质量的历史数据!


2. 安装

从市场下载并购买 Binary-Options-Strategy-Tester(二元期权策略测试器) 工具:
用于在 MetaTrader 4的策略测试器中测试二元期权策略的测试框架。

为什么需要完整版的 Binary-Options-Strategy-Tester 工具?
二元期权策略必须调用 Binary-Options-Strategy-Tester 的一个函数(通过 Binary-Options-Strategy-Library) 来设置虚拟交易,因为 MQL4 版权许可的问题,这只能在完整功能授权的版本中才能使用,所以您必须购买产品来测试二元期权策略或者本实例。

下载免费的 BinaryOptionsStrategyLibrary.mqh 文件并把它放到文件夹 \Include ([您 MetaTrader 4的路径]\MQL4\Include):
免费的开发库会提供一些函数来很方便地构建您的二元期权策略并与 Binary-Options-Strategy-Tester 通信。参见 Binary-Options-Strategy-Library 以获得此开发库的详细信息。

下载免费的 KVO.mq4 指标并把它(以及编译过的 KVO.ex4 文件)放到文件夹 \Indicators\Downloads ([您 MetaTrader 4的路径]\MQL4\Indicators\Downloads):
KVO 指标是用作示例的,它展示了对外部指标和它们的 ex4 文件的访问,章节为 "3.2.6 使用外部指标 (ex4 文件)"。参见 https://www.mql5.com/en/code/8677 以获得指标的详细信息。

现在您可以进一步学习章节 "3. 二元期权策略实例" 并自己构建实例的代码或者只在下面下载此实例的代码。

可选的下载 BinaryOptionsStrategyExample.mq4 并把它 (以及编译过的 BinaryOptionsStrategyExample.ex4 文件) 放到文件夹 \Indicators ([您 MetaTrader 4的路径]\MQL4\Indicators):
下载这个二元期权策略实例可以不用自己构建就能够运行,

如果要编译所需的 .ex4 文件,就要在 MetaTrader 语言编辑器中打开 .mq4 文件 (KVO.mq4 和 BinaryOptionsStrategyExample.mq4 - 而不是 Binary-Options-Strategy-Library.mqh),然后点击 "编译" 按钮,或者只要在这些文件保存到所述的文件夹中后重新启动 MetaTrader 4,MetaTrader 4就会自动为你完成。


3. 二元期权策略实例

以下的步骤将会引导您通过一个例子来学习如何构建保存在指标中的二元期权策略并与 Binary-Options-Strategy-Tester 进行通信。您可以自己构建它或者只要下载 BinaryOptionsStrategyExample.mq4 的代码。

请注意: 这个策略并不是一个可以获利的二元期权策略!它只是一个例子,关于如何在指标中构建策略并与 Binary-Options-Strategy-Tester 工具进行通信。当然您必须靠自己来构建一个能够获利的策略。但是您将看到,这个工具将会帮助您测试和提高您的二元期权策略。


3.1定义二元期权策略

首先,我们必须定义策略以及可以修改的数值 (输入参数)。MQL4 文档显示了所有可以通过 iCustom 界面访问的技术指标: https://docs.mql4.com/indicators

让我们假定,我们将想创建一个简单的移动平均交叉策略,如果"快速"移动平均和"慢速"移动平均有相互交叉,就在下一个烛形进行交易。文档已经说明了我们如何能够获得单个移动平均的值: https://docs.mql4.com/indicators/ima.

让我们再进一步,我们要从 "MA 平均周期数" (快速和慢速)中选择数值,并且还要选择 "所应用的价格类型"以及"平均的方法",其他的数值(例如交易品种,时段和偏移)要依赖于测试用例(例如测试器所运行的交易品种),应该自动设置。所以对于移动平均,我们基本上需要如下的变量:

int ma_period
int ma_method
int applied_price

因为我们需要两个移动平均以检查它们的交叉,在策略示例中我们需要以下的参数以及默认值:

int period_fast        =  5;
int period_slow        = 10;
int method_both        =  0; 
int applied_price_both =  0; 


3.2创建二元期权策略

我们需要构建一个指标来保存您的二元期权策略,然后把它拖曳到 Binary-Options-Strategy-Tester 所运行的图表上。

打开 MetaQuotes 语言编辑器 (在 MetaTrader 4 中点击 "工具" -> "MetaQuotes 语言编辑器" 或者只要按下 F4) 并点击 "新建":

语言编辑器的新建

就会出现 MQL 向导,选择 "自定义指标" 来创建一个空白的指标并点击 "下一步":

语言编辑器的自定义指标

输入名称,策略的版权信息以及链接,还有通过点击"增加"按钮来加上包含着类型和默认值(初始值)的输入参数,再点击 "下一步":

语言编辑器自定义指标值

在事件处理函数页面选择 "OnCalculate" 复选框,因为我们的策略需要在这个事件中在每个订单时刻进行检查,按下 "下一步":

语言编辑器自定义指标事件

在绘图属性页面选择"指标在独立窗口中"的复选框,因为我们需要一个独立的窗口来打印调试数值。按下 "完成":

语言编辑器自定义指标绘图属性

您指标的初始代码将会出现:

//+------------------------------------------------------------------+
//|                                 BinaryOptionsStrategyExample.mq4 |
//|                                       Copyright 2016, __martin__ |
//|                         https://www.mql5.com/en/users/__martin__ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, __martin__"
#property link      "https://www.mql5.com/en/users/__martin__"
#property version   "1.00"
#property strict
#property indicator_separate_window
//--- 输入参数
input int      period_fast=5;
input int      period_slow=10;
input int      method_both=0;
input int      applied_price_both=0;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                        |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 指标缓冲区映射
  
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 自定义指标迭代函数                        |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
  
//--- 返回 prev_calculated 的数值用于下一次调用
   return(rates_total);
  }
//+------------------------------------------------------------------+


3.2.1输入参数

初始的输入参数是由 MQL 向导创建的 (参见 3.2 创建二元期权策略),而我们会使用下面的步骤来增强它们,

为了避免在输入参数中"应用的价格"和"平均方法"中输入整数型数值,method_both 和 applied_price_both 的类型由整数型改为带有默认值的枚举类型。

ENUM_MA_METHOD: https://docs.mql4.com/constants/indicatorconstants/enum_ma_method
ENUM_APPLIED_PRICE: https://docs.mql4.com/constants/indicatorconstants/prices#enum_applied_price_enum

另外,输入参数的注释已经加上了,这样可以把注释显示为标签,而不是变量的名称:

...

//--- 输入参数
input int                period_fast        =  5;           //快速移动平均周期数
input int                period_slow        = 10;           //慢速移动平均周期数
input ENUM_MA_METHOD     method_both        = MODE_SMA;     //移动平均方法
input ENUM_APPLIED_PRICE applied_price_both = PRICE_CLOSE;  //移动平均应用的价格

...

通过这些修改,输入参数会提供下拉框,含有可以选择的数值,并且输入参数会有 "标签":

二元期权策略输入参数


3.2.2包含 Binary-Options-Strategy-Library

如果您已经下载和保存了开发库 (参见 2. 安装) 到 \Include 文件夹 ([您 MetaTrader 4 的路径]\MQL4\Include), 您就能像这样包含开发库:

//+------------------------------------------------------------------+
//|                                 BinaryOptionsStrategyExample.mq4 |
//|                                       Copyright 2016, __martin__ |
//|                         https://www.mql5.com/en/users/__martin__ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, __martin__"
#property link      "https://www.mql5.com/en/users/__martin__"
#property version   "1.00"
#property strict
#property indicator_separate_window

#include <BinaryOptionsStrategyLibrary.mqh>

//--- 输入参数

...
只有您把开发库放到您 MetaTrader 4的 \Include 文件夹下,才可以像例子描述的那样使用开发库。
不需要修改开发库的内容!

Binary-Options-Strategy-Library 将会使用两个新的参数来加强输入参数:

  • 每个烛形只能设置一个卖出或者一个买入交易;
  • 策略中只有新的烛形开始时才进行检查。

二元期权策略的输入参数进行了加强


3.2.3加上 CallStrategy()

在您策略指标的 OnCalculate() 中加上对 CallStrategy() 函数的调用,以便在每个新订单时刻调用策略。CallStrategy() 是由 Binary-Options-Strategy-Library 提供的,您已经像以上描述的那样包含它了:

...

//+------------------------------------------------------------------+
//| 自定义指标迭代函数                        |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

   CallStrategy(); //调用策略,函数位于 BinaryOptionsStrategyLibrary.mqh (上面已经包含了)

//--- 返回 prev_calculated 的值用于下一次调用
 
   return
(rates_total);
  }
//+------------------------------------------------------------------+

CallStrategy()- Binary-Options-Strategy-Library 中的这个函数将会调用在您指标中一个名为 CheckMyRules() 的函数,您可以在您的二元期权策略中设置您的条件,所以您必须在您的二元期权策略指标中实现 CheckMyRules() 函数。


3.2.4实现 CheckMyRules() 和辅助函数

在会通过 Binary-Options-Strategy-Library 调用的 CheckMyRules() 函数中,要实现策略的条件,而交易是通过开发库中的 PlaceTrade() 函数来进行的。移动平均的数值临时保存在变量中,以便在 if 条件中进行比较,而它们的数值是通过辅助函数 GetValuesForMA()来取得的:


...

//--- 输入参数
input int                period_fast        =  5;           //快速 MA 数值
input int                period_slow        = 10;           //慢速 MA 数值
input ENUM_MA_METHOD     method_both        = MODE_SMA;     //MA 方法
input ENUM_APPLIED_PRICE applied_price_both = PRICE_CLOSE;  //MA 应用的价格类型

...

//+------------------------------------------------------------------+
//| 在此设置您的交易规则 - 参见下面的例子.               |
//| StrategyTester 会调用这个函数来进行交易.           |
//| 不要:                               |
//| - 重命名函数                            |
//| - 增加函数的参数,比如 CheckMyRules(int a)            |
//| - 修改函数的返回值类型, 例如 int CheckMyRules()          |
//+------------------------------------------------------------------+
void CheckMyRules()
  {
  
   //保存 shift=0 (当前烛形) 的 MA 值 -> 当前烛形,
   //调用辅助函数 GetValueForMA() 以取得数值 - 参见下面的辅助函数
   double emaSlow_Current = GetValueForMA(period_slow, 0);
   double emaFast_Current = GetValueForMA(period_fast, 0);

   //保存 shift=1 (前一烛形) 的 MA 值-> 前一烛形,
   //调用辅助函数 GetValueForMA() 以取得数值 - 参见下面的辅助函数
   double emaSlow_Past = GetValueForMA(period_slow, 1);
   double emaFast_Past = GetValueForMA(period_fast, 1);

   if(emaFast_Past > emaSlow_Past
   && emaFast_Current < emaSlow_Past) //价差是否慢速MA和快速MA有交叉
     {
      PlaceTrade(OP_SELL); //为策略测试器设置卖出交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
   if(emaFast_Past < emaSlow_Past
   && emaFast_Current > emaSlow_Past) //检查是否慢速 MA 和快速 MA 有交叉
     {
      PlaceTrade(OP_BUY); //为策略测试器设置买入交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
  }

//+------------------------------------------------------------------+
//| 在此设置您的辅助函数,参加下面的例子               |
//+------------------------------------------------------------------+
//----
//+------------------------------------------------------------------+
//| 根据周期数,方法,应用的价格和偏移取得 MA 数值。        |
//| iMA() 的详细信息参见 https://docs.mql4.com/indicators/ima    |
//+------------------------------------------------------------------+
double GetValueForMA(int _period,int _shift)
  {
   return iMA(NULL,0,_period,0,method_both,applied_price_both,_shift);
  }
 


3.2.5打印出调试的数值

PrintDebugValue() 函数提供功能在测试器运行时打印出调试信息,在以下的例子中,MA 的数值和它们变量名称的标签会打印出来:


...

//--- 输入参数
input int                period_fast        =  5;           //快速 MA 数值
input int                period_slow        = 10;           //慢速 MA 数值
input ENUM_MA_METHOD     method_both        = MODE_SMA;     //MA 方法
input ENUM_APPLIED_PRICE applied_price_both = PRICE_CLOSE;  //MA 应用的价格类型

...

//+------------------------------------------------------------------+
//| 在此设置您的交易规则 - 参见下面的例子.               |
//| StrategyTester 会调用这个函数来进行交易.           |
//| 不要:                               |
//| - 重命名函数                            |
//| - 增加函数的参数,比如 CheckMyRules(int a)            |
//| - 修改函数的返回值类型, 例如 int CheckMyRules()          |
//+------------------------------------------------------------------+
void CheckMyRules()
  {
  
   //保存 shift=0 (当前烛形) 的 MA 值 -> 当前烛形,
   //调用辅助函数 GetValueForMA() 以获取数值 - 参见下面的辅助函数
   double emaSlow_Current = GetValueForMA(period_slow, 0);
   double emaFast_Current = GetValueForMA(period_fast, 0);

   //保存 shift=1 (前一烛形) 的 MA 值 -> 前一烛形,
   //调用辅助函数 GetValueForMA() 以取得数值 - 参见下面的辅助函数
   double emaSlow_Past = GetValueForMA(period_slow, 1);
   double emaFast_Past = GetValueForMA(period_fast, 1);

   PrintDebugValue("emaSlow_Current: ",(string)emaSlow_Current,0); //0行中的标签和数值
   PrintDebugValue("emaFast_Current: ",(string)emaFast_Current,1); //1行中的标签和数值
   PrintDebugValue("emaSlow_Past: ",(string)emaSlow_Past,2);       //2行中的标签和数值
   PrintDebugValue("emaFast_Past: ",(string)emaFast_Past,3);       //3行中的标签和数值

   if(emaFast_Past > emaSlow_Past
   && emaFast_Current < emaSlow_Past) //检查慢速 MA 和快速 MA 是否有交叉
     {
      PlaceTrade(OP_SELL); //为策略测试器设置卖出交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
   if(emaFast_Past < emaSlow_Past
   && emaFast_Current > emaSlow_Past) //检查慢速 MA 和快速 MA 是否有交叉
     {
      PlaceTrade(OP_BUY); //为策略测试器设置买入交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
  }

//+------------------------------------------------------------------+
//| 在此设置您的辅助函数,参见下面的例子               |
//+------------------------------------------------------------------+
//----

//+------------------------------------------------------------------+
//| 根据周期数,方法,应用价格和偏移取得 MA 的值。       |
//| 关于 iMA()的详细信息参见 https://docs.mql4.com/indicators/ima  |
//+------------------------------------------------------------------+
double GetValueForMA(int _period,int _shift)
  {
   return iMA(NULL,0,_period,0,method_both,applied_price_both,_shift);
  }
 


3.2.6使用外部指标(ex4 文件)

就算只有编译好的 ex4 文件,外部指标也可以在缓冲区中保存它的数值以在二元期权策略中访问。

假设我们想包含 KVO 指标 https://www.mql5.com/en/code/8677 的信号线,只有在信号线超过 0 时进行买入交易,而低于 0 时进行卖出交易。下载KVO.mq4指标并把编译好的 (ex4 文件) 放到文件夹 \Indicators\Downloads ([您 MetaTrader 4 的路径]\MQL4\Indicators\Downloads).

为了编译所需的 .ex4 文件,在 MetaQuotes 语言编辑器中打开 KVO.mq4 并点击 "编译" 按钮或者只要在文件保存在以上所述的文件夹后重新启动您的 MetaTrader 4,MetaTrader 4 就将自动帮您完成。

首先,我们必须识别出保存着相应要访问数值所对应的缓冲区,所以我们在 MetaTrader 4 中按下 "数据窗口" 按钮,显示使用的指标中所有可用的缓冲区,并把 KVO 指标拖到图表上,通过在图表上用十字交叉移动 (在图表上按下鼠标轮以激活十字交叉),指标缓冲区的数值就会显示在数据窗口中:

外部指标的缓冲区和数值

数据窗口的标签告诉我们,指标第二个缓冲区的值保存着信号线,如果指标缓冲区没有标签,我们可以通过比较缓冲区的数值和十字交叉所在图表部分显示的数值来找到正确的缓冲区。指标的缓冲区从0开始,所以我们有缓冲区1的值 = buffer 0, 缓冲区2的值 = buffer 1 并以此类推,从而访问 buffer 1 来得到信号值。

下一步我们必须知道我们想要访问的外部指标的所有输入参数,通过把指标拖到图表上,我们就可以看到所有的输入参数:

KVO 的输入参数

让我们进一步,我们想要通过(它的默认)数值访问指标:34, 55 和 13,我们使用一个辅助函数(基于 iCustom), 它提供功能可以根据缓冲区和偏移获得指标的数值,其中偏移0将是当前烛形的数值,偏移1是前一个烛形的值,偏移2是再前一个烛形的值,以此类推。另外我们把指标缓冲区的值临时保存到变量中,并增强策略的 if 条件:


...

//--- 输入参数
input int                period_fast        =  5;           //快速 MA 数值
input int                period_slow        = 10;           //慢速 MA 数值
input ENUM_MA_METHOD     method_both        = MODE_SMA;     //MA 方法
input ENUM_APPLIED_PRICE applied_price_both = PRICE_CLOSE;  //MA 应用的价格类型

...

//+------------------------------------------------------------------+
//| 在此设置您的交易规则 - 参见下面的例子.               |
//| StrategyTester 会调用这个函数来进行交易.           |
//| 不要:                               |
//| - 重命名函数                            |
//| - 增加函数的参数,比如 CheckMyRules(int a)            |
//| - 修改函数的返回值类型, 例如 int CheckMyRules()          |
//+------------------------------------------------------------------+
void CheckMyRules()
  {
  
   //保存 shift=0 (当前烛形) 的 MA 值 -> 当前烛形,
   //调用辅助函数 GetValueForMA() 以获取数值 - 参见下面的辅助函数
   double emaSlow_Current = GetValueForMA(period_slow, 0);
   double emaFast_Current = GetValueForMA(period_fast, 0);

   //保存 shift=1 (前一烛形) 的 MA 值 -> 前一烛形,
   //调用辅助函数 GetValueForMA() 以取得数值 - 参见下面的辅助函数
   double emaSlow_Past = GetValueForMA(period_slow, 1);
   double emaFast_Past = GetValueForMA(period_fast, 1);

   //保存 KVO 指标的当前烛形(偏移0)的信号值 (缓冲区 1)
   double kvoSignal = GetValuesFromIndicator__KVO__(1,0);

   PrintDebugValue("emaSlow_Current: ",(string)emaSlow_Current,0); //行0的标签和数值
   PrintDebugValue("emaFast_Current: ",(string)emaFast_Current,1); //行1的标签和数值
   PrintDebugValue("emaSlow_Past: ",(string)emaSlow_Past,2);       //行2的标签和数值
   PrintDebugValue("emaFast_Past: ",(string)emaFast_Past,3);       //行3的标签和数值

   if(emaFast_Past > emaSlow_Past
   && emaFast_Current < emaSlow_Past //检查是否慢速 MA 和快速 MA 有交叉
   && kvoSignal < 0) //检查 KVO 信号值是否小于 0
     {
      PlaceTrade(OP_SELL); //为策略测试期设置卖出交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
   if(emaFast_Past < emaSlow_Past
   && emaFast_Current > emaSlow_Past //检查慢速 MA 和快速 MA 是否有交叉
   && kvoSignal > 0) //检查 KVO 信号值是否大于 0
     {
      PlaceTrade(OP_BUY); //为策略测试器设置买入交易,函数位于 BinaryOptionsStrategyFunctions.mqh
     }
  
  }

//+------------------------------------------------------------------+
//| 在此设置您的辅助函数,参见下面的例子               |
//+------------------------------------------------------------------+
//----

//+------------------------------------------------------------------+
//| 根据周期数,方法,应用价格和偏移取得 MA 的数值.        |
//| iMA() 的详细信息参见 https://docs.mql4.com/indicators/ima   |
//+------------------------------------------------------------------+
double GetValueForMA(int _period,int _shift)
  {
   return iMA(NULL,0,_period,0,method_both,applied_price_both,_shift);
  }

//+------------------------------------------------------------------+
//| 如何从外部指标中取得数值的例子               |
//| 参见 https://docs.mql4.com/indicators/icustom          |
//| 参数:                                                      |
//| int _buffer - 指标缓冲区 (从 0 开始)                |
//| int _shift - 偏移值; 0 = 当前柱, 1 = 前一柱 |
//+------------------------------------------------------------------+
double GetValuesFromIndicator__KVO__(int _buffer, int _shift=0) //把 "__KVO__" 改成指标名称
  {

   return (
            iCustom (
                      NULL,                      //NULL 表示测试器选择的当前时段 - 不需改变
                      0,                         //0 表示测试器中选择的当前交易品种 - 不需要改变

                      //开始编辑
                      "\\Downloads\\KVO.ex4",    //指标文件(*.ex4 文件)的路径和名称
                      //开始指标的输入参数
                      34,
                      55,
                      13,
                      //指标输入参数结束
                      //结束编辑

                      _buffer,                   //缓冲区索引 (从0开始), _buffer 是通过函数参数传入的 - 不需要修改
                      _shift                     //偏移 (0 表示当前烛形), _shift 是通过函数参数传入的 - 不需要改变
                    )
          );

  }
 

也可以在我们的策略指标中加上输入参数以便使用 KVO 指标,并通过变量设置辅助参数中的数值,因为这篇教学只是一个例子,会尽可能简单,就不再这里显示这种方法了。


3.3完整代码

以下您可以看到 Binary-Options-Strategy-Example 通过以上所有步骤得到的完整代码,可以把它拖到 Binary-Options-Strategy-Tester 上测试并在图表上看到这样的结果了:

//+------------------------------------------------------------------+
//|                                 BinaryOptionsStrategyExample.mq4 |
//|                                       Copyright 2016, __martin__ |
//|                         https://www.mql5.com/en/users/__martin__ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, __martin__"
#property link      "https://www.mql5.com/en/users/__martin__"
#property version   "1.00"
#property strict
#property indicator_separate_window

#include <BinaryOptionsStrategyLibrary.mqh>

//+------------------------------------------------------------------+
//| 把您的输入参数放到这里 - 参见下面的例子             |
//+------------------------------------------------------------------+
//--- 输入参数
input int                period_fast        =  5;           //快速 MA 数值
input int                period_slow        = 10;           //慢速 MA 数值
input ENUM_MA_METHOD     method_both        = MODE_SMA;     //MA 方法
input ENUM_APPLIED_PRICE applied_price_both = PRICE_CLOSE;  //MA 应用的价格

//+------------------------------------------------------------------+
//| 自定义指标初始化函数                        |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 指标缓冲区的映射

//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| 自定义指标迭代函数                        |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

   CallStrategy(); //调用策略,函数位于 BinaryOptionsStrategyLibrary.mqh (上面已经包含)

//--- 返回 prev_calculated 的值用于下次调用
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| 把您的交易规则放到这里 - 参见下面的例子.               |
//| StrategyTester 会调用这个函数来设置交易.           |
//| 不要:                                                          |
//| - 重新命名函数                            |
//| - 增加函数参数,例如 CheckMyRules(int a)              |
//| - 修改函数返回值类型, 例如 int CheckMyRules()           |
//+------------------------------------------------------------------+
void CheckMyRules()
  {
  
   //保存 shift=0 (当前烛形) 的 MA 值 -> 当前烛形,
   //调用辅助函数 GetValueForMA() 来取得数值 - 参见下面的辅助函数
   double emaSlow_Current = GetValueForMA(period_slow, 0);
   double emaFast_Current = GetValueForMA(period_fast, 0);

   //保存 shift=1 (前一烛形) 的 MA 值 -> 前一烛形,
   //调用辅助函数 GetValueForMA() 来取得数值 - 参见下面的辅助函数
   double emaSlow_Past = GetValueForMA(period_slow, 1);
   double emaFast_Past = GetValueForMA(period_fast, 1);
  
   //保存KVO指标的当前烛形(偏移0)的信号值 (缓冲区1)
   double kvoSignal = GetValuesFromIndicator__KVO__(1,0);
  
   PrintDebugValue("emaSlow_Current: ",(string)emaSlow_Current,0); //行0的标签和数值
   PrintDebugValue("emaFast_Current: ",(string)emaFast_Current,1); //行1的标签和数值
   PrintDebugValue("emaSlow_Past: ",(string)emaSlow_Past,2);       //行2的标签和数值
   PrintDebugValue("emaFast_Past: ",(string)emaFast_Past,3);       //行3的标签和数值

   if(emaFast_Past > emaSlow_Past
   && emaFast_Current < emaSlow_Past    //检查慢速 MA 和快速 MA 是否有交叉
   && kvoSignal < 0)                    //检查 KVO 信号值是否小于 0  
     {
      PlaceTrade(OP_SELL);              //为策略测试器设置卖出交易, 函数位于 BinaryOptionsStrategyLibrary.mqh
     }
  
   if(emaFast_Past < emaSlow_Past
   && emaFast_Current > emaSlow_Past    //检查是否慢速 MA 和快速 MA 有交叉
   && kvoSignal > 0)                    //检查 KVO 信号值是否大于 0
     {
      PlaceTrade(OP_BUY);               //为策略测试器设置买入交易,函数位于 BinaryOptionsStrategyLibrary.mqh
     }
  
  }

//+------------------------------------------------------------------+
//| 在此设置您的辅助函数,参见下面的例子               |
//+------------------------------------------------------------------+
//----

//+------------------------------------------------------------------+
//| 根据周期数,方法,应用价格和偏移取得 MA 的值,        |
//| 关于 iMA() 的详细信息参见 https://docs.mql4.com/indicators/ima  |
//+------------------------------------------------------------------+
double GetValueForMA(int _period,int _shift)
  {
   return iMA(NULL,0,_period,0,method_both,applied_price_both,_shift);
  }

//+------------------------------------------------------------------+
//| 如何从外部指标中取得数值的例子,               |
//| 参见 https://docs.mql4.com/indicators/icustom          |
//| 参数:                               |
//| int _buffer - 指标缓冲区 (从 0 开始)               |
//| int _shift - 偏移值; 0 = 当前烛形, 1 = 前一烛形 |
//+------------------------------------------------------------------+
double GetValuesFromIndicator__KVO__(int _buffer, int _shift=0) //把 "__KVO__" 改成指标名称
  {
   return (
            iCustom (
                      NULL,                      //NULL 表示测试器中选择的当前时段 - 不需要改变
                      0,                         //0 表示测试器中选择的当前交易品种 - 不需要改变
                  
                      //开始编辑
                      "\\Downloads\\KVO.ex4",    //指标文件 (*.ex4 文件)的路径和名称               
                      //开始指标输入参数
                      34,
                      55,
                      13,
                      //指标输入参数结束
                      //结束编辑
                  
                      _buffer,                   //缓冲区索引 (从0开始), _buffer 是通过函数参数传入的 - 不需要改变
                      _shift                     //偏移 (0 表示当前烛形), _shift 是通过函数参数传入的 - 不需要改变
                    )
          );
  }
//+-----------------------------------------------------------------+


4. 运行一次回溯测试 (视频)

以下的视频显示了如何在MetaTrader 4的策略测试器中运行您二元期权策略的回溯测试。

  • 在 MetaTrader 4 中的策略测试器中启动 Binary-Options-Strategy-Tester 并设置输入参数
  • 把您的二元期权策略指标拖到图表上,设置输入参数并在"通用"页面选择 "允许外部EA交易输入"
  • 把您使用的指标使用它们的输入参数拖到图表上,在测试器运行的时候观察它们的数值(可选)
  • 在模板中保存所有的设置并使用所有的设置再次运行测试 - 使用策略测试器的暂停按钮(可选)
  • 在策略测试器图表上查看您二元期权策略的结果 


5. 运行一次前瞻测试

为了进行一次前瞻测试,只要把 Binary-Options-Strategy-Tester 工具和您的策略指标拖到您的模拟或者真实帐户的图表中,而不是在策略测试器中使用它:

  • 把 Binary-Options-Strategy-Tester 工具拖到模拟或者真实帐户图表中并设置输入参数
  • 把您的二元期权策略指标拖到图表上,设置输入参数并在"通用"页面选择 "允许外部EA交易输入"
  • 把您使用的指标使用它们的输入参数拖到图表上,当前瞻测试正在运行时观察它们的数值(可选)
  • 把全部设置保存到模板中并再次使用全部设置运行测试 (可选)
  • 在模拟或者真实帐户图表中查看您二元期权策略的结果


6. 常见问题

问题: 为什么您展示了一个不能获利的二元期权策略作为例子?
回答: 这只是一个关于如何在指标中构建策略并与市场中的 Binary-Options-Strategy-Tester 工具进行通信的例子,用于测试和提高您自己的策略。

问题: Binary-Options-Strategy-Tester 在一定数量的亏损后显示错误 "数组越界". 这是为什么呢?
回答: Binary-Options-Strategy-Tester 会在 x 此亏损之后提示错误并停止测试器来分析图表上的状况,如果您不想要这样,只要在设置选项中把它关闭。

问题: 当我把我自己的指标拖到图表中后,没有箭头出现在图表中,发生了什么呢?
回答: 当您把策略指标拖到图表中时,您必须在"通用"页面启用 "允许外部EA交易输入" (在这种情况下记录信息中将显示错误)。

问题: 在我把我自己的指标拖到图表上后,没有出现箭头,并且我已经启用了"允许外部EA交易输入",这是为什么呢?
回答: 策略必须调用 Binary-Options-Strategy-Tester 中的一个函数来进行虚拟交易,这与MQL4的版权概念有关,只有产品有完整授权时才能使用,所以您必须购买产品。

问题: 当我把我的指标拖到图表上时,我有完整授权,但是还是没有箭头出现,我在 MetaTrader 4的记录中得到了一些类似于 "无法调用(Cannot call) .." 或者 "无法载入(Cannot load) .." 的错误。我应该怎样做呢?
回答: 使用最新版本 (大于 v1.00) 的 BinaryOptionsStrategyLibrary.mqh。在您 BinaryOptionsStrategyLibrary.mqh 文件中检查版本标记并查看BinaryOptionsStrategyLibrary的 changelog v1.01

问题: 我在策略测试器的"结果","图表","报告"页面没有看到结果,我应该在哪里看结果呢?
回答: MetaTrader 4 的策略测试器无法处理二元期权,所以这些页面不能用,所以工具会计算所有的获利和亏损并在图表上打印结果。


7. 杂项

因为我需要功能来在 MetaTrader 4的策略测试器中自动测试二元期权策略,所以才构建了这个工具,我已经花了很多时间来介绍概念,实现 Binary-Options-Strategy-Tester 以及写文档,也许您还有更好的方法来做到这些,并且可以做些提高来适应您的需要,所以如果您有更好的想法请尽管联系我!