在MetaTrader交易系统中创建自定义指标有一些特性.
对于考虑做成自定义指标的程序, 一定要作如下二选一的定义:
#property indicator_chart_window // 指标在主图表窗口绘制
或者
#property indicator_separate_window // 指标在独立窗口中绘制
为了设置独立指标窗口的尺度, 要使用如下定义:
#property indicator_minimum Min_Value #property indicator_maximum Max_Value
其中 "Min_Value" 和 "Max_Value" 是对应的数值. 例如, 对于自定义指标 RSI 来说, 这些数值必须为0和100.
为绘制指标所需的指标数组的数量必须如下定义:
#property indicator_buffers N
其中N可以在从1到8之间选择.
指标线的颜色可以由以下定义设置:
#property indicator_color1 Silver #property indicator_color2 Red ... #property indicator_colorN <SomeColor>
其中 N 就是使用 "#define indicator_buffer" 定义的指标数组的数量.
还有一些用于控制指标计算和显示的函数. 这里使用 Ishimoku Kinko Hyo 自定义指标做说明:
//+------------------------------------------------------------------+ //| Ichimoku.mq4 | //| Copyright © 2004, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2004, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" #property indicator_chart_window #property indicator_buffers 7 #property indicator_color1 Red #property indicator_color2 Blue #property indicator_color3 SandyBrown #property indicator_color4 Thistle #property indicator_color5 Lime #property indicator_color6 SandyBrown #property indicator_color7 Thistle //---- 输入参数 extern int Tenkan=9; extern int Kijun=26; extern int Senkou=52; //---- 指标缓冲区 double Tenkan_Buffer[]; double Kijun_Buffer[]; double SpanA_Buffer[]; double SpanB_Buffer[]; double Chinkou_Buffer[]; double SpanA2_Buffer[]; double SpanB2_Buffer[]; //---- span_a 开始绘制 int a_begin; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,Tenkan_Buffer); SetIndexDrawBegin(0,Tenkan-1); SetIndexLabel(0,"Tenkan Sen"); //---- SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,Kijun_Buffer); SetIndexDrawBegin(1,Kijun-1); SetIndexLabel(1,"Kijun Sen"); //---- a_begin=Kijun; if(a_begin<Tenkan) a_begin=Tenkan; SetIndexStyle(2,DRAW_HISTOGRAM,STYLE_DOT); SetIndexBuffer(2,SpanA_Buffer); SetIndexDrawBegin(2,Kijun+a_begin-1); SetIndexShift(2,Kijun); SetIndexLabel(2,NULL); SetIndexStyle(5,DRAW_LINE,STYLE_DOT); SetIndexBuffer(5,SpanA2_Buffer); SetIndexDrawBegin(5,Kijun+a_begin-1); SetIndexShift(5,Kijun); SetIndexLabel(5,"Senkou Span A"); //---- SetIndexStyle(3,DRAW_HISTOGRAM,STYLE_DOT); SetIndexBuffer(3,SpanB_Buffer); SetIndexDrawBegin(3,Kijun+Senkou-1); SetIndexShift(3,Kijun); SetIndexLabel(3,NULL); SetIndexStyle(6,DRAW_LINE,STYLE_DOT); SetIndexBuffer(6,SpanB2_Buffer); SetIndexDrawBegin(6,Kijun+Senkou-1); SetIndexShift(6,Kijun); SetIndexLabel(6,"Senkou Span B"); //---- SetIndexStyle(4,DRAW_LINE); SetIndexBuffer(4,Chinkou_Buffer); SetIndexShift(4,-Kijun); SetIndexLabel(4,"Chinkou Span"); //---- return(0); } //+------------------------------------------------------------------+ //| Ichimoku Kinko Hyo | //+------------------------------------------------------------------+ int start() { int i,k; int counted_bars=IndicatorCounted(); double high,low,price; //---- if(Bars<=Tenkan || Bars<=Kijun || Bars<=Senkou) return(0); //---- 用0初始化 if(counted_bars<1) { for(i=1;i<=Tenkan;i++) Tenkan_Buffer[Bars-i]=0; for(i=1;i<=Kijun;i++) Kijun_Buffer[Bars-i]=0; for(i=1;i<=a_begin;i++) { SpanA_Buffer[Bars-i]=0; SpanA2_Buffer[Bars-i]=0; } for(i=1;i<=Senkou;i++) { SpanB_Buffer[Bars-i]=0; SpanB2_Buffer[Bars-i]=0; } } //---- Tenkan Sen i=Bars-Tenkan; if(counted_bars>Tenkan) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Tenkan; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } Tenkan_Buffer[i]=(high+low)/2; i--; } //---- Kijun Sen i=Bars-Kijun; if(counted_bars>Kijun) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Kijun; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } Kijun_Buffer[i]=(high+low)/2; i--; } //---- Senkou Span A i=Bars-a_begin+1; if(counted_bars>a_begin-1) i=Bars-counted_bars-1; while(i>=0) { price=(Kijun_Buffer[i]+Tenkan_Buffer[i])/2; SpanA_Buffer[i]=price; SpanA2_Buffer[i]=price; i--; } //---- Senkou Span B i=Bars-Senkou; if(counted_bars>Senkou) i=Bars-counted_bars-1; while(i>=0) { high=High[i]; low=Low[i]; k=i-1+Senkou; while(k>=i) { price=High[k]; if(high<price) high=price; price=Low[k]; if(low>price) low=price; k--; } price=(high+low)/2; SpanB_Buffer[i]=price; SpanB2_Buffer[i]=price; i--; } //---- Chinkou Span i=Bars-1; if(counted_bars>1) i=Bars-counted_bars-1; while(i>=0) { Chinkou_Buffer[i]=Close[i]; i--; } //---- return(0); } //+------------------------------------------------------------------+
"SetIndexStyle" 函数控制一个指标数组的绘制参数. DRAW_LINE 绘制模式规定, 在对应指标数组中定义的数值之间使用直线来绘制. DRAW_HISTOGRAM 绘制模式用于主窗口指标的绘制, 它也有其特性. 柱状图绘制在两个索引数组中的数值之间: 一个偶数数组(这里是 SpanA_Buffer) 和一个奇数数组(这里是 SpanB_Buffer). 另外, 索引更高的数组用于颜色数值.
"SetIndexDrawBegin" 函数制定了从指标数组的那个元素开始绘制.
"SetIndexBuffer" 函数允许把任何类型为"double"的一维数组声明为索引数组. 然后, 系统将管理索引数组. 这就是为什么其中有些数组不需要被特别指定.
//---- 指标缓冲区 double Tenkan_Buffer[]; double Kijun_Buffer[]; double SpanA_Buffer[]; double SpanB_Buffer[]; double Chinkou_Buffer[]; double SpanA2_Buffer[]; double SpanB2_Buffer[];
ArrayResize 函数不能应用于指标数组, 因为它不起作用. 同样, 在指标数组中使用ArrayInitialize函数也是不起作用的, 当指标缓冲区尚未分配时, 就更加不需要"初始化"功能. 指标数组是在内存的分配和重新分配时自动初始化的. 是使用 EMPTY_VALUE, 或者 SetIndexEmptyValue 函数指定的数值作为初始值的. "空"数值不会显示.
"SetIndexLabel" 函数用于设置显示于工具提示和数据窗口中数值的名称, (默认为"ValueN", 其中N就是数组的索引编号). 如果传入NULL而不是名称, 对应的数值就不会在工具提示和数据窗口中显示. 在以上的例子中, 云使用了柱形图做阴影, 而以线为框. 其中, 对应的"线"和"柱形图"数组是相同的, 也可以只显示它们中的一个.
"IndicatorCounted" 函数可以使指标计算更加经济. 这个函数返回指标载入时到某一时刻的柱的数量, 也就是说, 已经计算过的柱数(潜在要求是之前载入没有错误), 它们不需要重新计算了. 自定义指标重新初始化或者历史数据有了大的更新时, 此数量会被自动重设为0.
让我们多讨论一个实例. 这个自定义指标称为加速/减速震荡指标:
//+------------------------------------------------------------------+ //| Accelerator.mq4 | //| Copyright © 2005, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2005, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" //---- 指标设置 #property indicator_separate_window #property indicator_buffers 3 #property indicator_color1 Black #property indicator_color2 Green #property indicator_color3 Red //---- 指标缓冲区 double ExtBuffer0[]; double ExtBuffer1[]; double ExtBuffer2[]; double ExtBuffer3[]; double ExtBuffer4[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- 使用额外两个缓冲区进行计数. IndicatorBuffers(5); //---- 绘图设置 SetIndexStyle(0,DRAW_NONE); SetIndexStyle(1,DRAW_HISTOGRAM); SetIndexStyle(2,DRAW_HISTOGRAM); IndicatorDigits(Digits+2); SetIndexDrawBegin(0,38); SetIndexDrawBegin(1,38); SetIndexDrawBegin(2,38); //---- 4个指标缓冲区的镜像 SetIndexBuffer(0,ExtBuffer0); SetIndexBuffer(1,ExtBuffer1); SetIndexBuffer(2,ExtBuffer2); SetIndexBuffer(3,ExtBuffer3); SetIndexBuffer(4,ExtBuffer4); //---- 数据窗口和指标子窗口的标签名称 IndicatorShortName("AC"); SetIndexLabel(1,NULL); SetIndexLabel(2,NULL); //---- 初始化完成 return(0); } //+------------------------------------------------------------------+ //| 加速/减速振荡指标 | //+------------------------------------------------------------------+ int start() { int limit; int counted_bars=IndicatorCounted(); double prev,current; //---- 最后计数的柱将要重新计数 if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; //---- macd 计数的额外一个缓冲区 for(int i=0; i<limit; i++) ExtBuffer3[i]=iMA(NULL,0,5,0,MODE_SMA,PRICE_MEDIAN,i)- iMA(NULL,0,34,0,MODE_SMA,PRICE_MEDIAN,i); //---- 信号线计数的第二个额外缓冲区 for(i=0; i<limit; i++) ExtBuffer4[i]=iMAOnArray(ExtBuffer3,Bars,5,0,MODE_SMA,i); //---- 在两个缓冲区之间分发数据 bool up=true; for(i=limit-1; i>=0; i--) { current=ExtBuffer3[i]-ExtBuffer4[i]; prev=ExtBuffer3[i+1]-ExtBuffer4[i+1]; if(current>prev) up=true; if(current<prev) up=false; if(!up) { ExtBuffer2[i]=current; ExtBuffer1[i]=0.0; } else { ExtBuffer1[i]=current; ExtBuffer2[i]=0.0; } ExtBuffer0[i]=current; } //---- 完成 return(0); } //+------------------------------------------------------------------+
"IndicatorBuffers" 函数指定了在指标计算过程中用到的缓冲区数量. 通常情况下, 如果绘制指标需要更多索引数组时会调用它. 这样系统会管理额外的数组.
"SetIndexDigits" 函数管理输出信息的精确度. 在本例中, 当计算两个移动平均之差以及信号线时, 标准的小数点后四位精确度是不够的.
"SetIndexDrawBegin" 函数指定了指标数组中元素开始计算的位置. 在我们的例子中, 信号线是按另外一条简单移动平均的简单移动平均来计算的. 这就是为何指标的前38个值不会绘制而作为空值存在.
"IndicatorShortName" 函数设置指标的短名称, 它将显示在指标窗口的左上角以及"数据窗口"中. 如果没有设置段名称, 就会使用自定义指标的名字. 在本例中, 不需要SetIndexLabel函数, 因为只有一个输出值. 所以, 指标的名称对于一个值就足够了.
"SetIndexStyle" 函数管理指标数组的绘制参数. DRAW_NONE 绘制模式表示该线无需绘制. 重要的是, 指标的柱形图必须使用两种不同颜色. 来自ExtBuffer0 的数据被分配在另外两个数组中, ExtBuffer1 和 ExtBuffer2. 为了不在工具提示和数据窗口中显示数据, 要在SetIndexLabel函数的参数中使用NULL. 在独立窗口的指标中使用 DRAW_HISTOGRAM 模式可以在0值和对应的数组(和以上描述的在主窗口中画柱形图做比较)绘制柱形图.
用于自定义指标计算的输入参数和函数必须被定义为"extern", 可以是任何类型.
如果自定义指标没有设置任何输入参数, 它将以最简单的形式被调用.
double current_AC = iCustom( NULL, 0, "Accelerator", 0, 0 );
前面两个参数分别传入"NULL"和"0"表示会使用当前的图表. 自定义指标的名称使用的是对应文件的名字(不包括mq4的扩展名). 如果倒数第二个参数设为0, 它表示我们只对第一个指标数组有兴趣. 最后一个参数是0表示我们只关心所需指标数组的最后一个元素(也就是说, 最新的, 当前的数值).
参数就是按照上面描述的方式通过函数传给自定义指标计算的. 例如, 自定义指标名称为 "Ichimoku", 它的参数有(9,26,52), 将被如下调用:
iCustom( NULL, 0, "Ichimoku", 9, 26, 52, 0, shift );
严格来说, 不一定要把参数传给传给自定义指标函数. 如果程序中没有外部变量, 也就不用传参数. 或者, 如果需要, 可以使用参数描述中的初始值. 例如, 不使用参数调用相同的自定义指标可以如下调用:
iCustom( NULL, 0, "Ichimoku", 0, shift );
这表示变量将使用初始值, 即 "Tenkan", "Kijun", "Senkou", 以及, 9, 26, 和 52. 然而, 如果是在EA交易中调用具有多组参数的自定义指标, 并不推荐使用默认设置.
请必须注意, 使用过多的自定义指标或者指标编写得不正确, 都有可能使客户终端的运行明显变慢!
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程