在MQL中,指标(Indicator)用一个文件组织
对指标的引用归根结底是MQL跨文件组织代码的形式。
对于指标的引用方式与#include
这样的文本包含的思路不同,文本包含会造成很多问题,如名字冲突。MQL利用一种类似模块的方式来引用指标。
获取指标引用需要一个叫做iCustom的函数。
我想,这里的“i”应该是指“指标”(Indicator);
iCustom函数具有一个自由的参数列表,但有三个必须指定的参数:品种、周期、指标名。
一个典型的调用是这样的:
int ExtHandle = iCustom(_Symbol, _Period, "AAA.mq5");
它会去引用 Indicators
目录下的 AAA.mq5
,即访问文件 Indicators/AAA.mq5
。
之前提到的方法并没有指定该指标的参数,即,一切参数使用默认值。但是如果要自定义参数又该如何呢?
iCustom具有一个自由的参数列表,在三个必须指定的参数后面有一个缺省的参数列表,可以指定引用指标的参数,顺序按照指标内 input
的顺序给出。
一旦使用自定义参数,就一定要给出所有的参数,不能出现“部分指派”的形式。
我们假定AAA指标需要两个参数,且它们是这样被定义的:
input int a = 1;
input double b = 2.0;
那么在其他的文件里,要这样引用:
int h1 = iCustom(_Symbol, _Peroid, "AAA.mq5"); // 使用默认值: a = 1, b = 2.0;
int h2 = iCustom(_Symbol, _Peroid, "AAA.mq5", 2, 1.0); // 指定 a = 2, b = 1.0;
再给几个反面教材:
int h1 = iCustom(_Symbol, _Peroid, "AAA.mq5", 2); //Error: 不能仅指定 a = 2;
int h2 = iCustom(_Symbol, _Peroid, "AAA.mq5", 1.1, 2.0); // Error: a不是浮点数。
在获得指标的引用(句柄)了之后,就可以从中拷贝数据了。
此时需要使用CopyBuffer
函数。
一个指标可以有复数个缓冲区(Buffer),所谓缓冲区也就是变长数组的概念(在MQL中是用链表实现的)。
每一个缓冲区通常代表一条线或者是一个柱子。
例如双线MACD就使用了至少三个缓冲区(DIFF、DMA、MACD)。
布林线也使用了三个缓冲区(Upper,Middle,Lower)。
所以——一个指标引用中可以取出多个数组的数据,在CopyBuffer的时候需要指定从几号缓冲区中取数据。
那么,怎么知道要取得数据在几号缓冲区呢?如果是别人写的指标,直接看指标自带的文档,上面通常会有;如果是自己写的……
是用SetIndexBuffer
函数绑定缓冲区的……还记得你做过类似的事情吗……
SetIndexBuffer(0, DIFFBuffer, INDICATOR_DATA);
SetIndexBuffer(1, DMABuffer, INDICATOR_DATA);
SetIndexBuffer(2, MACDBuffer, INDICATOR_DATA);
没错,设定索引,这本质就是模块导出的一种实现。
类比 CommonJS 的 module.exports 规范。
注意:Buffer都是double数组。
在设定索引之后,外界就可以对这个数据进行引用了。
引用的例子,比如:
int MACDHandle = iCustom(_Symbol, _Period, "MACD.mq5"); // 获取指标引用
double diff[2]; // 定义数组。
CopyBuffer(MACDHandle, 0, 0, 2, diff); // 将MACD指标中的0号缓冲(DIFF)从0号位置(最新的数据),拷贝2个数据到diff数组
注意,缓冲区是时间逆序的,即最新的数据会在0号位置。而数组中的数据却是时间正序的。
若DIFF指标的最新的数据是a,次新的数据是b;则拷贝到数组diff中,有diff[0] = b, diff[1] = a
;
MQL中有一堆经过封装的,以小写i
开头的函数,具有跟iCustom
类似的功能,例如iMA, iMACD, iBands
等,它们的参数就不一定是之前提过的“3+N”模式了,具体的要查文档。但之后如何从中取出数据却是如出一辙。