MQL5 中调用指标的方式有几种, 而且大都是利用 IndicatorCreate() 和 iCustom() 函数执行的。此外,这些函数只会返回指标句柄,再通过它来完成指标相关的进一步工作。那么,什么是句柄呢?如何处理 IndicatorCreate() 与 iCustom() 函数?您的“EA 交易”又会如何获取指标数据?所有这些问题,本文都有涉及。
创建一个源文件
想要开始我们的 EA 交易,先创建其源文件。大部分工作都会在 MetaEditor4 中通过从 File -> New 菜单调用 MQL5 Wizard 来完成。第一步,先选择“EA 交易”并按 Next (下一步)。
第二步,我们会得到输入名称、作者联系信息以及 EA 交易输入参数的机会。仅输入 EA 交易(必填字段)名称和作者。如有必要,您之后还可以在程序代码中直接添加输入参数。
待您点击 Finish (完成)后,该向导即会创建下述源代码:
//+------------------------------------------------------------------+ //| SampleMQL5.mq5 | //| Copyright KlimMalgin | //| | //+------------------------------------------------------------------+ #property copyright "KlimMalgin" #property link "" #property version "1.00" //+------------------------------------------------------------------+ //| EA初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(0); } //+------------------------------------------------------------------+ //| EA去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| EA tick函数 | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
我们先来简要查看一下某“EA 交易”的源文件结构,然后再行深入。
程序代码的最开始是一个头文件。这是一种包含文件名与作者的注释。标题之后是 EA 交易的“程序属性” (#property)。程序版本会自动显示,作者的姓名和到您页面的链接(如果您已输入)同样如此。描述完属性后,输入参数即被指定(我们尚未指定),然后即声明全局变量。
我想提醒一下,该属性应于主 mql5 文件中设置。内含文件的所有属性都会被忽略!
接下来是三个处理事件的函数:
MQL5 中使用指标的方法已有变化!在 MQL4 中,想要获取特定柱的指标值,必须将柱索引作为参数传递至 iCustom() 函数。而现在,我们不再去获取指标缓冲区的值,而是获取其句柄。 利用此句柄,我们就可以利用名为 CopyBuffer() 的专用函数,按给定的间隔获取指标值。那么,什么是句柄呢?
句柄是一种独特的标识符,为搭配特定指标使用而创建。它表示为一个整数,且存储为 int 型变量。MQL5 中获取指标句柄有 2 种方法:
两种方法都同等有效,使用哪一种则取决于您。我们来仔细研究一下这两种方法。
作为 IndicatorCreate() 函数成功执行的结果之一,我们会获得指标的句柄,而如果失败,此函数就会返回值为 -1 的无效句柄。如果被调用的指标拥有参数,则在调用该函数前,我们必须填妥一个 MqlParam 型参数的数组。此数组的每一个元素都代表一种 MqlParam 类型的结构,其中包含 4 个字段。此结构的第一个字段会指明剩余的三个字段中哪一个将用作指标的参数。
我们来编写可以创建“移动平均线”及 ParabolicSAR 指标句柄的代码吧。我们从声明全局变量开始 - 存储句柄、声明一个 MqlParam 型数组,为指标设置参数:
int MA1 = 0, // 声明变量存储快速MA指标的句柄 MA2 = 0, // 声明变量存储慢速MA指标的句柄 SAR = 0; // 声明变量存储SAR指标的句柄 MqlParam params[]; // 存储指标参数的数组
此代码必须在定义“EA 交易”属性之后立即置入。
现在,声明变量时,您就可以填写参数数组并调用 IndicatorCreate() 函数了。利用 OnInit() 函数效果更好,因为指标只会在程序开始时创建一次,而不是每一订单号都创建!
我们来填写参数数组并调用第一个指标:
int OnInit() { //--- // 设置params的大小和被调用指标参数数量一致 ArrayResize(params,4); // 设置快速MA的周期 params[0].type =TYPE_INT; params[0].integer_value=5; // 偏移 params[1].type =TYPE_INT; params[1].integer_value=0; // 计算方法:简单平均 params[2].type =TYPE_INT; params[2].integer_value=MODE_SMA; // 用于计算的价格类型:收盘价 params[3].type =TYPE_INT; params[3].integer_value=PRICE_CLOSE; MA1 = IndicatorCreate(Symbol(), // 用于计算指标的交易品种 0, // 时间框架。0 代表当前时间框架 IND_MA, // ENUM_INDICATOR枚举值定义的指标类型 4, // 数组传递的参数数量 params // 参数数组 ); //--- return(0); }
因为 params 数组是作为一个动态数组声明的(即,方括号内未设置数组大小),所以,在使用之前必须设置其大小。我们会利用 ArrayResize() 函数将大小定义为等同于我们将要于此数组中传递的参数的数量。针对“移动平均线”,有四项参数需要填写。
第一个参数为 MA 时段。该参数作为一个整数设置。因此,我们会将第一个参数 type 字段设置为 TYPE_INT 值,而我们的 IndicatorCreate() 函数也会由此“得知” - 该值应从 integer_value 字段获取。因为移动平均线所有参数都有 int 类型,所以可依此设置。
填好数组后,您就可以调用 IndicatorCreate() 函数了。其输出会被存储于 MA1 变量中。调用此函数应该没有什么难度,而且必须传送至其中的参数也只有 5 个:
就是这样!我们已经获取到了快 MA 句柄,然后还要获取慢 MA 与 SAR 指标句柄。OnInit() 函数会呈现如下结果:
int OnInit() { //--- // 设置params的大小和被调用指标参数数量一致 ArrayResize(params,4); //* 调用指标 * //*************************** // 设置快速MA的周期 params[0].type =TYPE_INT; params[0].integer_value=5; // 偏移 params[1].type =TYPE_INT; params[1].integer_value=0; // 计算方法:简单平均 params[2].type =TYPE_INT; params[2].integer_value=MODE_SMA; // 用于计算的价格类型:收盘价 params[3].type =TYPE_INT; params[3].integer_value=PRICE_CLOSE; MA1 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params); // 设置慢速MA的周期 params[0].type =TYPE_INT; params[0].integer_value=21; // 偏移 params[1].type =TYPE_INT; params[1].integer_value=0; // 计算方法:简单平均 params[2].type =TYPE_INT; params[2].integer_value=MODE_SMA; // 用于计算的价格类型:收盘价 params[3].type =TYPE_INT; params[3].integer_value=PRICE_CLOSE; MA2 = IndicatorCreate(Symbol(), 0, IND_MA, 4, params); // 改变数组的大小来存储SAR指标的参数 ArrayResize(params,2); // 步长 params[0].type =TYPE_DOUBLE; params[0].double_value = 0.02; // 最大值 params[1].type =TYPE_DOUBLE; params[1].double_value = 0.2; SAR = IndicatorCreate(Symbol(), 0, IND_SAR, 2, params); //--- return(0); }
想要获取慢 MA 句柄,您只需从 5 到 21 改变时段即可。而调用 ParabolicSAR 时,则将浮点数(双)作为其参数传送。填充数组时,您必须考虑到这一点,并分别将 TYPE_DOUBLE 置入类型字段、将参数本身置于 double_value 字段。而且,SAR 只要求填写两个参数,所以利用 ArrayResize() 将数组大小改为 2。
IndicatorCreate() 函数至此结束。以我的看法,如果使用指标的类库已经创建,则这种调用指标的方法在所有面向对象的方法中最为便利。这种库会方便大规模的项目的处理。至于说快速 EA 交易的编写与测试,最好还是采用下述方法。
能够获取指标句柄的第二种方法,就是调用一个技术指标函数。技术指标函数中的每一个函数,都旨在获取某个随 MetaTrader 5 交付的标准指标的句柄。根据其目标指标,它们都拥有特定的一组参数。比如说,调用了针对 CCI 指标的 iCCI() 函数,则其代码如下:
CCI = iCCI( Symbol(), // 交易品种 PERIOD_CURRENT, // 周期 20, // 平滑周期 PRICE_CLOSE // 价格类型或者句柄 );
其它指标的句柄,可以作为技术指标函数(而非价格类型)的最后参数提交。之后是以同样方式调用的指标,会利用价格数据、而非其它指标的数据进行计算。
iCustom() 函数引起了对于其它技术指标函数的追捧热潮,因为它可以调用包括自定义指标在内的任何指标。我们编写一段与上面类似的代码,只是这次使用 iCustom() 函数:
int OnInit() { //--- MA1 = iCustom(NULL,0,"Custom Moving Average", 5, // 周期 0, // 偏移 MODE_SMA, // 计算方式 PRICE_CLOSE // 在收盘价上进行计算 ); MA2 = iCustom(NULL,0,"Custom Moving Average", 21, // 周期 0, // 偏移 MODE_SMA, // 计算方式 PRICE_CLOSE // 在收盘价上进行计算 ); SAR = iCustom(NULL,0,"ParabolicSAR", 0.02, // 步长 0.2 // 最大值 ); //--- return(0); }
牢记:最好是在 EA 交易启动时获取句柄。那么,我们在 OnInit() 函数中实现其调用吧。
iCustom() 函数包含下述参数:
我们说过 - 如果您不具备工作的相关库,则此方法搭配指标使用更加便利。现在,接收到句柄时,您就可以开始使用指标值了!
MQL5 中使用指标数据非常好的一件事,就是我们现在有机会只按照我们需要的间隔获取数据了。设置此间隔的方式有多种:
想要阅读指标的日期,则会用到 CopyBuffer() 函数。获取数据采用哪种方法,取决于将向此函数传送哪些输入参数。
我们来编写将指标数据(之前已取得其句柄)复制到数组的代码:
void OnTick() { //--- // 存储指标变量值的动态数组 double _ma1[], _ma2[], _sar[]; // 将数组设置成和时间序列一样,例如数组索引为0的元素 // 存储最近一个柱形的值,索引为1的元素存储第二近柱形的值,等等。 ArraySetAsSeries(_ma1, true); ArraySetAsSeries(_ma2, true); ArraySetAsSeries(_sar, true); // 使用指标句柄将指标的缓存值,复制到数组中, //这些数组正是用于这个目的 if (CopyBuffer(MA1,0,0,20,_ma1) < 0){Print("CopyBufferMA1 error =",GetLastError());} if (CopyBuffer(MA2,0,0,20,_ma2) < 0){Print("CopyBufferMA2 error =",GetLastError());} if (CopyBuffer(SAR,0,0,20,_sar) < 0){Print("CopyBufferSAR error =",GetLastError());} //--- }
现在,我们的所有工作都将集中于 OnTick() 函数,因为每一个订单号指标上的数据都会变化且必须更新。
首先,我们为每个指标缓冲区声明一个动态数组。以我看来,如果数组索引与时间序列相同 - 即,数组元素 0 中会是最后一柱的数据,而元素 1 中则是倒数第二柱的数据,以此类推 - 则使用指标数据要方便得多。索引方法利用 ArraySetAsSeries() 函数设置。
索引方法设置完毕后,我们就可以开始填充数组了。为计算每个指标,我们会调用含有下述参数组的 CopyBuffer() 函数:
如调用成功,则 CopyBuffer() 函数会返回被复制元素的数量;如果失败则会返回 -1。如果失败,必须跟踪查找错误,所以,如果返回值小于 0 - 我们就要利用 Print() 函数在 EA 交易日志中记录该错误。
总结
到目前为止,我们对于指标的使用还远未结束。本文只是讲到了获取其数据的一些基本方法。而且为了方便,最好是采用面向对象的方法 - 而这一方法已于 MQL5 中得到了一种高水平的呈现!
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...