外汇EA编写教程:用 MQL5 绘制指标的喷发

简介

毫无疑问,许多交易人员和交易策略的开发人员会对下面这些问题感兴趣:

寻找这些问题的答案让我探索出一种市场研究的新方法:构造和分析指标喷发。为了让大家有更直观的印象,请参见下图:

图 1 DCMV 指标喷发。

图 2. 基于iMA轨道线的指标喷发。

图片展示了不同指标的喷发,但它们的构建原理是相同的。在每次价格跳动后,将出现越来越多的带不同颜色和各种形状的点。它们形成了众多的集群,形状包括星云、云团、轨迹、直线、弧线等。这些形状有助于发现影响市场价格变动的无形的跳跃力和驱动力。对这些喷发的研究和分析与手相学类似。

喷发及其属性

喷发是位于指标特定线的交叉点上的一组点。

喷发的属性尚未明确,仍然留待研究人员探索。这里列示的是已知属性:

  • 同一类型的点有集群倾向;
  • 喷发具有方向 – 从现在到未来或到过去;
  • 集群十分重要 – 密集的集群可吸引,或反过来排斥价格。

指标喷发的计算

我们将通过示例探讨喷发计算的基本原则。我们选取两个指标 – iBandsiMA – 并找到它们的交叉线。我们将使用它们来绘制喷发点。为此,我们需要使用图形对象。算法在“EA 交易”中实施,但它可在指标中完成。

初始指标如图 3 所示:

图 3. iBands 指标(绿色)和 iMA 指标(红色)

我们需要使用“EA 交易”来创建喷发点。最好是使用 MQL5 向导来创建“EA 交易”模板。

图 4. 使用 MQL5 向导来创建“EA 交易”模板。

//+------------------------------------------------------------------+
//|                                      Emission of Bands && MA.mq5 |
//|                                                 Copyright DC2008 |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| EA交易初始化函数                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  
}
//+------------------------------------------------------------------+
//| EA交易去初始化函数                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  
}
//+------------------------------------------------------------------+
//| EA的tick函数                                                      |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  
}
//+------------------------------------------------------------------+

首先,我们需要一些辅助绘图。我们需要使用半直线延续指标线(图 5)。这样便能控制喷发点的计算和可视化的正确性。稍后,我们将从图表中删除这些线条。

图 5. 辅助绘图。使用半直线延续指标线。

因此,让我们将图形对象(水平线和趋势线)添加至我们的“EA 交易”代码中。

input bool     H_line=true;   // 启用绘制水平线的标识
input bool     I_line=true;   // 启用绘制指标线的标识
//---
string         name;
//---- 指标缓存
double      MA[];    // iMA指标数组 
double      BBH[];   // iBands指标数组   - UPPER_BAND 
double      BBL[];   // iBands指标数组- LOWER_BAND
double      BBM[];   // iBands指标数组- BASE_LINE
datetime    T[];     // 时间坐标数组
//---- 指标的句柄
int         MAHandle;   // iMA 指标 句柄
int         BBHandle;   // iBands 指标 句柄
//+------------------------------------------------------------------+
//| EA交易初始化函数                                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MAHandle=iMA(Symbol(),0,21,0,MODE_EMA,PRICE_CLOSE);
   BBHandle=iBands(Symbol(),0,144,0,2,PRICE_CLOSE);
//---
   if(H_line)     // iBands 指标水平线
      {
         //--- iBands - UPPER_BAND
         name="Hi";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Red);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- iBands - LOWER_BAND
         name="Lo";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Blue);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- iBands - BASE_LINE
         name="MIDI";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,DarkOrange);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
      
}
//---
   if(I_line)     // Indicator lines
      {
         //--- iMA
         name="MA";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Red);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,2);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - UPPER_BAND
         name="BH";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - LOWER_BAND
         name="BL";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - BASE_LINE
         name="BM";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
      
}
   return(0);
  
}
//+------------------------------------------------------------------+
//| EA交易去初始化函数                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  
}
//+------------------------------------------------------------------+
//| EA的tick函数                                                      |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- 用当前值填充数组
   CopyBuffer(MAHandle,0,0,2,MA);
   ArraySetAsSeries(MA,true);  
   CopyBuffer(BBHandle,0,0,2,BBM);
   ArraySetAsSeries(BBM,true);  
   CopyBuffer(BBHandle,1,0,2,BBH);
   ArraySetAsSeries(BBH,true);  
   CopyBuffer(BBHandle,2,0,2,BBL);
   ArraySetAsSeries(BBL,true);
   CopyTime(Symbol(),0,0,10,T);
   ArraySetAsSeries(T,true);
     
   //--- iBands指标(修正的)水平线指标(修正的)水平线
   if(H_line)
      {
      name="Hi";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBH[0]);
      name="Lo";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBL[0]);
      name="MIDI";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBM[0]);
      
}
   //--- 指标线(修正的)
   if(I_line)
      {
      name="MA";  //--- iMA
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,MA[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,MA[0]);
      name="BH";  //--- iBands - UPPER_BAND
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBH[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBH[0]);
      name="BL";  //--- iBands - LOWER_BAND
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBL[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBL[0]);
      name="BM";  //--- iBands - BASE_LINE
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBM[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBM[0]);
      
}
  
}
//+------------------------------------------------------------------+

由于喷发向过去和未来延续,趋势线属性应如下所示:

  • OBJPROP_RAY_LEFT = 1,(半直线向左);
  • OBJPROP_RAY_RIGHT = 1,(半直线向右)。

如此一来,带附加线的图表将呈图 6 所示效果。

准备阶段到此结束,现在我们来进行喷发。我们将会在以下线的交叉处创建第一系列的点:

  • 位于 “MA” (iMA) 线和 “BH” (iBands = UPPER_BAND) 线之间;
  • 位于 “MA” (iMA) 线和 “BL” (iBands = LOWER_BAND) 线之间;
  • 位于 “MA” (iMA) 线和 “BM” (iBands = BASE_BAND) 线之间。

图 6. 辅助绘图。使用直线延续指标线。

现在,到了计算交叉点坐标和绘制喷发点的时候了。我们来创建函数:

void Draw_Point(
                string   P_name,     // 对象名称(OBJ_ARROW)
                double   P_y1,       // 柱形[1]第一行的Y轴坐标
                double   P_y0,       // 柱形[0]第一行的Y轴坐标
                double   P_yy1,      // 柱形[1]第二行的Y轴坐标 
                double   P_yy0,      // 柱形[0]第二行的Y轴坐标
                char     P_code1,    // 柱形[0]右侧的图表
                char     P_code2,    // 柱形[0]左侧的图表
                color    P_color1,   // 柱形[0]右侧的点的颜色
                color    P_color2    // 柱形[0]左侧的点的颜色
                )
  {
   double   P,X;
   datetime P_time;
   if(MathAbs((P_yy0-P_yy1)-(P_y0-P_y1))>0)
     {
      P=P_y1+(P_y0-P_y1)*(P_y1-P_yy1)/((P_yy0-P_yy1)-(P_y0-P_y1));
      X=(P_y1-P_yy1)/((P_yy0-P_yy1)-(P_y0-P_y1));
      if(X>draw_period)
        {
         P_time=T[0]+(int)(X*PeriodSeconds());
         ObjectCreate(0,P_name,OBJ_ARROW,0,0,0);
         ObjectSetDouble(0,P_name,OBJPROP_PRICE,P);
         ObjectSetInteger(0,P_name,OBJPROP_TIME,P_time);
         ObjectSetInteger(0,P_name,OBJPROP_WIDTH,0);
         ObjectSetInteger(0,P_name,OBJPROP_ARROWCODE,P_code1);
         ObjectSetInteger(0,P_name,OBJPROP_COLOR,P_color1);
         if(X<0)
           {
            ObjectSetInteger(0,P_name,OBJPROP_ARROWCODE,P_code2);
            ObjectSetInteger(0,P_name,OBJPROP_COLOR,P_color2);
           
}
        
}
     
}
  
}

并将下面的代码行添加至函数 OnTick

//+------------------------------------------------------------------+
   int GTC=GetTickCount();                                             
//+------------------------------------------------------------------+
   name="H"+(string)GTC;
   Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],170,178,Red,Red);
   name="L"+(string)GTC;
   Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],170,178,Blue,Blue);
   name="M"+(string)GTC;
   Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],170,178,Green,Green);
//---
   ChartRedraw(0);

现在,我们来运行“EA 交易”并查看结果(图 7)。

很好,但还有其他一些交叉情形,这是我们没有考虑到的。例如,iBands 指标有三条线彼此相交,可对全局做出补充。

图 7. iMA 和 iBands 指标的喷发(3 个交叉点)。

现在,让我们尝试将另一系列的点添加至计算出的喷发,交叉点位于下面列出的线之间:

  • 位于线 “BH” (iBands = UPPER_BAND) 和线 “BL” (iBands = LOWER_BAND) 之间;
  • 位于线 “BH” (iBands = UPPER_BAND) 和线 “BM” (iBands = BASE_BAND) 之间;
  • 位于线 “BL” (iBands = LOWER_BAND) 和线 “BM” (iBands = BASE_BAND) 之间。

由于这些交叉点,我们将获得 3 个点,但它们将具有相同的坐标。因此,仅使用一个位于线 “BH” 和线 “BL” 之间的交叉点即已足够。

让我们将这些代码行添加至我们的“EA 交易”,然后查看结果(图 8)。

   name="B"+(string)GTC;
   Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],170,178,Magenta,Magenta);

图 8. iMA 和 iBands 指标的喷发(4 个交叉点)。

因此,我们获得了喷发,但总有一种我们错过了某些重要事情的感觉。然而,我们到底错过了什么?

为什么我们仅使用了这些输入参数?如果我们更改输入参数,我们会得到什么结果?无论如何,它们在喷发中的角色是什么?

的确,我们获得的喷发对应于一个单一的频率,由指标的输入参数所导致。要计算完整的多频率频谱,需要为其他频率执行相同的计算。作为示例,以下是我的可能的喷发频率版本:

//---- 指标的句柄
int         MAHandle[5];   // iMA 指标数组
int         BBHandle[7];   // iBands 指标句柄数组
//+------------------------------------------------------------------+
//| EA交易初始化函数                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MAHandle[0]=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[1]=iMA(NULL,0,34,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[2]=iMA(NULL,0,55,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[3]=iMA(NULL,0,89,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[4]=iMA(NULL,0,144,0,MODE_EMA,PRICE_CLOSE);
//---
   BBHandle[0]=iBands(NULL,0,55,0,2,PRICE_CLOSE);
   BBHandle[1]=iBands(NULL,0,89,0,2,PRICE_CLOSE);
   BBHandle[2]=iBands(NULL,0,144,0,2,PRICE_CLOSE);
   BBHandle[3]=iBands(NULL,0,233,0,2,PRICE_CLOSE);
   BBHandle[4]=iBands(NULL,0,377,0,2,PRICE_CLOSE);
   BBHandle[5]=iBands(NULL,0,610,0,2,PRICE_CLOSE);
   BBHandle[6]=iBands(NULL,0,987,0,2,PRICE_CLOSE);
//---
   return(0);
  
}

为将所有可能的组合考虑在内,我们需要将以下代码添加至“EA 交易”:

//+------------------------------------------------------------------+
   CopyTime(NULL,0,0,10,T);
   ArraySetAsSeries(T,true);
   int GTC=GetTickCount();
//+------------------------------------------------------------------+
   int iMax=ArraySize(BBHandle)-1;
   int jMax=ArraySize(MAHandle)-1;
   for(int i=0; i<iMax; i++)
     {
      for(int j=0; j<jMax; j++)
        {
         //--- 用当前值填充数组
         CopyBuffer(MAHandle[j],0,0,2,MA);
         ArraySetAsSeries(MA,true);
         CopyBuffer(BBHandle[i],0,0,2,BBM);
         ArraySetAsSeries(BBM,true);
         CopyBuffer(BBHandle[i],1,0,2,BBH);
         ArraySetAsSeries(BBH,true);
         CopyBuffer(BBHandle[i],2,0,2,BBL);
         ArraySetAsSeries(BBL,true);

         name="H"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],250,158,Aqua,Aqua);
         name="L"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],250,158,Blue,Blue);
         name="M"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],250,158,Green,Green);
         name="B"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],250,158,Magenta,Magenta);
        
}
     
}
//---
   ChartRedraw(0);

喷发频谱中包含的频率越多,图表上的图形就越好,但也不能滥用 – 它是消耗计算机资源最简单的方式,并会使图表上的图形变得一团糟。频率的数量可凭经验判定。为了获得更好的图形感知,我们必须特别注意绘制风格。

图 9. 多频率喷射频谱。

论喷射的绘制风格

MQL5 语言为喷射的绘制提供了各种网页颜色Windings 字符。在此,我想分享我对绘制风格的一些看法:
 

  1. 每个人对图形图像都有自己的感知,因此您需要花费一些时间来自定义喷射。
  2. 我们无法从图 9 的“混沌”中识别任何规律或模式。它是糟糕绘制的一个示例。
  3. 尝试在彩虹频谱中使用相邻色。
  4. 区分过去(来自 [0] 柱的左侧)和未来(来自 [0] 柱的右侧)的字符码。
  5. 将点的颜色和形状成功结合能够使喷发转变为杰作,这不仅有助于交易,同时还能悦观者目。

作为示例,以下是我的喷发绘制风格版本(见图 10-17):

         name="H"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],250,158,Aqua,Aqua);
         name="L"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],250,158,Blue,Blue);
         name="M"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],250,158,Magenta,Magenta);
         name="B"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],250,158,DarkOrchid,DarkOrchid);

iMA 和 iBands 喷发图库

以下是本文所述及的喷发图像。

 

图 10. 

 

11

12.

13.

14.

 

15.

16.

 

17.

喷发分析

喷发分析是一项独立的任务。最有价值的是实时观察它的动态,这是理解众多效果和模式的最佳方式。

注意价格的修正 – 看上去喷发似乎“知道”目标价格。此外,您可以查看支撑、阻力和均衡价位。

总结

  1. 指标的喷发可能会引起正在探索市场研究及分析的新方法的交易人员和交易系统开发人员的兴趣。
  2. 作为一篇介绍性文章,本文并未提供现成的解决方案。然而,文中给出的喷发计算技术可应用至其他指标或指标的组合。
  3. 在文章的成文过程中,我碰到的问题要多过答案。以下是部分摘录:如何优化喷发绘制的算法;喷发频谱特性在喷发结构中处于什么角色;如何在自动交易中使用喷发?

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/26

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投