使用MQL4和MQL5绘制基于分形指标的趋势线

Almat Kaldybay | 27 八月, 2015

内容简介表


简介

最近,我一直在考虑如何使用趋势线。如何选择绘制趋势线的点以及绘制的精确度一直是个问题。我决定使用分形来作为基础。

我的主要工作是分析市场,也能花些时间来做交易。你不能仅仅在长时间框架下绘制趋势线,应能够通过极点精确到15分钟图表上。原因是长时间框架上的分形时间并不总是等于M15上的极值点的时间。简而言之,自动化在此能够派上用场。我开始用MQL5编写代码然后移植到MQL4上,因为我需要程序运行于MetaTrader 4。

在本文中,我将以MQL4和MQL5两种语言来呈现问题的解决方案。虽然在本文中对两种语言进行了比较,但并不是为了对比MQL4和MQL5的执行效率。当然我也意识到可能有比我更好的解决方法。本文对使用MQL4或MQL5编写脚本的初学者有帮助,尤其是那些计划使用分形和趋势线的朋友。


1. 输入参数,DeInit()函数及初始化变量声明

我使用如下变量作为参数:

input color Resistance_Color=Red;       // 设置阻力线颜色
input ENUM_LINE_STYLE Resistance_Style; // 设置阻力线类型
input int Resistance_Width=1;           // 设置阻力线宽度
input color Support_Color=Red;          // 设置支撑线颜色
input ENUM_LINE_STYLE Support_Style;    // 设置支撑线类型
input int Support_Width=1;              // 设置支撑线宽度

这些变量对于MQL4和MQL5都是一样的。

在MQL5中我们得先创建指标:

//--- iFractals 指标句柄 
int Fractal;
//+------------------------------------------------------------------+
//| EA初始化函数                                                      |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 获取iFractals指标句柄
   Fractal=iFractals(Symbol(),PERIOD_D1);
//---
   return(INIT_SUCCEEDED);
  }

因为程序将绘制图形对象,那么有必要在EA从图标上移除的同时删除它们。

void OnDeinit(const int reason)
  {
   ObjectDelete(0,"TL_Resistance");
   ObjectDelete(0,"TL_Support");
  }

绘制两条直线(支撑和阻力线)需要四个点。要确定点需要知道时间和价格。

坐标以如下顺序确定:首选,我们找到极点所在K线,找到它我们就能确定极点的价格和时间。

OnTick()函数中声明变量:

MQL4
//--- 声明变量
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;

MQL5
//--- 声明变量
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;
//--- 声明用于写入iFractal指标值的缓存数组
double FractalDown[],FractalUp[];
double UpFractal_1,UpFractal_2,LowFractal_1,LowFractal_2;

首先,我仅仅声明了那些用于存储形成分形的K线索引变量。

在MQL4中:

  1. n - 使用for循环操作符找到最近的分形所需的变量;
  2. UpperFractal_1, UpperFractal_2,  LowerFractal_1, LowerFractal_2 - 这些变量将存储最近的两个极点对应的K线索引,极点即最高或最低价(用来确定分形)

在MQL5中我们引入额外的变量:

  1. FractalDown[],FractalUp[]; - 声明存储iFractals指标缓存值的double类型数组;
  2. 接下来,double类型变量: UpFractal_1,UpFractal_2,LowFractal_1,LowFractal_2。它们将存储极点的价格。

2. 搜索最近的分形

要找到形成最近分形的K线,我们使用for循环操作符

让我们确定最先的两个K线的索引,它们对应第一和第二上分形。

MQL4
//--- 找到最近的上分形K线索引
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_1=n+1;
     }
//--- 找到第二近的上分形K线索引
   for(n=UpperFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_2=n+1;
     }

 MQL5
//--- 首先,我们要将分形指标缓存值写入数组
//--- 用缓存值填充数组
   CopyBuffer(Fractal,0,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalUp);
   CopyBuffer(Fractal,1,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalDown);
//--- 索引类似时间序列
   ArraySetAsSeries(FractalUp,true);
   ArraySetAsSeries(FractalDown,true);
//--- 接下来我们使用for循环来查找第一个上分形
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- 如果值非空,跳出循环体
      if(FractalUp[n]!=EMPTY_VALUE)
         break;
     }
//--- 将第一个分形的价格写入变量
   UpFractal_1=FractalUp[n];
//--- 将其索引写入变量
   UpperFractal_1=n;
//--- 查找第二个上分形 
   for(n=UpperFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalUp[n]!=EMPTY_VALUE) //如果值非空,跳出循环体
         break;
     }
//--- 将第二个分形的价格写入变量
   UpFractal_2=FractalUp[n];
//--- 将第二个分形的索引写入变量
   UpperFractal_2=n;

在这里我清楚的揭示了MQL5和MQL4的区别 - 使用获取时间序列的函数

在MQL4中,我立即开始查找形成分形的K线索引,但是在MQL5中我定义了FractalUp[]和FractalDown[]数组来存储上下分形的值,它们通过iFractals指标和CopyBuffer()函数获取。接下来,我使用ArraySetAsSeries()函数将这些数组设置为时间序列。

在MQL4中和仅仅获得分形对应K线的索引,但是在MQL5中我使用CopyBuffer()函数获取分形的索引和其对应的价格。

类似的,我们找到了最近的两个下分形:

MQL4
//--- 找到最近的下分K线形索引
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_1=n+1;
     }
//--- 找到第二近的下分形K线索引
   for(n=LowerFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_2=n+1;
     }

 MQL5
//--- 找到下分形的值
//--- 查找最近的下分形
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- 如果值非空,跳出循环体
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- 将第一个分形的价格写入变量
   LowFractal_1=FractalDown[n];
//--- 将其索引写入变量
   LowerFractal_1=n;
//--- 查找第二个下分形 
   for(n=LowerFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- 将第二个分形的价格写入变量
   LowFractal_2=FractalDown[n];
//--- 将第二个分形的索引写入变量
   LowerFractal_2=n;

如你所见,MQL4和MQL5代码非常类似。只有一些句法上细微的区别。


3. 确定分形的价格和时间

要绘制趋势线,我们需要确定分形的时间和价格。当然,在MQL4中我们可以简单的使用High[]和Low[]预定义时间序列变量,以及iTime()函数,然而我们也需要获取更为精确的坐标来确保绘制趋势线的准确性。

图1-2显示了H4和M15时间框架下极点时间的差别。

图1. H4图表上的极点时间。

图1. H4图表上的极点时间。

图2. M15图表上的极点时间

图2. M15图表上的极点时间

我得出结论,M15上的极点精度对我来说已经足够。

总的来说,极点确定的原则在MQL4和MQL5上几乎是一样的,但是细节上还是略有差异:

MQL4MQL5
  1. 在大时间框架下确定极点的时间值;
  2. 使用找到的时间值,通过iBarShift()函数在较小时间框架上,确定极值所在K线的索引;
  3. 因为24小时能够用96个15分钟K线代替,我们使用iHigh()iLow()iTime()ArrayMaximum()ArrayMinimum()函数在这96个元素中查找极点(最高和最低价)。
  1. 在大时间框架下确定极点的时间值;
  2. 使用找到的时间值,确定下一个日线的开始时间。我们要将这个值用于CopyHigh(), CopyLow()CopyTime() 函数中;
  3. 声明和填充15分钟时间框架下的时间和价格数组。
  4. 使用ArrayMaximum()ArrayMinimum() 函数,找到最低和最高价格,以及极点的时间值。 

每一步的代码如下:

 MQL4
// Step 1. 在大时间框架下确定极点时间:
//--- 确定分形的时间
   datetime UpFractalTime_1=iTime(NULL, 1440,UpperFractal_1);
   datetime UpFractalTime_2=iTime(NULL, 1440,UpperFractal_2);
   datetime LowFractalTime_1=iTime(NULL, 1440,LowerFractal_1);
   datetime LowFractalTime_2=iTime(NULL, 1440,LowerFractal_2);

// Step 2.  在较小的时间框架上确定极点所在K线的索引   
//--- 在M15上查找分形索引
   int UpperFractal_1_m15=iBarShift(NULL, 15, UpFractalTime_1,true);
   int UpperFractal_2_m15=iBarShift(NULL, 15, UpFractalTime_2,true);
   int LowerFractal_1_m15=iBarShift(NULL, 15, LowFractalTime_1,true);
   int LowerFractal_2_m15=iBarShift(NULL, 15, LowFractalTime_2,true);


// Step 3. 在M15上使用数组查找极点:
//--- 使用数组查找极点
//--- 声明i变量用于循环
   int i;
//--- 1. 首先,查找低值极点
//--- 3.1 查找最近的低值极点
//--- 声明存储K线索引值的数组
   int Lower_1_m15[96];
//--- 声明存储价格的数组
   double LowerPrice_1_m15[96];
//--- 开始循环:
   for(i=0;i<=95;i++)
     {
      //--- 用K线索引值填充数组
      Lower_1_m15[i]=LowerFractal_1_m15-i;
      //--- 用价格填充数组
      LowerPrice_1_m15[i]=iLow(NULL,15,LowerFractal_1_m15-i);
     }
//--- 确定数组中的最低价
   int LowestPrice_1_m15=ArrayMinimum(LowerPrice_1_m15,WHOLE_ARRAY,0);
//--- 确定数组中最低价对应的K线索引
   int LowestBar_1_m15=Lower_1_m15[LowestPrice_1_m15];
//--- 确定最低价K线所在的时间
   datetime LowestBarTime_1_m15=iTime(NULL,15,Lower_1_m15[LowestPrice_1_m15]);

//--- 3.2 查找第二极值点
   int Lower_2_m15[96];
   double LowerPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- 用K线索引值填充数组
      Lower_2_m15[i]=LowerFractal_2_m15-i;
      //--- 用价格填充数组
      LowerPrice_2_m15[i]=iLow(NULL,15,LowerFractal_2_m15-i);
     }
//--- 确定数组中的最低价
   int LowestPrice_2_m15=ArrayMinimum(LowerPrice_2_m15,WHOLE_ARRAY,0);
//--- 确定数组中最低价对应的K线索引
   int LowestBar_2_m15=Lower_2_m15[LowestPrice_2_m15];
//--- 确定最低价K线所在的时间
   datetime LowestBarTime_2_m15=iTime(NULL,15,Lower_2_m15[LowestPrice_2_m15]);

//--- 3.3 查找最近的高值极点
   int Upper_1_m15[96];
   double UpperPrice_1_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- 用K线索引值填充数组
      Upper_1_m15[i]=UpperFractal_1_m15-i;
      //--- 用价格填充数组
      UpperPrice_1_m15[i]=iHigh(NULL,15,UpperFractal_1_m15-i);
     }
//--- 确定数组中的最高价
   int HighestPrice_1_m15=ArrayMaximum(UpperPrice_1_m15,WHOLE_ARRAY,0);
//--- 确定数组中最高价对应的K线索引
   int HighestBar_1_m15=Upper_1_m15[HighestPrice_1_m15];
//--- 确定最高价K线所在的时间
   datetime HighestBarTime_1_m15=iTime(NULL,15,Upper_1_m15[HighestPrice_1_m15]);

//--- 3.4 查找第二高值极值点
   int Upper_2_m15[96];
   double UpperPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- 用K线索引值填充数组
      Upper_2_m15[i]=UpperFractal_2_m15-i;
      //--- 用价格填充数组
      UpperPrice_2_m15[i]=iHigh(NULL,15,UpperFractal_2_m15-i);
     }

 MQL5
// Step 1. 在大时间框架下确定极点时间:
//--- 声明存储较大时间框架下K线对应的时间的数组
   datetime UpFractalTime_1[],LowFractalTime_1[],UpFractalTime_2[],LowFractalTime_2[];
//--- 确定较大时间框架下分形的时间
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_1,1,UpFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_1,1,LowFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_2,1,UpFractalTime_2);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_2,1,LowFractalTime_2);


// Step 2. 确定下一个日线的开始时间
//--- 确定下一个日线的开始时间(CopyHigh(),CopyLow() 和 CopyTime()的结束时间)
   datetime UpFractalTime_1_15=UpFractalTime_1[0]+86400;
   datetime UpFractalTime_2_15=UpFractalTime_2[0]+86400;
   datetime LowFractalTime_1_15=LowFractalTime_1[0]+86400;
   datetime LowFractalTime_2_15=LowFractalTime_2[0]+86400;


// Step 3. 声明和填充15分钟时间框架下的时间和价格数组:   
//--- 声明存储最大和最小价格值的数组
   double High_1_15[],Low_1_15[],High_2_15[],Low_2_15[];
//--- 用CopyHigh() 和 CopyLow() 函数填充数组
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15);
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15);
//--- 声明存储对应极点所在K线时间的数组  
   datetime High_1_15_time[],High_2_15_time[],Low_1_15_time[],Low_2_15_time[];
//--- 填充数组
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15_time);

// Step 4. 查找最低和最高价格,以及极点对应的时间值
//--- 用ArrayMaximum() 和 ArrayMinimum() 函数确定最高和最低价格以及时间
   int Max_M15_1=ArrayMaximum(High_1_15,0,96);
   int Max_M15_2=ArrayMaximum(High_2_15,0,96);
   int Min_M15_1=ArrayMinimum(Low_1_15,0,96);
   int Min_M15_2=ArrayMinimum(Low_2_15,0,96);

最后,我们确定了下面趋势线的坐标:

1. 支撑线:

MQL4MQL5
  1. 第一个时间坐标 -  LowestBarTime_2_m15;
  2. 第一个价格坐标  - LowerPrice_2_m15[LowestPrice_2_m15];
  3. 第二个时间坐标  - LowestBarTime_1_m15;
  4. 第二个价格坐标  - LowerPrice_1_m15[LowestPrice_1_m15].
  1. 第一个时间坐标 - Low_2_15_time[Min_M15_2];
  2. 第一个价格坐标 - Low_2_15[Min_M15_2];
  3. 第二个时间坐标 - Low_1_15_time[Min_M15_1];
  4. 第二个价格坐标 - Low_1_15[Min_M15_1].

2. 对于阻力线:

MQL4MQL5
  1. 第一个时间坐标 -  HighestBarTime_2_m15;
  2. 第一个价格坐标  - UpperPrice_2_m15[HighestPrice_2_m15];
  3. 第二个时间坐标  - HighestBarTime_1_m15;
  4. 第二个价格坐标  - UpperPrice_1_m15[HighestPrice_1_m15].
  1. 第一个时间坐标 - High_2_15_time[Max_M15_2];
  2. 第一个价格坐标 - High_2_15[Max_M15_2];
  3. 第二个时间坐标 - High_1_15_time[Max_M15_1];
  4. 第二个价格坐标 - High_1_15[Max_M15_1].


4. 创建对象并编辑属性重绘趋势线

现在,当我们知道了直线的坐标后,我们仅仅需要创建图形对象了:

MQL4
//--- 创建支撑线
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,LowestBarTime_2_m15,LowerPrice_2_m15[LowestPrice_2_m15],
                LowestBarTime_1_m15,LowerPrice_1_m15[LowestPrice_1_m15]);
   ObjectSet("TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSet("TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSet("TL_Support",OBJPROP_WIDTH,Support_Width);
//--- 创建阻力线
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,HighestBarTime_2_m15,UpperPrice_2_m15[HighestPrice_2_m15],
                HighestBarTime_1_m15,UpperPrice_1_m15[HighestPrice_1_m15]);
   ObjectSet("TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSet("TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSet("TL_Resistance",OBJPROP_WIDTH,Resistance_Width);

MQL5
//--- 创建支撑线
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,Low_2_15_time[Min_M15_2],Low_2_15[Min_M15_2],Low_1_15_time[Min_M15_1],Low_1_15[Min_M15_1]);
   ObjectSetInteger(0,"TL_Support",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSetInteger(0,"TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSetInteger(0,"TL_Support",OBJPROP_WIDTH,Support_Width);
//--- 创建阻力线
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,High_2_15_time[Max_M15_2],High_2_15[Max_M15_2],High_1_15_time[Max_M15_1],High_1_15[Max_M15_1]);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_WIDTH,Resistance_Width);

至此我创建了需要的线并且基于输入参数确定了他们的参数。

现在我们要绘制趋势线了。

当市场情况发生变化时,例如,当新的极点出现时,我们要将已有的线移除:

MQL4
//--- 重绘支撑线
//--- 将支撑线的时间坐标写入变量中
   datetime TL_TimeLow2=ObjectGet("TL_Support",OBJPROP_TIME2);
   datetime TL_TimeLow1=ObjectGet("TL_Support",OBJPROP_TIME1);
//--- 如果线的坐标不符合当前坐标
   if(TL_TimeLow2!=LowestBarTime_1_m15 && TL_TimeLow1!=LowestBarTime_2_m15)
     {
      //--- 移除直线
      ObjectDelete(0,"TL_Support");
     }
//--- 重绘支撑线
//--- 将阻力线的时间坐标写入变量中
   datetime TL_TimeUp2=ObjectGet("TL_Resistance",OBJPROP_TIME2);
   datetime TL_TimeUp1=ObjectGet("TL_Resistance",OBJPROP_TIME1);
//--- 如果线的坐标不符合当前坐标
   if(TL_TimeUp2!=HighestBarTime_1_m15 && TL_TimeUp1!=HighestBarTime_2_m15)
     {
      //--- 移除直线
      ObjectDelete(0,"TL_Resistance");
     }

MQL5
//--- 重绘支撑线
//--- 将支撑线的时间坐标写入变量中
   datetime TL_TimeLow2=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,0);
   datetime TL_TimeLow1=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,1);
//--- 如果线的坐标不符合当前坐标
   if(TL_TimeLow2!=Low_2_15_time[Min_M15_2] && TL_TimeLow1!=Low_1_15_time[Min_M15_1])
     {
      //--- 移除直线
      ObjectDelete(0,"TL_Support");
     }
//--- 重绘支撑线
//--- 将阻力线的时间坐标写入变量中
   datetime TL_TimeUp2=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,0);
   datetime TL_TimeUp1=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,1);
//--- 如果线的坐标不符合当前坐标
   if(TL_TimeUp2!=High_2_15_time[Max_M15_2] && TL_TimeUp1!=High_1_15_time[Max_M15_1])
     {
      //--- 移除直线
      ObjectDelete(0,"TL_Resistance");
     }


5. 检查历史数据加载情况

在测试中我发现这些线并不总是能够正确绘制。

起先我向可能是代码中有bug或者我的解决方案有问题。但是后来我意识到问题是由于较小时间框架如M15上的历史数据加载不完全导致的。为了提醒用户这种情况的出现,我决定增加额外的代码来检查M15上K线是否足够。

为了实现这一目标,我使用MQL4中的iBarShift()函数,这也是我在“确定分形的价格和时间值”时用到过的函数。

如果没有找到K线,iBarShift()函数会返回-1。因此,我们输出以下警告:

MQL4
//--- 检查历史数据加载情况
//--- 如果至少有一个分形对应的K线在M15上没有找到
   if(UpperFractal_1_m15==-1 || UpperFractal_2_m15==-1
      || LowerFractal_1_m15==-1 || LowerFractal_2_m15==-1)
     {
      Alert("The loaded history is insufficient for the correct work!");
     }

在MQl5中我使用Bars()函数,如果时间序列数据没有在生成,它会返回一个空值:

 
//--- 检查历史数据加载情况
//--- 1. 确定特定时间框架下的K线数量
   int High_M15_1=Bars(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15);
   int High_M15_2=Bars(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15);
   int Low_M15_1=Bars(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15);
   int Low_M15_2=Bars(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15);
//--- 2. 检查用于正确绘制线段的历史数据是否足够
//--- 如果至少有一个没有找到
   if(High_M15_1==0 || High_M15_2==0 || Low_M15_1==0 || Low_M15_2==0)
     {
      Alert("The loaded history is insufficient for the correct work!");
     }


6. 突破趋势线的信号,推送通知

为了使图完整,我决定当趋势线被突破时增加一个提示信号。趋势线由日时间框架下的极点绘制,但是为了能够早些确认突破,在H4图上K线必须收盘低于或高于趋势线。

总的来说,我们可以将此过程分为三步:

  1. 确定K线手哦按家和趋势线价格;
  2. 确定价格突破趋势线的条件;
  3. 发送关于突破的通知。
MQL4
// 1. 获取趋势线的价格参数 
//--- 确定索引为1的K线的收盘价
   double Price_Close_H4=iClose(NULL,240,1);
//--- 确定索引为1的K线的时间
   datetime Time_Close_H4=iTime(NULL,240,1);
//---确定H4图上K线的索引
   int Bar_Close_H4=iBarShift(NULL,240,Time_Close_H4);
//--- 确定H4图上趋势线的价格
   double Price_Resistance_H4=ObjectGetValueByShift("TL_Resistance",Bar_Close_H4);
//--- 确定H4图上趋势线的价格   
   double Price_Support_H4=ObjectGetValueByShift("TL_Support",Bar_Close_H4);

// 2. 突破趋势线的条件
//--- 突破支撑线
   bool breakdown=(Price_Close_H4<Price_Support_H4);
//--- 突破阻力线
   bool breakup=(Price_Close_H4>Price_Resistance_H4);

// 3. 发送推送消息
   if(breakdown==true)
     {
      //---每4小时只发送一次通知
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the support line");
        }
     }
   if(breakup==true)
     {
      //---每4小时只发送一次通知
      SleepMinutes=240;
      LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the resistance line");
        }
     }

MQL5
// 1. 获取趋势线的价格参数
   double Close[];
   CopyClose(Symbol(),PERIOD_H4,TimeCurrent(),10,Close);
//--- 设置数组索引顺序
   ArraySetAsSeries(Close,true);
//---
   datetime Close_time[];
   CopyTime(Symbol(),PERIOD_H4,TimeCurrent(),10,Close_time);
//--- 设置数组索引顺序
   ArraySetAsSeries(Close_time,true);
//---
   double Price_Support_H4=ObjectGetValueByTime(0,"TL_Support",Close_time[1]);
   double Price_Resistance_H4=ObjectGetValueByTime(0,"TL_Resistance",Close_time[1]);

// 2. 突破趋势线的条件
   bool breakdown=(Close[1]<Price_Support_H4);
   bool breakup=(Close[1]>Price_Resistance_H4);

// 3. 发送推送消息
   if(breakdown==true)
     {
      //---每4小时只发送一次通知
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=(int)TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the support line");
        }
     }
   if(breakup==true)
     {
      //---每4小时只发送一次通知
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=(int)TimeCurrent();
         SendNotification(Symbol()+"The price has broken through the resistance line");
        }
     }

为了确定一个突破,在MQL4中我使用ObjectGetValueByShift()函数,在MQL5中使用ObjectGetValueByTime()函数。

获取我仅仅可以设置1来代替Bar_Close_H4作为ObjectGetValueByShift()的参数,但是我决定先确定H4图上的索引号。我使用这个论坛帖子所介绍的方法来限制消息发送次数,在这里非常感谢这篇帖子的作者。


7. 趋势线在交易中的实际应用

最简单的办法是:确定一个突破,等待回踩后再入场。

理想状态下,会有如下情况:

图 3. 突破趋势线

图 3. 突破趋势线

 你可以使用想象力来尝试确定形态,例如,技术分析的形态,三角形。

图4. 三角模式

图4. 三角模式

上图中较小时间框架下的趋势线没有说明。


总结

全文总结,希望那个对你有用。本文面向和我一样的业务初级程序开发者。

在撰写本文时我获益匪浅:首先,我开始更多地对代码进行注释;其次,刚开始时我使用较为笨拙和繁复的方法来查找极点,但是后来我慢慢的使用更为简单的解决方案,正如文中所呈现的。

感谢您,有任何问题都请联系我。