熟悉市场情绪的人都知道 MACD 指标(其全称为平滑异同移动平均线)- 自计算机分析方法面世以来即已被交易人员用于分析价格变动的强大工具。
长久以来,我都在研究这个在图表中占得一席之地的 MACD 指标。我接触过这一指标的多种不同类型,它们具有不同的选项和不同的计算算法,因此我决定在指标中结合我所知道的所有类型。
我们将需要以下参数用于计算:
要绘制该指标,我们还需要以下内容:
转到“MQL5 向导”菜单:
图 1. 使用“MQL5 向导”创建指标
图 2. 在“MQL5 向导”中定义常用指标参数
图 3. 在“MQL5 向导”中定义指标的绘图属性
我们已获得指标的初始模板。首先,我们需要计算指标的 MACD 线。
我们不会深入探究计算该线的精确公式 - 我们将使用 iMACD 函数:
int iMACD ( string symbol, // 交易品种名称 ENUM_TIMEFRAMES period, // 时间周期 int fast_ema_period, // 快速EMA周期 int slow_ema_period, // 慢速EMA周期 int signal_period, // 信号线的平滑周期 ENUM_APPLIED_PRICE applied_price // 价格类型或者是一个句柄 )
该函数返回合适指标副本的句柄。使用此句柄,就有可能获得该指标计算得到的数据。可使用函数 CopyBuffer() 复制来自指标缓冲区的数据(技术指标在其自身的内部缓冲区中包含计算得出的数据,取决于指标,最多可以有 5 个缓冲区)。
接下来,我们使用函数 iMACD 为 MACD 数据生成请求:
int MACDhadling = iMACD(NULL,0,Fast,Slow,Signal,PRICE_CLOSE);
它将返回指标副本的句柄。
我们通过函数 CopyBuffer 将数据复制到必要缓冲区中:
int CopyBuffer( int indicator_handle, // 指标句柄 int buffer_num, // 指标的缓存数量 int start_pos, // 开始位置 int count, // 要复制的数据数量 double buffer[] // 用于复制数据的目标数组 );
现在,让我们请求指标的 MACD 线:
CopyBuffer(MACDhadling,0,0,NewData,MACDlineBuffer);
我们获得指标的信号线:
CopyBuffer(MACDhadling,1,0,NewData,SignallineBuffer);
我们将其聚集在一起,看看我们获得了什么:
int MACDhadling=iMACD(NULL,0,Fast,Slow,Signal,PRICE_CLOSE); CopyBuffer(MACDhadling,0,0,rates_total,MACDlineBuffer); CopyBuffer(MACDhadling,1,0,rates_total,SignallineBuffer);
现在我们有了计算的 MACD 和信号线。
我们继续。
由于来自
MACDlineBuffer
缓冲区的数据以及
SignallineBuffer
缓冲区是通过复制获得的,它们的索引从图表末尾处开始。
以往,对价格数组数据的访问是从数据的末尾执行。实际上,新数据总是写入数组的末尾,而当前(未完成)柱的索引始终等于零。在时序数组中,索引为 0 表示当前柱的数据,当前柱对应于该时间表的未完成时间间隔。
为了在所有缓冲区中使用相同的索引方向,我们应将其他缓冲区定义为时序型。
ArraySetAsSeries(HistogramBuffer,false); ArraySetAsSeries(HistogramColors,false);
我们需要获得直方图的数据,这通过从 MACD 线减去信号线计算得出:
for(int i=0;i<rates_total;i++) { HistogramBuffer[i]=MACDlineBuffer[i]-SignallineBuffer[i]; }
我们将所有内容结合在一起:
ArraySetAsSeries(HistogramBuffer,false); ArraySetAsSeries(HistogramColors,false); int MACDhadling=iMACD(NULL,0,Fast,Slow,Signal,PRICE_CLOSE); CopyBuffer(MACDhadling,0,0,rates_total,MACDlineBuffer); CopyBuffer(MACDhadling,1,0,rates_total,SignallineBuffer); for(int i=0;i<rates_total;i++) { HistogramBuffer[i]=MACDlineBuffer[i]-SignallineBuffer[i]; HistogramColors[i]=1; }
我们有该指标的 5 种变型。
首先,我们实施第三项和第四项。
3. 仅绘制 oSMA 直方图;
4. 仅绘制 MACD 线。
我们来创建适当的按钮。
项目 4:
ObjectCreate(0,"ShowMACD",OBJ_BUTTON,ChartWindowFind(),100,100); //创建按钮 ObjectSetInteger(0,"ShowMACD",OBJPROP_XDISTANCE,75); //分配坐标 ObjectSetInteger(0,"ShowMACD",OBJPROP_YDISTANCE,5); ObjectSetInteger(0,"ShowMACD",OBJPROP_CORNER,CORNER_RIGHT_UPPER); // 以及一个标定点 ObjectSetString(0,"ShowMACD",OBJPROP_TEXT,"ShowMACD"); // 按钮标签 ObjectSetInteger(0,"ShowMACD",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"ShowMACD",OBJPROP_XSIZE,70); //按钮大小 ObjectSetInteger(0,"ShowMACD",OBJPROP_YSIZE,20); ObjectSetInteger(0,"ShowMACD",OBJPROP_SELECTABLE,false); // 使它可选
对于意外按钮删除或其在下一订单号的平移,按钮将返回。
项目 3:
ObjectCreate(0,"ShowOsMA",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"ShowOsMA",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"ShowOsMA",OBJPROP_YDISTANCE,30); ObjectSetInteger(0,"ShowOsMA",OBJPROP_CORNER,CORNER_RIGHT_UPPER); ObjectSetString (0,"ShowOsMA",OBJPROP_TEXT,"Show OsMA"); ObjectSetInteger(0,"ShowOsMA",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"ShowOsMA",OBJPROP_XSIZE,70); ObjectSetInteger(0,"ShowOsMA",OBJPROP_YSIZE,20); ObjectSetInteger(0,"ShowOsMA",OBJPROP_SELECTABLE,false);
让我们针对情形 4 的按下和未按下按钮创建实施。
这要求查看缓冲区索引。
SetIndexBuffer(0,MACDlineBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignallineBuffer,INDICATOR_DATA); SetIndexBuffer(2,HistogramBuffer,INDICATOR_DATA); SetIndexBuffer(3,HistogramColors,INDICATOR_COLOR_INDEX); if(ObjectGetInteger(0,"ShowMACD",OBJPROP_STATE)!=1) { PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_NONE); // 索引为0的缓存不绘制 PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_NONE); // 索引为1的缓存也不绘制 } else { PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); // 索引为0的缓存绘制成线 PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_LINE); // 索引为1的缓存也绘制成线 }
按下按钮,绘制 MACD 线;未按下则不会绘制 MACD 线。
让我们针对情形 3 的按下和未按下按钮创建实施。
if(ObjectGetInteger(0,"ShowOsMA",OBJPROP_STATE)!=1) { //索引为2的缓存不绘制 PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_NONE); } else { //索引为2的缓存绘制成一个彩色直方图 PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_COLOR_HISTOGRAM); }
我们创建两个按钮:"2color" 和 "Impulse",并将它们放置在图表的右下角处。
ObjectCreate(0,"2color",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"2color",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"2color",OBJPROP_YDISTANCE,50); ObjectSetInteger(0,"2color",OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(0,"2color",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"2color",OBJPROP_XSIZE,70); ObjectSetInteger(0,"2color",OBJPROP_YSIZE,20); ObjectSetInteger(0,"2color",OBJPROP_SELECTABLE,false); ObjectSetString (0,"2color",OBJPROP_TEXT,"MultiColor"); ObjectCreate(0,"Impulse",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"Impulse",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"Impulse",OBJPROP_YDISTANCE,25); ObjectSetInteger(0,"Impulse",OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(0,"Impulse",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"Impulse",OBJPROP_XSIZE,70); ObjectSetInteger(0,"Impulse",OBJPROP_YSIZE,20); ObjectSetInteger(0,"Impulse",OBJPROP_SELECTABLE,false);
对于基于 Elder 系统的冲量检查,我们需要添加一个新的数组用于放置 EldersMA 值。
为此,我们需要将缓冲区的总数量加一。+
#property indicator_buffers 4
应更改为:
#property indicator_buffers 5
并声明一个新缓冲区。
double EldersiEMA[];
我们将它定义为缓冲区以用于内部计算:
SetIndexBuffer(4,EldersiEMA,INDICATOR_CALCULATIONS);
现在,我们将指数移动平均线值复制到缓冲区:
// you can do all in single line CopyBuffer(iMA(NULL,0,EldersEMA,0,MODE_EMA,PRICE_CLOSE),0,0,rates_total,EldersiEMA);
由于缓冲区通过复制功能获得,其索引和图表其他缓冲区的索引一样,都是从图表末尾开始。
现在,让我们写下 2 色 OsMA 的条件:
if (ObjectGetInteger(0,"2color",OBJPROP_STATE)) { for(int i=1;i<rates_total;i++) { // 如果直方图上升,则将颜色设置为0 if(HistogramBuffer[i] > HistogramBuffer[i-1]) HistogramColors[i]=0; // 如果直方图下降,则将颜色设置为1 if(HistogramBuffer[i] < HistogramBuffer[i-1]) HistogramColors[i]=1; } } else { ObjectSetString(0,"2color",OBJPROP_TEXT,"MultiColor"); // 这里是多种颜色OSMA的条件 }
颜色索引在线中指定:
#property indicator_label3 "Histogram" #property indicator_type3 DRAW_COLOR_HISTOGRAM #property indicator_color3 DeepSkyBlue,Red,Green
第一种颜色的索引等于 0,第二种颜色的索引等于 1,依此类推。
现在,让我们写下冲量系统变型的条件:
if (ObjectGetInteger(0,"Impulse",OBJPROP_STATE)) // // "Impulse" 按钮被点击 { ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Impulse"); // 使用MACD线来判断趋势 for(int i=1;i<rates_total;i++) { // 直方图上升并且MACD线上升 if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (MACDlineBuffer[i]>MACDlineBuffer[i-1])) HistogramColors[i]=0; else { // 直方图下降并且MACD线下降 if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (MACDlineBuffer[i]<MACDlineBuffer[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2; // 如果不符合任何条件 } } } else { ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Elder's"); // 使用EMA线检查是否出现趋势 for(int i=1;i<rates_total;i++) { // 直方图上升并且EMA线上升 if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (EldersiEMA[i]>EldersiEMA[i-1])) HistogramColors[i]=0; else { // 直方图下降并且EMA线下降 if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (EldersiEMA[i]<EldersiEMA[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2;// 如果不符合任何条件 } } }
现在,我们将冲量系统条件添加至 OsMA 绘制条件:
if (ObjectGetInteger(0,"2color",OBJPROP_STATE)) { for(int i=1;i<rates_total;i++) { if(HistogramBuffer[i] > HistogramBuffer[i-1]) HistogramColors[i]=0; if(HistogramBuffer[i] < HistogramBuffer[i-1]) HistogramColors[i]=1; } } else { ObjectSetString(0,"2color",OBJPROP_TEXT,"MultiColor"); if(ObjectGetInteger(0,"Impulse",OBJPROP_STATE)) { ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Impulse"); for(int i=1;i<rates_total;i++) { if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (MACDlineBuffer[i]>MACDlineBuffer[i-1])) HistogramColors[i]=0; else { if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (MACDlineBuffer[i]<MACDlineBuffer[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2; } } } else { ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Elder's"); for(int i=1;i<rates_total;i++) { if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (EldersiEMA[i]>EldersiEMA[i-1])) HistogramColors[i]=0; else { if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (EldersiEMA[i]<EldersiEMA[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2; } } } }
现在,我们编写条件以防止不必要的按钮闪烁:
if (ObjectGetInteger(0,"2color",OBJPROP_STATE)) ObjectSetString (0,"2color",OBJPROP_TEXT,"2ColorMACD"); else ObjectSetString(0,"2color",OBJPROP_TEXT,"MultiColor"); if (ObjectGetInteger(0,"Impulse",OBJPROP_STATE)) ObjectSetString (0,"Impulse",OBJPROP_TEXT,"Impulse"); else ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Elder's")
我们删除改变按钮文本的代码。
将所有内容结合在一起:
//+------------------------------------------------------------------ //| MACD_By_CoreWinTT.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------ #property copyright "2010, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 5 #property indicator_plots 3 //---- 绘制 MACD线 #property indicator_label1 "MACDline" #property indicator_type1 DRAW_LINE #property indicator_color1 Green #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //---- 绘制信号线 #property indicator_label2 "Signalline" #property indicator_type2 DRAW_LINE #property indicator_color2 Red #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //---- 绘制直方图 #property indicator_label3 "Histogram" #property indicator_type3 DRAW_COLOR_HISTOGRAM #property indicator_color3 DeepSkyBlue,Red,Green #property indicator_style3 STYLE_SOLID #property indicator_width3 2 //--- 输入参数 input int Fast=12; input int Slow=26; input int Signal=9; input int EldersEMA=13; //--- 指标缓存 double MACDlineBuffer[]; double SignallineBuffer[]; double HistogramBuffer[]; double HistogramColors[]; double EldersiEMA[]; //+------------------------------------------------------------------ //| 自定义指标初始化函数 | //+------------------------------------------------------------------ int OnInit() { //--- 指标缓存映射 SetIndexBuffer(0,MACDlineBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignallineBuffer,INDICATOR_DATA); SetIndexBuffer(2,HistogramBuffer,INDICATOR_DATA); SetIndexBuffer(3,HistogramColors,INDICATOR_COLOR_INDEX); SetIndexBuffer(4,EldersiEMA,INDICATOR_CALCULATIONS); //--- return(0); } //+------------------------------------------------------------------ //| 自定义指标迭代函数 | //+------------------------------------------------------------------ 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[]) { ArraySetAsSeries(HistogramBuffer,false); ArraySetAsSeries(HistogramColors,false); int MACDhadling=iMACD(NULL,0,Fast,Slow,Signal,PRICE_CLOSE); CopyBuffer(MACDhadling,0,0,rates_total,MACDlineBuffer); CopyBuffer(MACDhadling,1,0,rates_total,SignallineBuffer); for(int i=0;i<rates_total;i++) { HistogramBuffer[i]=MACDlineBuffer[i]-SignallineBuffer[i];HistogramColors[i]=1; } ObjectCreate(0,"ShowMACD",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"ShowMACD",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"ShowMACD",OBJPROP_YDISTANCE,5); ObjectSetInteger(0,"ShowMACD",OBJPROP_CORNER,CORNER_RIGHT_UPPER); ObjectSetString(0,"ShowMACD",OBJPROP_TEXT,"ShowMACD"); ObjectSetInteger(0,"ShowMACD",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"ShowMACD",OBJPROP_XSIZE,70); ObjectSetInteger(0,"ShowMACD",OBJPROP_YSIZE,20); ObjectSetInteger(0,"ShowMACD",OBJPROP_SELECTABLE,false); ObjectCreate(0,"ShowOsMA",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"ShowOsMA",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"ShowOsMA",OBJPROP_YDISTANCE,30); ObjectSetInteger(0,"ShowOsMA",OBJPROP_CORNER,CORNER_RIGHT_UPPER); ObjectSetString(0,"ShowOsMA",OBJPROP_TEXT,"Show OsMA"); ObjectSetInteger(0,"ShowOsMA",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"ShowOsMA",OBJPROP_XSIZE,70); ObjectSetInteger(0,"ShowOsMA",OBJPROP_YSIZE,20); ObjectSetInteger(0,"ShowOsMA",OBJPROP_SELECTABLE,false); if(ObjectGetInteger(0,"ShowMACD",OBJPROP_STATE)!=1) { PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_NONE); PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_NONE); } else { PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_LINE); } if(ObjectGetInteger(0,"ShowOsMA",OBJPROP_STATE)!=1) { PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_NONE); } else { PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_COLOR_HISTOGRAM); } ObjectCreate(0,"2color",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"2color",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"2color",OBJPROP_YDISTANCE,50); ObjectSetInteger(0,"2color",OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(0,"2color",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"2color",OBJPROP_XSIZE,70); ObjectSetInteger(0,"2color",OBJPROP_YSIZE,20); ObjectSetInteger(0,"2color",OBJPROP_SELECTABLE,false); ObjectSetString(0,"2color",OBJPROP_TEXT,"MultiColor"); ObjectCreate(0,"Impulse",OBJ_BUTTON,ChartWindowFind(),100,100); ObjectSetInteger(0,"Impulse",OBJPROP_XDISTANCE,75); ObjectSetInteger(0,"Impulse",OBJPROP_YDISTANCE,25); ObjectSetInteger(0,"Impulse",OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(0,"Impulse",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,"Impulse",OBJPROP_XSIZE,70); ObjectSetInteger(0,"Impulse",OBJPROP_YSIZE,20); ObjectSetInteger(0,"Impulse",OBJPROP_SELECTABLE,false); ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Impulse"); if(ObjectGetInteger(0,"2color",OBJPROP_STATE)) ObjectSetString(0,"2color",OBJPROP_TEXT,"2ColorMACD"); else ObjectSetString(0,"2color",OBJPROP_TEXT,"MultiColor"); if(ObjectGetInteger(0,"Impulse",OBJPROP_STATE)) ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Impulse"); else ObjectSetString(0,"Impulse",OBJPROP_TEXT,"Elder's"); CopyBuffer(iMA(NULL,0,EldersEMA,0,MODE_EMA,PRICE_CLOSE),0,0,rates_total,EldersiEMA); if(ObjectGetInteger(0,"2color",OBJPROP_STATE)) { for(int i=1;i<rates_total;i++) { if(HistogramBuffer[i] > HistogramBuffer[i-1]) HistogramColors[i]=0; if(HistogramBuffer[i] < HistogramBuffer[i-1]) HistogramColors[i]=1; } } else { if(ObjectGetInteger(0,"Impulse",OBJPROP_STATE)) { for(int i=1;i<rates_total;i++) { if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (MACDlineBuffer[i]>MACDlineBuffer[i-1])) HistogramColors[i]=0; else { if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (MACDlineBuffer[i]<MACDlineBuffer[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2; } } } else { for(int i=1;i<rates_total;i++) { if((HistogramBuffer[i]>HistogramBuffer[i-1]) && (EldersiEMA[i]>EldersiEMA[i-1])) HistogramColors[i]=0; else { if((HistogramBuffer[i]<HistogramBuffer[i-1]) && (EldersiEMA[i]<EldersiEMA[i-1])) HistogramColors[i]=1; else HistogramColors[i]=2; } } } } //--- 返回prev_calculated的值用于下一次调用 return(rates_total); } //+------------------------------------------------------------------
算法结构图如图 4 中所示:
图 4. 指标算法的结构图
结果在图 5-7 中显示。
图 5
图 6
图 7
本文可作为指南,供那些开始使用计算机价格分析来研究市场和实施指标图形控件的简单方法的初学者使用。
我希望本文可提高读者创建图形控件系统的技术技能,并通过隐藏阻碍物帮助读者找到自己的“市场视野”。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程