1.1 小型趋势
小型趋势的图形如图 1 所示。赫兹简单地介绍一下构建小型趋势的规则:
- 在向上走势中(下一个柱的最大值大于上一个柱的最大值,且下一个柱的最小值大于上一个柱的最小值),赫兹将下一个柱的最大值和上一个柱连接,得到一条上升趋势线段;
- 在向下走势中(下一个柱的最小值小于上一个柱的最小值,且下一个柱的最大值小于上一个柱的最大值),赫兹将下一个柱的最小值和上一个连接,得到一条下降趋势线段;
- 如果在向上走势期间,其他柱的最大值不大于上一个柱的最大值,且因此下一个柱的最小值小于上一个柱的最小值,则走势方向变为向下;
- 如果在向下走势期间,下一个柱的最小值不小于上一个柱的最小值,且与此同时下一个柱的最大值大于上一个柱的最大值,则方向变为向上;
- 对于任何走势,如果下一个柱的最小值大于上一个柱的最小值,且与此同时下一个柱的最大值小于上一个柱的最大值,则该柱为内侧柱(参见图 1)且不参与图形的构建;
- 如果在任何走势期间,下一个柱的最大值大于上一个柱的最大值,且与此同时下一个柱的最小值小于上一个柱的最小值,则该柱为外侧柱(参见图 1)。基于柱的方向(上升或下降),赫兹构建图形对应的区段。
图 1. 小型趋势
当图形的走势方向改变时,就会形成所谓的小型顶部和底部。在图 1 中,小型底部以左侧红色价格标签标示,小型顶部以右侧绿色价格标签标示。
如果在向上走势期间方向变为向下,但没有达到上一个底部,则赫兹有了一个纠正移动。否则,方向会发生变化。这对于向下走势同样适用。
因此,价格在底部和顶部之间移动。这种移动称之为摆动。与此同时,可以生成纠正移动。
这些就是构建小型趋势的指标线的简要规则。更详细的描述可在上文提到的书中第一部分找到。该指标的图形与著名的峰谷指标的图形类似。但不同的是,它不是在其他柱收盘后重新绘制,并且没有影响趋势线的构建的外部可调节参数。
1.2. GannMicroTrend 指标
赫兹来开发一个显示小型趋势的图形的指标。它的外观应与图 1 所示的相同。赫兹同样将输出添加至顶部和底部的最新位置的显示,对应于时间。该指标位于本文所附文件 GannMicroTrend.mq5 中。
要显示该信息,使用在《利用标准库类创建您自己的“市场报价”》一文中介绍的库。为此,在指标文本的开头添加 #include 指令:
#include <TextDisplay.mqh>
假设上述文件位于 \MQL5\Include 文件夹中。
指标应在全局变量中存储当前趋势的参数和最新顶部和底部的参数。同时,应该能够针对历史数据中指定的柱数指定计算深度。
为此,添加外部指定参数:
input int MaxBars = 1000;
input bool IsSaveTrendParams = true;
//---------------------------------------------------------------------
input bool ShowInfo = true;
input int UpDownInfoShift = 1;
input int LeftRightInfoShift = 1;
input color TitlesColor = LightCyan;
input color TopFieldsColor = Green;
input color LowFieldsColor = Brown;
//---------------------------------------------------------------------
input color UpTrendColor = LightGreen;
input color DnTrendColor = LightPink;
input int LineWidth = 4;
//---------------------------------------------------------------------
input color UpStopLossColor = Green;
input color DnStopLossColor = Brown;
input int StopLossWidth = 1;
这些参数的目的如下表中所示:
参数名称 | 参数目的 |
---|---|
MaxBars | 历数据中指标要绘制的最大柱数。如果为 0,则历史数据中的所有可用柱已计算和绘制。 |
IsSaveTrendParams | 如果为真,则当前趋势的参数和最新顶部和底部的参数存储在全局变量中。 |
ShowInfo | 如果为真,则图形窗口显示最新顶部和底部的坐标。 |
UpDownInfoShift | 信息输出位置的从上到下的垂直偏移。 |
LeftRightInfoShift | 信息输出位置的从左到右的水平偏移。 |
TitlesColor | 信息输出时的标题的颜色。 |
TopFieldsColor | 输出最新顶部的参数时的文本颜色。 |
LowFieldsColor | 输出最新底部的参数时的文本颜色。 |
UpTrendColor | 绘制向上走势的线的颜色。 |
DnTrendColor | 绘制向下走势的线的颜色。 |
LineWidth | 趋势线的宽度。 |
UpStopLossColor | 标示顶部的右侧价格标签的颜色。 |
DnStopLossColor | 标示底部的左侧价格标签的颜色。 |
StopLossWidth | 指示顶部和底部的价格标签的大小。 |
赫兹将使用来自标准类组的 CChartObjectTrend 类型的图形对象构建趋势线。次级顶部将通过 CChartObjectArrowLeftPrice 类型的对象使用左侧价格标签标示,而次级底部通过 CChartObjectArrowRightPrice 类型的对象标示。所有这些对象都包含在随 MetaTrader 5 终端一起提供的标准库类中。
由于未来可能需要操作趋势线以及顶部/底部,赫兹将它们存储于同样来自标准类组的 CList. 类型的对象列表中。为此,我们在指标的开头添加包含头文件的指令:
#include <Arrays\List.mqh>
#include <ChartObjects\ChartObjectsLines.mqh>
#include <ChartObjects\ChartObjectsArrows.mqh>
接下来,添加对象列表:
CList* trend_list_Ptr = NULL; // list of the trend lines
CList* up_list_Ptr = NULL; // list of the peaks
CList* dn_list_Ptr = NULL; // list of the bottoms
现在,我们具备了构建指标的所有元素。
指标的 OnInit 函数如下所示:
int OnInit()
{
trend_list_Ptr = new CList();
if(CheckPointer(trend_list_Ptr) != POINTER_DYNAMIC)
{
Print("Error of creating the object CList #1");
return(-1);
}
up_list_Ptr = new CList();
if(CheckPointer(up_list_Ptr) != POINTER_DYNAMIC)
{
Print("Error of creating the obkect CList #2");
return(-1);
}
dn_list_Ptr = new CList();
if(CheckPointer(dn_list_Ptr) != POINTER_DYNAMIC)
{
Print("Error of creating the object CList #3");
return(-1);
}
if(InitGraphObjects() != 0)
{
Print("Error of creating the object TableDisplay");
return(-1);
}
return(0);
}
此为创建指向对象列表的指针,然后检查创建是否成功。如果指针无效,则会接收到一条错误消息,此时指标的工作完成。错误发生的位置可通过带 # 字符的编号识别。接下来,显示最新顶部和底部参数的表格完成初始化。这在功能码 InitGraphObjects 中完成。
指标的主要部分是执行计算所需的事件处理程序。它就是 OnCalculate 函数。让赫兹来一点一点地进行分析。第一部分 - 检查重新计算指标整个显示部分的需要。当指标在图形上第一次启动、在图形上下载更新的历史数据、时间表发生变化时,就产生了这种需要。在所有这些情形中,自变量 prev_calculated 的值等于零。
这通过以下方式验证:
int index, start = prev_calculated - 1;
if(prev_calculated == 0)
{
if(CheckPointer(trend_list_Ptr) != POINTER_INVALID)
{
trend_list_Ptr.Clear();
}
if(CheckPointer(up_list_Ptr) != POINTER_INVALID)
{
up_list_Ptr.Clear();
}
if(CheckPointer(dn_list_Ptr) != POINTER_INVALID)
{
dn_list_Ptr.Clear();
}
// Determine the bar number of the beginning of calculations:
if(MaxBars > 0 && rates_total > MaxBars)
{
start = rates_total - MaxBars;
}
else
{
start = 0;
}
time_prev = 0;
trend_prev = 0;
}
如果需要重新计算整个指标,则我们清除趋势线列表以及顶部/底部列表。此外,这些图形对象也将从图形上删除。然后我们确定需要从其开始计算指标的柱的编号 - start 变量。如果 MaxBars 外部变量的值大于零且小于 rates_total 图形上的柱的数量,则计算开始的起始柱将等于:
start = rates_total - MaxBars;
回想一下,按时间序列的柱的索引编排从零(最近的柱)开始。
如果 MaxBars 外部变量的值等于零(表示赫兹需要在图形的所有柱上计算指标)或大于图形上柱的数量,则赫兹需要针对图形上所有的柱重新计算指标所有的值,即 start 等于零。
接下来,是一个趋势线和次级顶部/底部位置的计算循环。按照上述规则分析柱的最大和最小价格值相当简单,无需进一步解释(请参见 GannMicroTrend.mq5 文件中的源文本)。
要绘制趋势线的区段,我们使用下面的简单函数:
//---------------------------------------------------------------------
// Drawing of a section
//---------------------------------------------------------------------
void CreateCut(datetime _dt1, double _prc1, datetime _dt2, double _prc2, color _clr, int _wd)
{
string name = GetUniqName(prefix + " ");
CChartObjectTrend* trend_obj = new CChartObjectTrend();
if(CheckPointer(trend_obj) != POINTER_INVALID)
{
trend_obj.Create(0, name, 0, _dt1, _prc1, _dt2, _prc2);
trend_obj.Color(_clr);
trend_obj.Width(_wd);
trend_list_Ptr.Add(trend_obj);
}
}
在此我们使用该函数以获得图形中唯一的名称 GetUniqName,这在《利用标准库类创建您自己的“市场报价”》一文中进行了详细介绍。在成功构建趋势线图形对象的情形下,它的参数是指定的(颜色和线宽),且该对象通过调用 CList::Add 方法添加至线列表。
要绘制次级顶部/底部的位置,我们分别使用 CreateUpStopLoss/CreateDnStopLoss 函数。这两个函数与 CreateCut 函数类似,将创建的对象添加到它们的列表。
完成计算后,显示出最新顶部和底部的参数。在此我们使用在上一步中创建的列表。我们获得这些已按照时间递增进行排序的列表,并通过调用 CList::GetLastNode 方法获得顶部或底部的最新对象。
所述指标的工作效果在下图中显示:
图 2. 小型趋势指标
2.1. 中型趋势
中期趋势的图形反映由两个柱描绘的市场的走势(双柱走势)。中期趋势的图形如图 2 中所示。我们简单地介绍一下构建中型趋势的图形的规则:
- 在向上走势中(下一个柱的最大值大于上两个柱走势的最大值,且下一个柱的最小值同样大于上两个柱走势的最小值),我们连接下一个柱的最大值和上两个柱走势的最大值,得到一条上升中型趋势线;
- 在向下走势中(下一个柱的最小值小于上两个柱走势的最小值,且下一个柱的最大值同样小于上两个柱走势的最小值),我们连接下一个柱的最小值和上两个柱走势的最小值,得到一条下降中型趋势线;
- 如果在向上走势期间,下一个柱的最大值不大于上两个柱走势的最大值,且与此同时下一个柱的最小值小于上两个柱走势的最小值,则走势方向变为向下;
- 如果在向上走势期间,下一个柱的最大值不大于上两个柱走势的最大值,且与此同时下一个柱的最小值小于上两个柱走势的最小值,则走势方向变为向下;
- 在任何走势期间,如果下一个柱的最小值大于上两个柱走势的最小值,且与此同时下一个柱的最大值小于上两个柱走势的最大值,则该柱为内侧柱(参见图 2)且不参与图形的构建;
- 如果在任何走势期间,下一个柱的最大值大于之前柱的最大值,且与此同时下一个柱的最小值小于之前柱的最小值,则该柱为外侧柱(参见图 2)。基于柱的方向(上升或下降),我们构建图形对应的区段。
图 3. 中型趋势
中型趋势上升反向的唯一指标是中型顶部价位交叉。同样地,中型趋势下降反向的唯一指标是中型底部价位交叉。
如果中型趋势为上升,且市场形成一个向下中型摆动但未冲掉中型摆动的上一个底部,则它是一个纠正。如果中型趋势为下降,且市场形成一个向上中型摆动但未冲掉中型摆动的上一个顶部,则它也是一个纠正。
2.2. GannMiddleTrend 指标
我们来开发一个显示中型趋势的图形的指标。它的外观应与图 2 所示的相同。此外,最新顶部和底部的位置应在屏幕上显示。该指标可在本文随附的 GannMiddleTrend.mq5 文件中找到。
在该指标中,针对图形元素的渲染,我们将使用指标缓冲区和绘制类型 DRAW_COLOR_SECTION。这些在我们稍后开发 EA 时会用到。要从 EA 访问该指标的数据,我们使用指标缓冲区。
屏幕上指标的显示参数通过下列指令指定:
#property indicator_buffers 2
#property indicator_plots 1
#property indicator_type1 DRAW_COLOR_SECTION
#property indicator_color1 LightGreen, LightPink
#property indicator_width1 4
在此我们按顺序指定:
- 指标缓冲区的数量(仅有两个 - 数据缓冲区和颜色索引缓冲区);
- 屏幕上显示的图形的数量;
- 指标图形的渲染类型(我们将使用彩色区段渲染);
- 可以在颜色索引缓冲区中指定的颜色;
- 指标图形的线宽;
在 OnInit 初始化函数中,我们将分派项与指标缓冲区结合,并指定其他一些指标参数。
这通过下述代码段实现:
SetIndexBuffer(0, DataBuffer, INDICATOR_DATA);
SetIndexBuffer(1, ColorBuffer, INDICATOR_COLOR_INDEX);
IndicatorSetInteger(INDICATOR_DIGITS, Digits( ));
IndicatorSetString(INDICATOR_SHORTNAME, "GannMiddleTrend");
PlotIndexSetString(0, PLOT_LABEL, "GannMiddleTrend");
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
在此我们按顺序指定:
- 在指标中用作数据缓冲区(用于在屏幕上渲染)的缓冲区;
- 用于指定指标图形区段的颜色的缓冲区;
- 指标值在屏幕上显示时的小数位数;
- 指标在屏幕上显示时的简称;
- 图形序列(数据缓冲区)在屏幕上显示时的名称;
- 屏幕上对应于空值的值(无渲染);
该指标与小型趋势指标 GannMicroTrend 在结构和外部参数上相似。区别在于,前者在指标缓冲区和颜色缓冲区中直接而不是使用图形对象分派值。因此,我们将跳过实施的细节。
指标工作的结果如下图所示:
图 4. 中型趋势指标
我们可以看到,该图形与图 3 中手工绘制的图形一样。
3.1. 主要趋势
主要趋势的图形反映由三个柱描绘的市场的走势(三柱走势)。它的外观应与图 5 所示的相同。我们简单地介绍一下构建主要趋势的规则:
- 在向上走势中(下一个柱的最大值大于上三个柱走势的最大值,且下一个柱的最小值同样大于上三个柱走势的最小值),我们连接下一个柱的最大值和上三个柱走势的最大值,得到一条上升主要趋势线;
- 在向下走势中(下一个柱的最小值小于上三个柱走势的最小值,且下一个柱的最大值同样小于上三个柱走势的最小值),我们连接下一个柱的最小值和上三个柱走势的最小值,得到一条下降主要趋势线;
- 如果在向上走势期间,下一个柱的最大值不大于上三个柱走势的最大值,且与此同时下一个柱的最小值小于上三个柱走势的最小值,则走势方向变为向下;
- 如果在向上走势期间,下一个柱的最大值不大于上三个柱走势的最大值,且与此同时下一个柱的最小值小于上三个柱走势的最小值,则走势方向变为向下;
- 对于任何走势,如果下一个柱的最小值大于上三个柱走势的最小值,且与此同时下一个柱的最大值小于上三个柱走势的最大值,则该柱为内侧柱(参见图 5)且不参与图形的构建;
- 如果在任何走势期间,下一个柱的最大值大于之前柱的最大值,且与此同时下一个柱的最小值小于之前柱的最小值,则该柱为外侧柱(参见图 5)。基于柱的方向(上升或下降),我们构建图形对应的区段。
图 5. 主要趋势
主要趋势上升反向的唯一指标是主要顶部价位交叉。同样地,主要趋势下降反向的唯一指标是主要底部价位交叉。
如果主要趋势为上升,且市场形成一个向下摆动但未冲掉上一个底部摆动,则它是一个纠正。如果主要趋势为下降,且市场形成一个向上摆动但未冲掉上一个顶部摆动,则它是一个纠正。
3.2. GannMainTrend 指标
我们来开发一个显示主要趋势的图形的指标。它的外观应与图 5 所示的相同。此外,最新顶部和底部的位置应在屏幕上显示。该指标位于本文随附的 GannMainTrend.mq5 文件中。
该指标在结构和外部参数上与 GannMiddleTrend 中型趋势指标并无二致,因此我们将不再对其实施细节进行赘述。指标工作的结果如下图所示:
图 6. 主要趋势指标
4. 在交易中运用摆动图表
至此,我们具备了以模型 - 小型趋势指标、中型趋势指标和主要趋势指标 - 分析市场的工具。现在,让我们试着基于价格摆动开发一个简单的交易系统。
James Hyerczyk 在其著作中提出以下策略:
- 识别具有长期下降(上升)走势的区段;
- 在形成停滞后,沿水平方向移动,市场在最新摆动的顶部上方下达买入订单(在最新摆动的底部下方下达卖出订单);
- 将保护性止损设置在最新摆动底部的正下方(最新摆动顶部的正上方);
- 建仓后,止损随价格移动,位于生成的新摆动的底部(顶部)的下方;
让我们通过图形进行说明:
图 7. 摆动交易
在极高的价格水平,建议考虑转向卖出持仓的可能性;在极低价位,建议考虑转向买入持仓的可能性。图 7 显示了中型趋势的图形。通过主要趋势图形,我们将识别具有长期走势的区段。通过中期趋势图形,我们将识别“停滞”市场区段。仓位将通过中型趋势图形来监控。
下面是同一区段的主要趋势的图形:
图 8. 主要趋势图表上的区段
长期下降走势(摆动)位于主要顶部 1.36913 和主要底部 1.18758 之间。该走势跨越 1815.5 四位数点。接下来是“停滞”市场区段,具有从 1.24664 到 1.21495 的几近水平的趋势(请参见图 7)。该走势跨越 316.9 点。之后,价格突破中型趋势图形上最新摆动的中型顶部的水平,然后向上。
我们设置初始止损位于中型底部和最新摆动 1.21495 的正下方,监控中型趋势图形上的仓位。作为此次交易的结果,我们将得到约 1.31186 - 1.23966 = 722.0 点的盈利。
5. 用于 MQL5 向导中的交易信号类
在设计实施我们的交易系统的代码前,让我们概要地想象一下交易的整个过程。让我们来看看下面的图解:
图 9. 买入持仓交易
买入仓位的调整包括以下操作:
- 根据主要趋势图形定义一个长期下降走势(图 9 中区段 A-B)。走势的持续时间和量级分别由参数 dT 和 dP 指定。这些参数的值应在研究该工具历史数据的基础上确定。
- 在主要趋势图形上形成主要底部后(图 9 中的 B 点),等待中型趋势图形上 C 点和 D 点的形成。这些点,连同 B 点,一起构成中型 B-C 和 C-D 摆动。如果这些摆动的持续时间和量级未超出指定值,则我们假设它们形成了一个水平趋势(或“停滞”市场)。
- 等待中型 C 顶部水平(或在 C 后形成的最近的中型顶部)的到达。突破后,在 D 点设置一个位于中型底部正下方的止损(或在 C 后形成的最近的中型底部)。
- 通过在形成的中型底部的下方(位于 F 点和 L 点正下方)移动止损,随着上升走势的继续监控仓位。
- 取决于资金管理的模式,我们可以在突破点、形成的中型顶部(G 点和 M 点)添加仓位交易量。
卖出仓位的调整所需的操作呈“镜像”对称。
因此,要构建“EA 交易”,我们需要以下组件:生成买入/卖出信号的模块;敞口仓位追踪止损的模块;以及可能的一个模块,负责“金字塔式交易”仓位(资金管理)。
在撰写此部分时,我参考了下述文章的内容:《MQL5 向导:如何创建交易信号模块》、《MQL5 向导:如何创建敞口仓位的追踪模块》以及《MQL5 向导:如何创建风险和资金管理模块》。
负责生成交易信号的模块的开发包括以下操作:
- 添加一条预处理程序指令以包含标准 mqh 文件,该文件包含所有用于生成“EA 交易”的基类。这通过以下方式实现:
#include <Expert\ExpertSignal.mqh>
在我们生成交易信号的模块的开头插入该指令。
- 以注释的形式添加的特殊字符串指示 MetaEditor 编辑器此文件应在生成 EA 时使用:
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class |
//| Title=Signal based on swings on charts |
//| of the middle and main trends according to Gann (iCustom) |
//| Type=Signal |
//| Name=TGannBreakSignal |
//| Class=TGannBreakSignal |
//| Page= |
//| Parameter=MinMainSwingContinuance,int,5 |
//| Parameter=MinMainSwingSize,double,300.0 |
//| Parameter=MinMiddleSwingContinuance,int,3 |
//| Parameter=MaxMiddleSwingSize,double,200.0 |
//| Parameter=OpenPriceSpace,double,5.0 |
//| Parameter=StopLossSpace,double,5.0 |
//+------------------------------------------------------------------+
// wizard description end
在此我们可以看到参数 Title,它用于指定在 EA 生成期间出现在列表 MetaEditor 中的模块的名称(请参见下述描述)。一个重要的参数 - Type,它用于确定指定模块为信号生成模块。同时,外部参数和它们的默认值出现。
所有这些代码行紧跟包含标准文件 ExpertSignal.mqh 的指令之后。
- 对基类的后代类的描述在文件 ExpertSignal.mqh 中进行。该类应实现一些在 CExpertSignal 基类中缺失的功能性。类的描述如下所示(部分不太重要的类未在下方列示):
class TGannBreakSignal : public CExpertSignal
{
private:
int min_main_swing_continuance; // minimum swing duration time of the main tren
double min_main_swing_size_points; // minimum swing amplitude on the chart of the main trend
int min_middle_swing_continuance; // minimum swing duration time on the chart of the middle trend
double max_middle_swing_size_points; // maximum swing amplitude of the chart of the middle trend
double open_price_space; // distance between the open price and peak/bottom
double stop_loss_space; // distance between the stop loss price and peak/bottom
datetime main_swing_lf_datetime; // time of left point of a swing on the chart of the main trend
double main_swing_lf_price; // price of left point of a swing on the chart of the main trend
datetime main_swing_rt_datetime; // time of right point of a swing on the chart of the main trend
double main_swing_rt_price; // price of right point of a swing on the chart of the main trend
int main_swing_continuance; // swing duration time on the chart of the main trend
double main_swing_size_points; // swing amplitude (in points) on the chart of the main trend
datetime middle_swing_lf_datetime; // time of left point of a swing on the chart of the middle trend
double middle_swing_lf_price; // price of left point of a swing on the chart of the middle trend
datetime middle_swing_rt_datetime; // time of right point of a swing on the chart of the middle trend
double middle_swing_rt_price; // price of right point of a swing on the chart of the middle trend
int middle_swing_continuance; // swing duration time on the chart of the middle trend
double middle_swing_size_points; // swing amplitude (in points) on the chart of the middle trend
int handle_main_swing;
int handle_middle_swing;
double main_swing_buff[];
double middle_swing_buff[];
datetime time_buff[];
double price_buff[];
public:
TGannBreakSignal(); // constuctor
~TGannBreakSignal(); // destructor
// Settings:
void MinMainSwingContinuance(int _cont);
void MinMainSwingSize(double _size);
void MinMiddleSwingContinuance(int _cont);
void MaxMiddleSwingSize(double _size);
void OpenPriceSpace(double _space);
void StopLossSpace(double _space);
int GetMainSwingContinuance(); // gets swing duration time on the chart of the main trend
double GetMainSwingSizePoints(); // gets swing amplitude (in 4-digit points) on the chart of the main trend
int GetMiddleSwingContinuance(); // gets swing duration time on the chart of the middle trend
double GetMiddleSwingSizePoints(); // gets swing amplitude (in 4-digit points) on the chart of the middle trend
// overloaded methods of the CExpertSignal class:
virtual bool ValidationSettings();
virtual bool CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration);
virtual bool CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration);
virtual bool InitIndicators(CIndicators *indicators);