下载MetaTrader 5

在 MQL5 中寻找趋势的几种方法

8 十月 2013, 09:18
Dmitriy Skub
0
2 722

简介

所有交易员都知道一个规则:”趋势是您的好朋友,要跟着它走。“但是到底什么是趋势,这却是仁者见仁智者见智的问题。几乎每位交易员都听说过一些可怕的故事,这些故事的中心思想就是:逆趋势而行者,亡。

任何一位交易人都有可能面临准确把握趋势的好机会。也许这就是每个人都想找到的万能圣杯。在本文中,我们将讨论几个判断趋势的方法。更准确地说,是如何通过 MQL5 方法,制定几个经典的趋势判断程序。


1. 什么是趋势,为什么要知道趋势

首先,让我们来介绍一下趋势的广义概念。

趋势,是市场价格变化的长期倾向(方向)。趋势的这个广义概念会产生几个结果:

  • 价格变化的方向以时间周期为其基础,考虑的是价格时间序列。
  • 价格变化的方向需要选择一个参考点,从这个点开始时间序列的分析,判断趋势。

让我们用图形表示这个概念:

图 1. 趋势分析

图 1. 趋势分析

让我们来看这个图,您会发现从 2005 年末至 2006 年 5 月,整体的趋势为上涨(图中绿色箭头)。但是,当我们把实现缩短到图中更短的时间周期,就会发现 2006 年 2 月的趋势明显为下降(图中红色箭头),而几乎整个 1 月的价格都处于盘整期(黄色箭头)。

因此,在您判断趋势之前,首先要决定您想要选择的时间周期。对于交易而言,时间周期最重要的作用,就是决定在市场中从建仓到平仓的整个持仓时间。此外,还会决定您的保护性止损和预计平仓操作,以及交易操作频率。

本文的目的,就是帮助交易新手娴熟运用 Meta Trader 5 平台的趋势判断工具。本文还为您提供关于编写简单指标的基础知识,让这个程序自动运行。其终极目标就是编写简单的 EA 交易,使用这些指标进行自动交易。  

我们将以欧元美元日线图(1 天时间周期)为例,这是 Forex 市场中流动性最强的工具。该时间周期的持仓时间可以从几天到几个月不等。相应地,目标就是达到几百甚至几千的点位,在几百个点位之外的地方设置保护性止损。

广义而言,以下所有描述都可以用于任何一个时间周期。但要记住的是,图表时间周期越小,市场噪音对交易的影响就越大。所谓市场噪音是影响市场波动的新闻、大交易者的市场操作和其他因素。

理论上而言,趋势的时间越长,其变化的可能性就越小,那么,当您进行趋势交易的时候,赚钱的机会就比赔钱的机会更大。现在,您必须理解如何判断价格图中的趋势。我们将在本文中讨论这个问题。


2. 如何判断趋势

以下是判断趋势的一些已知方法:

  1. 移动平均线
  2. 峰谷分析
  3. 平均趋向指标 (ADX)
  4. NRTR 指标
  5. Heiken Ashi 烛图的颜色

我们接下来将对所有这些方法的优势和劣势进行分析,然后与历史同期表现进行比较。

2.1. 使用移动平均线判断趋势

也许,这是判断趋势及其方向的最简单办法 - 使用移动平均线。移动平均线是技术分析的第一个工具,目前有很多应用中的变体,是大部分指标的基础。交易人即使用一条移动平均线,也使用由几条移动平均线组成的“扇形线”。

让我们先介绍单一移动平均线的简单原则:
  • 趋势上涨在指定时间周期中,柱的收盘价位于移动平均线的上方
  • 趋势下跌在指定时间周期中,柱的收盘价位于移动平均线的下方

在这种情况下,当价格在移动平均线附近上下浮动时(所谓的“震荡的”),我们将使用柱的收盘价,减少“错误的”趋势变化的次数。

让我们用图形解释这个方法:

图 2. 使用移动平均线判断趋势

图 2. 使用移动平均线判断趋势

在这里,我们使用 EURUSD D1 日线图和简单的 200 天移动平均线图,以收盘价为基础(图中的红线)。在图形的底部,您可以看到特别开发的趋势指标 - MATrendDetector。趋势方向由指标柱形图与零轴相对的位置所显示。+1 对应上涨趋势。-1 下跌趋势。我们将对它和本文中使用的其他指标进行讨论。

您可以看到,当柱的收盘价高于/低于移动平均线,价格接下来经常都会向反方向移动,也就是说,这个方法会给出很多错误信号。这就是为什么 EA 交易和各种指标很少使用这种方式,它被认为是一个非常“粗糙“的趋势过滤器。

2.2. 使用三条移动平均线判断趋势

那么,如何改善移动平均线的趋势判断准确度呢?比如,您可以使用两根或更多不同时间周期的指标平均线。那么,无论是多少根不同时间周期的移动平均线(超过一根),判断趋势的规则都如下所示:

  • 趋势上涨某一时间周期中,所有移动平均线的柱收盘价都按照正确的顺序显示为上扬。
  • 趋势下跌某一时间周期中,所有移动平均线的柱收盘价格都按照正确的顺序显示为下降。

我们使用的术语是:

  • 正确上涨顺序 - 移动平均线的周期越长,其位置就应该越高
  • 正确下跌顺序 - 移动平均线的时间越长,其位置就应该越低

这种“平均线正确顺序”又被称为平均线扇形图向上/向下开口,因为它看起来确实如其所述。

让我们用图形解释这个方法:

图 3. 使用多条移动平均线判断趋势

图 3. 使用多条移动平均线判断趋势

我们将使用 EURUSD D1 日线图和简单的 200 天移动平均线(粗红线)、50 天(中粗黄线)和 21 天(紫色细线),以收盘价为基准。

在图标的下方是特别开发的趋势指标 - FanTrendDetector。趋势方向由指标柱形图与零轴相对的位置所显示。+1 对应上涨趋势。-1 下跌趋势。如果柱形图的值等于零,就意味着无法判断趋势。还有用于比较的 MATrendDetector 指标。

很显然,这样做可以减少趋势改变警告的数量。但是会增加趋势判断延后的可能性。这是可以理解的,所有移动平均数排列成“正确”顺序,这需要些时间。至于它的优势和劣势?这要看使用这些方法的交易系统而定。

在这个例子中,移动平均线的周期值只是随意选择,但这的确是交易人以及作者本人经常使用的方式。通过选择一系列的移动平均线和它们的数值,您就能为某一货币对改善这种趋势判断方法的特征。

2.3. 使用峰谷指标的高点和低点判断趋势

现在,让我们采用典型的技术分析方法来判断趋势。也就是说,我们会使用以下的 Charles Dow 规则:

  • 趋势上涨如果在价格图中,下一个局部高点始终都高于前一个局部高点,每个局部低点也都高于之前的局部低点。
  • 趋势下跌如果在价格图中,下一个局部高点始终都低于前一个局部高点,每个局部低点也都低于之前的局部低点。

我们将通过峰谷指标的顶/底部来寻找局部高点/低点。

让我们用图形解释这个方法:

图 4. 利用峰谷指标判断趋势

图 4. 利用峰谷指标判断趋势

我们将使用的 EURUSD D1 图和峰谷指标参数为:ExtDepth = 5ExtDeviation = 5ExtBackstep = 3

在图标的下方是特别开发的趋势指标 - ZigZagTrendDetector

这种趋势判断方法存在的主要问题是:在实时情况中无法判断极点是否已经形成。当您查看历史图形时可以很容易找到极点,您知道它在什么地方形成。但是在价格实时变化的过程中,已经形成的极点可能会忽然消失,或者忽然又出现。想要了解这一点,可以看看在 EA 交易的模拟测试模式中的峰谷线走向。

由于存在这个缺点,这种方法在实际交易中没有太大用处。但对历史数据的技术分析很有帮助,可以以此找出规律,分析不同交易系统的质量。

2.4. 使用 ADX 指标判断趋势

以下要讨论的是使用 ADX (平均趋向)指标判断趋势的方法。这种指标不仅用于判断趋势走向,还可评估趋势强度。这是 ADX 指标的一个重要价值。趋势强度由 ADX 主线来决定 - 如果值大约 20(普遍接受的水平,但是目前不是最佳的选择),那么趋势就足够强。

趋势方向由 +DI 和 -DI 线之间的相互关系决定。这个指标以指数平均法对这三条线进行柔化,因此对趋势变化的反应会相对滞后。

让我们用图表来解释这种趋势判断方法:

  • 趋势上涨,如果 +DI 线高于 -DI 线。
  • 趋势下跌,如果 +DI 线低于 -DI 线。

本例并未使用 ADX 趋势线来判断趋势。需要减少这种指标的错误信号数量。如果趋势比较弱(ADX 小于 20),最好等到趋势变强的时候再进行趋势交易。

让我们用图形解释这个方法:

图 5. 使用 ADX 指标判断趋势

图 5. 使用 ADX 指标判断趋势

我们在这里使用的 EURUSD D1 图和 ADX 指标参数为:PeriodADX = 21(蓝色粗线 - ADX 趋势强度值,细绿线 - +DI 值,细红线 - -DI 值)。

在图下方是特别开发的趋势指标 - ADXTrendDetector。为了便于对照,ADXTrendDetector 上方图表(深红色)中未启动趋势强度过滤器,(ADXTrendLevel = 0),下方图表(蓝色)则启用了这个功能(ADXTrendLevel = 20)。

请注意,当我们打开趋势强度过滤器,趋势走向判断中所谓的“反弹”部分已经被去掉。这种过滤器比较适合在实际交易中应用。根据货币对走向的性质,巧妙地根据市场当前情况选择外部参数(平仓/幅度/趋势),可以更好地改善指标的质量。

通常,这个指标会带来一个好机会,构建一个作为输入过滤器的趋势追踪交易系统。

2.5. 使用 NRTR 指标判断趋势

以下趋势判断方法使用了NRTR(趋势转折)指标。这种指标始终都与目标价极点(上升趋势中的较低价和下跌趋势中的较高价格),保持着不变的距离。这种指标的主要理念是:应该忽视主要趋势中一些小的纠正移动,当移动相对于主趋势超过了某个程度,就是趋势改变的信号。

这个理念中可以得出判断趋势方向的原则:

  • 趋势上涨 - 如果指标线的收盘价与上升趋势相一致。
  • 趋势下跌 - 如果指标线的收盘价与下降趋势相一致。

为了减少错误趋势反转对价格波动所产生的影响,我们会使用收盘价来检测 NRTR 线的位置。

让我们用图形解释这个方法:

图 6. 使用 NRTR 指标判断趋势

图 6. 使用 NRTR 指标判断趋势

蓝色大点对应上涨趋势,红色大点对应下跌趋势。在图表下方显示了我们的趋势指标NRTRTrendDetector

2.6. 使用三值 Heiken Ashi 烛图判断趋势

判断趋势另外一个常用方法就是 Heiken Ashi 烛图。Heiken Ashi 烛图是经过改进的日本烛图。它们的值与之前的烛柱进行部分平均。

让我们用图形解释这个方法:

图 7. 以 Heiken Ashi 烛图颜色判断趋势

图 7. 以Heiken Ashi 烛图颜色判断趋势

如您所见,当价格在单向通道中波动时,这种方法仍然不能避免出现错误信号。但更大的问题是,这个质变不仅能改写上一个柱,还会改写倒数第二个柱,也就是说,我进行输入的信号可能在下一个柱出现反转。这是因为当柱体的颜色确定后,就会分析两个柱,所以建议将这种方法与其他辅助信号结合使用。


3. 趋势指标

现在,让我们来创建趋势指标。

3.1. 根据移动平均线创建的趋势指标

根据移动平均线可以设计出最简单的指标,也是判断趋势最简单的方法。首先让我们来看看它都包括哪些部分。指标的全部源代码都在本文所附的 MATrendDetector.MQ5 文件之中。

在指标程序之初就是线,与库连接,计算不同的移动平均值。安装客户端就可以随时使用这个库。线如下所示:

#include <MovingAverages.mqh>
    

我们将使用其中的一个函数,计算一个简单的移动平均指标:

double SimpleMA(const int position, const int period, const double &price[])
    

下面是如何定义输入参数:

  • position - 在 price[] 数组中的初始指数,以此开始计算。
  • period - 移动平均指数的周期,必须大于零。
  • price[] - 数组,包括将指标放置在表格过程中的具体价格区间。默认 Close[] 即使用柱收盘价。

函数返回移动平均线已计算出的值。

文本的下一部分中,包括在屏幕上显示指标的初始设置:

//---------------------------------------------------------------------
#property indicator_separate_window
//---------------------------------------------------------------------
#property indicator_applied_price       PRICE_CLOSE
#property indicator_minimum             -1.4
#property indicator_maximum             +1.4
//---------------------------------------------------------------------
#property indicator_buffers             1
#property indicator_plots               1
//---------------------------------------------------------------------
#property indicator_type1               DRAW_HISTOGRAM
#property indicator_color1              Black
#property indicator_width1              2
//---------------------------------------------------------------------
设置以下参数:
  • #property indicator_separate_window将让 MetaTrader 5 终端在独立窗口中显示指标图表
  • #property indicator_applied_price    PRICE_CLOSE - 默认的价格类型。
  • #property indicator_minimum   -1.4 - 纵轴最小值,在指标窗口中显示。
  • #property indicator_minimum   +1.4 - 纵轴最大值,在指标窗口中显示。

最后两个参数可以让您设置一个固定的标尺显示指标图表。之所以能过这样做,是因为我们知道我们指标的最大值和最小值 - 包括从 -1 到 +1。这样做是为了让图表更好看,而不是为了在窗口中重叠窗口边框和指标名称。

  • #property indicator_buffers   1 - 指标计算的缓冲区数量。我们只使用一个缓冲区。
  • #property indicator_plots       1 - 在指标中的图形系列数量。我们在屏幕中只显示一个图表。
  • #property indicator_type1     DRAW_HISTOGRAM - 将指标图表显示为柱形图。
  • #property indicator_color1     Black - 指标图表的默认颜色。
  • #property indicator_width1    2 - 指标图表的线宽,在本例中为柱形图的柱宽。

接下来是输入指标的外部参数,当我们在图中设置指标、以及之后指标生效的过程中,可以对其进行修改:

input int   MAPeriod = 200;
    

这里只有一个参数 - 移动平均线周期值。

指标下一个重要部分就是函数,处理指标在图中生效时的各种事件

第一个是初始化函数 - OnInit()。加载指数后立刻调用这个函数。在我们的指数中,它如下所示:

void OnInit()
{
  SetIndexBuffer( 0, TrendBuffer, INDICATOR_DATA );
  PlotIndexSetInteger( 0, PLOT_DRAW_BEGIN, MAPeriod );
}

SetIndexBuffer() 指数将之前的数组声明绑定,我们将用一个指标缓冲区,把趋势值 TrendBuffer[] 存储在其中。我们只有一个指标缓冲区,其指标等于零。

PlotIndexSetInteger() 函数设置了初始柱的数量,但不会在指标窗口中将其画出。

如果柱的数量少于其周期,就很难用数学方法计算出一个简单的移动平均线,让我们确定柱的数量,将其设置为等于移动平均线周期。

下一个函数是 OnCalculate(),它处理的是重新计算一个指标需求的事件:

int OnCalculate(const int _rates_total, 
                const int _prev_calculated,
                const int _begin, 
                const double& _price[ ] )
{
  int  start, i;

//   如果屏幕上的柱数少于平均周期,计算无法进行:
  if( _rates_total < MAPeriod )
  {
    return( 0 );
  }

//  确定指标缓冲区计算的初始柱:
  if( _prev_calculated == 0 )
  {
    start = MAPeriod;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//      循环计算指标缓冲区数值:
  for( i = start; i < _rates_total; i++ )
  {
    TrendBuffer[ i ] = TrendDetector( i, _price );
  }
  return( _rates_total );
}
在指标初始化之后会第一次调用这个函数,每当价格数据改变一次都会调用一次。比如,当已计算的信号有了一个新跳动。让我们来具体讨论它。

首先,让我们来看看在图上是否有足够的柱数量 - 如果少于运动平均线的周期,这没有可以计算的内容,这个函数以 return 返回运算符结束。如果柱数量足够进行计算,确定初始柱,从这里开始计算指标。这样做,是为了在每次价格跳动时,不要重新计算所有指标值。

我们在这里使用的是终端提供的机制。每次当您调用一个处理函数,检查 _prev_calculated 函数自变量值 - 即柱数量,这个值在之前调用的 OnCalculate() 函数中进行处理。如果值的数量为零,则重新计算所有指标值。否则,就用 _prev_calculated - 1. 索引只重新计算最后一个柱。

计算指标缓冲区值的循环由 for 运算符执行 - 为了每一个已重新计算的指标缓冲区值,我们调用其主体中的趋势判断函数 TrendDetector。因此,只覆写这个函数,我们就能为计算趋势方向实施不同的计算式。在本例中,指标其他部分事实上并没有改变(外界参数可能正在改变)。

现在,让我们来讨论一下趋势判断函数自身 - TrendDetector

int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma;
  int     trend_direction = 0;

  current_ma = SimpleMA(_shift, MAPeriod, _price);

  if(_price[_shift] > current_ma)
  {
    trend_direction = 1;
  }
  else if(_price[_shift] < current_ma)
  {
    trend_direction = -1;
  }

  return(trend_direction);
}
这个函数执行以下任务:
  • 计算简单的移动平均线,从柱开始,由 _shift 自变量设置。它使用了库函数 SimpleMA
  • 用这个柱的价格值比较移动平均值。
  • 如果价格值超过移动平均值,则返回 1,如果价格平均值小于移动平均值,则返回 -1,否则会返回零。

如果函数返回零,它意味着不可能判断趋势。

指标设置的结果见 图 2图 3

3.2. 根据移动平均“扇形图”创建趋势指标

现在让我们看看,如何在这个指标的基础上创建一个比较复杂的指标,用移动平均线“扇形图”判断趋势。

指标的全部源代码都在本文所附的 FanTrendDetector.MQ5 文件之中。

这个指标与之前指标的差别为:

  • 在外部参数中设置三个移动平均周期:
input int MA1Period = 200; // 长期移动平均的周期数
input int MA2Period = 50;  // 中期移动平均的周期数
input int MA3Period = 21;  // 短期移动平均的周期数
  • 另外一个 TrendDetector 函数:
int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma1, current_ma2, current_ma3;
  int     trend_direction = 0;
  current_ma1 = SimpleMA(_shift, MA1Period, _price);
  current_ma2 = SimpleMA(_shift, MA2Period, _price);
  current_ma3 = SimpleMA(_shift, MA3Period, _price);
  if(current_ma3 > current_ma2 && current_ma2 > current_ma1)
  {
    trend_direction = 1;
  }
  else if(current_ma3 < current_ma2 && current_ma2 < current_ma1)
  {
    trend_direction = -1;
  }
  return(trend_direction);
}
这个函数所检查的是,通过使用 if...else 运算符和它们的顺序,对这三者之间进行比较,检查移动平均指标是否按照正确的顺序排列。如果这些平均线以增加的顺序排列,则返回 1 - 上升。如果这些平均线以减少的顺序排列,则返回 -1 - 下降。在这两种情况下,检查 if 代码块,如果为 false,则返回零(无法判断趋势)。函数有两个输入自变量 - 分析柱缓冲区以及带有价格系列缓冲区自身的位移。

指标其他部分与上一个指标相同。

3.3. 根据峰谷指标创建的趋势指标

现在,让我们来看看这个指标,使用峰谷区间寻找极点,根据 Charles Dow 规则判断趋势方向。指标的全部源代码都在本文所附的 ZigZagTrendDetector.MQ5 文件中。

外部变量和将峰谷外部指标的参数值一起进行赋给:

//---------------------------------------------------------------------
//  外部参数:
//---------------------------------------------------------------------
input int   ExtDepth = 5;
input int   ExtDeviation = 5;
input int   ExtBackstep = 3;
//---------------------------------------------------------------------
这个指标的一个重要不同之处就是指标缓冲区的数量。这里除了显示缓冲区,我们还要再使用两个计算缓冲区。因此,我们在指标代码中修改了相应的设置:
#property indicator_buffers  3
    

添加两个缓冲区。它们将储存从外部指标 ZigZag 处获得的极点:

double ZigZagHighs[];  // zigzag 的上方拐点
double ZigZagLows[];   // zigzag 的下方拐点

还需要修改指标初始化事件处理程序,将这两个新增缓冲区设置为计算缓冲区:

//  储存 zigzag 拐点的缓冲区
SetIndexBuffer(1, ZigZagHighs, INDICATOR_CALCULATIONS);
SetIndexBuffer(2, ZigZagLows, INDICATOR_CALCULATIONS);

OnCalculate 函数的计算代码中,还必须在我们的缓冲区中提供读取峰谷区间。如下所示操作:

//  把zigzag的上方和下方拐点复制到缓冲区:
  CopyBuffer(indicator_handle, 1, 0, _rates_total - _prev_calculated, ZigZagHighs);
  CopyBuffer(indicator_handle, 2, 0, _rates_total - _prev_calculated, ZigZagLows);
//  循环计算指标缓冲区值:
  for(i = start; i < _rates_total; i++)
  {
    TrendBuffer[i] = TrendDetector(i);
  }

TrendDetector 函数看起来如下所示:

//---------------------------------------------------------------------
//  确定当前趋势方向:
//---------------------------------------------------------------------
//  返回值:
//    -1 - 下跌趋势
//    +1 - 上涨趋势
//     0 - 趋势没有确定
//---------------------------------------------------------------------
double    ZigZagExtHigh[2];
double    ZigZagExtLow[2];
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int    trend_direction = 0;

//  寻找zigzag的最后四个拐点:
  int    ext_high_count = 0;
  int    ext_low_count = 0;
  for(int i = _shift; i >= 0; i--)
  {
    if(ZigZagHighs[i] > 0.1)
    {
      if(ext_high_count < 2)
      {
        ZigZagExtHigh[ext_high_count] = ZigZagHighs[i];
        ext_high_count++;
      }
    }
    else if(ZigZagLows[i] > 0.1)
    {
      if(ext_low_count < 2)
      {
        ZigZagExtLow[ext_low_count] = ZigZagLows[i];
        ext_low_count++;
      }
    }

//  如果找到两对极值,退出循环:
    if(ext_low_count == 2 && ext_high_count == 2)
    {
      break;
    }
  }

//  如果所需的极值数没有找到,趋势无法确定:

  if(ext_low_count != 2 || ext_high_count != 2)
  {
    return(trend_direction);
  }

//  实施指标趋势方向条件检查:
  if(ZigZagExtHigh[0] > ZigZagExtHigh[1] && ZigZagExtLow[0] > ZigZagExtLow[1])
  {
    trend_direction = 1;
  }
  else if(ZigZagExtHigh[0] < ZigZagExtHigh[1] && ZigZagExtLow[0] < ZigZagExtLow[1])
  {
    trend_direction = -1;
  }
  return(trend_direction);
}
    

在这里,我们搜索前 4 个峰谷极点。注意,搜索将在历史数据中进行。所以,在 for 循环中的索引会随着每次搜索重复降至零。如果找到极点,根据 Dow 规则对这些极点进行对比,实现趋势定义的一致性。有两个可能的极点位置,上升极点和下降极点。用 if...else 运算符对这些变量进行检查。

3.4. 根据 ADX 指标创建的趋势指标

让我们来看看 ADXTrendDetector 趋势指标,它使用的是 ADX 指标。指标的全部源代码都在本文所附的 ADXTrendDetector.MQ5 文件中。将外部 ADX 指标的参数值赋给外部变量:

//---------------------------------------------------------------------
//      外部参数
//---------------------------------------------------------------------
input int  PeriodADX     = 14;
input int  ADXTrendLevel = 20;

TrendDetector 函数的形式为:

//---------------------------------------------------------------------
//  确定当前趋势方向:
//---------------------------------------------------------------------
//  返回值:
//    -1 - 下跌趋势
//    +1 - 上涨趋势
//     0 - 趋势没有确定
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  ADXBuffer[ 1 ];
  double  PlusDIBuffer[ 1 ];
  double  MinusDIBuffer[ 1 ];

//  把 ADX 指标值复制到缓冲区:
  CopyBuffer(indicator_handle, 0, _shift, 1, ADXBuffer);
  CopyBuffer(indicator_handle, 1, _shift, 1, PlusDIBuffer);
  CopyBuffer(indicator_handle, 2, _shift, 1, MinusDIBuffer);

//  如果考虑 ADX 值  (趋势强度):
  if(ADXTrendLevel > 0)
  {
    if(ADXBuffer[0] < ADXTrendLevel)
    {
      return(trend_direction);
    }
  }

//  检查 +DI 和 -DI 的相对位置:
  if(PlusDIBuffer[0] > MinusDIBuffer[0])
  {
    trend_direction = 1;
  }
  else if(PlusDIBuffer[0] < MinusDIBuffer[0])
  {
    trend_direction = -1;
  }
  return( trend_direction );
}
    

使用 CopyBuffer(),为计算柱数量,从外部指标 ADX 处获得所需的缓冲区指标值,由 _shift 自变量提供。下一步,分析这几条 +DI 和 -DI 线的相对位置。在必要的情况下,考虑趋势强度 - 如果比定义值小,就未判断出趋势。

3.5. 根据 NTRT 指标创建的趋势指标

NRTRTrendDetector 趋势指标的结构以 NRTR 为基础,与之前的指标比较类似。指标的全部源代码都在本文所附的 NRTRTrendDetector.MQ5 文件中。

第一个差别 - 在于外部参数的代码块:
 
//---------------------------------------------------------------------
//      外部参数:
//---------------------------------------------------------------------
input int     ATRPeriod =  40;    // ATR 周期数, 以柱为单位
input double  Koeff     = 2.0;    // ATR 数值改变系数   
//---------------------------------------------------------------------

第二个差别 - 在于判断趋势方向的 TrendDetector 函数:

//---------------------------------------------------------------------
//      确定当前趋势方向:
//---------------------------------------------------------------------
//  返回值:
//    -1 - 下跌趋势
//    +1 - 上涨趋势
//     0 - 趋势没有确定
//---------------------------------------------------------------------

int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  Support[1];
  double  Resistance[1];

//      把 NRTR 指标值复制到缓冲区:
  CopyBuffer(indicator_handle, 0, _shift, 1, Support);
  CopyBuffer(indicator_handle, 1, _shift, 1, Resistance);

//  检查指标线的值:
  if(Support[0] > 0.0 && Resistance[0] == 0.0)
  {
    trend_direction = 1;
  }
  else if(Resistance[0] > 0.0 && Support[0] == 0.0)
  {
    trend_direction = -1;
  }
  return( trend_direction );
}
    

在这里,我们用索引 0 和 1 读取外部指标 NRTR 两个缓冲区的值。上升时,Support 缓冲区中的值不等于零,下跌时,Resistance 缓冲区中的值不等于零。

3.6. 根据 Heiken Ashi 烛图创建趋势指标

现在,让我们使用 Heiken Ashi 烛图来设置趋势指标。

在本例中,我们不会调用外部指标,但会计算烛柱。这将改善指标性能,让 CPU 可以进行更重要的任务。指标的全部源代码都在本文所附的 HeikenAshiTrendDetector.MQ5 文件之中。

由于 Heiken Ashi 指标不能假定外部参数,我们可以移除 input 运算符的代码块。然后在运算符再计算事件的处理程序中进行主要的修改。在这里,我们将使用一个处理程序的替换变量,访问当前图表的所有价格数组。

现在,OnCalculate() 函数的样子如下所示:

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& TickVolume[],
              const long& Volume[], 
              const int& Spread[])
{
  int     start, i;
  double  open, close, ha_open, ha_close;

//  确定指标缓冲区计算的初始柱:
  if(_prev_calculated == 0)
  {
    open = Open[0];
    close = Close[0];
    start = 1;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//  循环计算指标缓冲区值:
  for(i = start; i < _rates_total; i++)
  {

//  Heiken Ashi 蜡烛柱开盘价:
    ha_open = (open + close) / 2.0;

//  Heiken Ashi 蜡烛柱收盘价:
    ha_close = (Open[i] + High[i] + Low[i] + Close[i]) / 4.0;
    TrendBuffer[i] = TrendDetector(ha_open, ha_close);
    open = ha_open;
    close = ha_close;
  }
  return(_rates_total);
}
当我们决定 Heiken Ashi 烛柱颜色时,只需要两个价格 - 开盘价和收盘价,然后只计算这两个值。

当我们通过 TrendDetector 函数调用判断趋势方向后,将 Heiken Ashi 烛图当前的价格值保存在 openclose 中间变量中。TrendDetector 函数看起来很简单。您可以将其插入 OnCalculate,但这样做的目的,是为了在计算式有了进一步发展的情况下,可以让这个函数获得更多的功能和复杂性。这个函数如下:

int TrendDetector(double _open, double _close)
{
  int    trend_direction = 0;
  if(_close > _open)         // 如果蜡烛柱增长,说明是上涨趋势
  {
    trend_direction = 1;
  }
  else if(_close < _open)     // 如果蜡烛柱下降,说明是下跌趋势
  {
    trend_direction = -1;
  }
  return(trend_direction);
}
函数的自变量是 Heiken Ashi 烛图的两个价格,开盘价和收盘价,这两个价格将决定其方向。


4. 在 EA 交易中使用趋势判断指标的举例

让我们创建一个使用不同指标的 EA 交易。对比使用不同趋势判断方式创建的 EA交易,是一件很有趣的事情。首先,用默认参数检查结果,然后试着对其进行调整,找到最佳参数。

在本例中,创建 EA 交易的目的是对比各趋势判断方法的准确性和速度。因此,让我们先简要介绍一下创建所有 EA 交易的总原则:

  • 建买仓当趋势从下跌改变为上涨,或从不确定改变为上涨。
  • 建卖仓当趋势从上涨改变为下跌,或从不确定改变为下跌。
  • 平仓趋势向相反方向反转或无法判断。
  • EA 交易必须建/平一个仓位当一个新柱开盘(当有了相应的信号)时必须这样做。

我们所创建的所有趋势指标都包含零索引指标缓冲区,在其中储存了趋势方向所需要的数据。我们将在 EA 交易中使用它获得一个开/平仓的信号。

由于我们需要交易函数,所以我们在其中纳入了相应的库,与 MetaTrader 5 一起安装。这个库包含了 CTrade 课程和处理仓位和订单的几种方法。用交易函数简化日常工作量。库包含在以下线中:

#include <Trade\Trade.mqh>
我们将使用它的两种方法:建仓和平仓。第一个方法可以让您进行指定方向和交易量的建仓:
   PositionOpen(const string symbol, 
             ENUM_ORDER_TYPE order_type,
             double volume, double price,
             double sl, double tp, const string comment )
输入自变量如下显示:
  • symbol - 交易工具的类型,比如 "EURUSD"。
  • order_type - 短线或长线建仓的方向。
  • volume - 所建仓位以手数定义的量,比如 0.10。
  • price - 建仓价格。
  • sl - 止损价格。
  • tp - 获利价格。
  • comment - 注释,当交易终端中显示仓位时出现。

第二个方法允许您平一个仓位:

PositionClose( const string symbol, ulong deviation )


输入自变量如下显示:
        
  •  symbol - 交易工具的类型,比如 "EURUSD"。
  •  deviation - 平仓时与当前价格之间的最大许可方差(点数)。

让我们了来看看使用 MATrendDetector 指标的 EA 交易结构。EA 交易的全部源代码见本文所附的 MATrendExpert.MQ5 文件。第一个主要的 EA 交易代码块,是设置外部参数的代码块。

input double Lots = 0.1;
input int    MAPeriod = 200;

EA 交易的 Lots 参数 - 手数的大小,在建仓时使用。为了获得不同趋势判断方法的比较结果,我们使用了不需要资金管理的固定手数。趋势指标使用的所有其他外部参数,如上文所述。列表和目标与相应的指标完全一致。

EA 交易的第二个重要代码块 - EA 交易初始化的事件处理程序。

//---------------------------------------------------------------------
//      初始化事件处理函数:
//---------------------------------------------------------------------
int OnInit()
{
//  创建外部指标句柄用于将来引用:
  ResetLastError();
  indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Examples\\MATrendDetector", MAPeriod);
// 如果初始化没有成功,返回非零值:
  if(indicator_handle == INVALID_HANDLE)
  {
    Print("MATrendDetector 初始化错误, 代码 = ", GetLastError());
    return(-1);
  }
  return(0);
}
这里的创建处理是为了调用趋势指标,如果创建成功,就返回零代码。如果创建指标处理失败(比如,指标不适用于 EX5 格式),我们将这个信息打印出来,然后返回非零代码。在本例中,EA 交易停止了进一步的工作,与日志中相应的信息一起,从终端卸载。

EA 交易的第二个代码块 - EA 交易去初始化时间处理程序

//---------------------------------------------------------------------
//      指标去初始化事件处理函数:
//---------------------------------------------------------------------
void OnDeinit(const int _reason)
{
//  删除指标句柄:
  if(indicator_handle != INVALID_HANDLE)
  {
    IndicatorRelease(indicator_handle);
  }
}
在这里删除了指标处理,释放其分配的存储。

去初始化 EA 交易不需要任何其他操作。

接下来就是 EA 交易的主代码块 - 当前符号新跳动的事件处理程序。

//---------------------------------------------------------------------
//  当前交易品种新订单事件处理函数:
//---------------------------------------------------------------------
int    current_signal = 0;
int    prev_signal = 0;
bool   is_first_signal = true;
//---------------------------------------------------------------------
void OnTick()
{

//  等待一个新柱的开始:
  if(CheckNewBar() != 1)
  {
    return;
  }

//  获得建仓/平仓信号:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }

//  选择当前交易品种仓位:
  if(PositionSelect(Symbol()) == true)
  {

//  检查我们是否需要平掉一个反向仓位:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }

//  检查是否有买入信号:
  if(CheckBuySignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK ), 0, 0);
  }

//  检查是否有卖出信号:
  if(CheckSellSignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID ), 0, 0);
  }

//  保存当前信号:
  prev_signal = current_signal;
}
让我们来看看 EA 交易所使用的辅助函数。

首先,为了在图表上开盘一个新柱,我们的EA必须检查信号。这就需要使用 CheckNewBar 函数:

//---------------------------------------------------------------------
//  返回新柱的标志:
//---------------------------------------------------------------------
//  - 如果返回1,说明有一个新柱
//---------------------------------------------------------------------
int CheckNewBar()
{
  MqlRates  current_rates[1];
  ResetLastError();
  if(CopyRates(Symbol(), Period(), 0, 1, current_rates)!= 1)
  {
    Print("CopyRates 复制错误, 代码 = ", GetLastError());
    return(0);
  }
  if(current_rates[0].tick_volume>1)
  {
    return(0);
  }
  return(1);
}
新柱的存在由跳动量的值来决定。在开盘一个新柱时,其量在初始化时等于零(因为没有报价)。新跳动点的出现,大小就变成等于 1。

在这个函数中,我们将创建 current_rates[] 数组,这是 MqlRates 结构数组,包括一个元素,将当前价格和交易量的信息复制其中,然后检查跳动量的值。

在用当前符号设置的新跳动时间处理程序中,我们将按照以下的方式使用这个函数:

//  等待一个新柱的开始:
if(CheckNewBar()!= 1)
{
  return;
}
因此,新柱开盘,您就能获得一个关于当前趋势方向的信号。如下所示操作:
//  获得建仓/平仓信号:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }
    

由于我们需要跟踪趋势中的变化,就需要记住之前柱的趋势值。在上述的一段代码中,我们将使用 prev_signal 变量。此外,我们还应该使用标志,标出这是第一个信号(之前没有信号)。这是 is_first_signal 变量。如果标志的值为 true,我们就用初始值对 prev_signal 变量进行初始化。

在这里,我们使用 GetSignal 函数,返回从我们的指标中获得的当前趋势方向。如下图所示:

//---------------------------------------------------------------------
//      取得建仓/平仓信号:
//---------------------------------------------------------------------
int GetSignal()
{
  double    trend_direction[1];

//  从趋势指标获得信号:
  ResetLastError();
  if(CopyBuffer(indicator_handle, 0, 0, 1, trend_direction) != 1)
  {
    Print("CopyBuffer 复制错误, 代码 = ", GetLastError());
    return(0);
  }
  return((int)trend_direction[0]);
}
趋势指标数据从零缓冲区复制到我们的 trend_direction 数组,其中包括一个元素。数组元素值从这个函数处返回。此外,为了避免出现编译器警告,从 double 类型转到 int 类型。

在建一个新仓位之前,您应该检查是否有必要平掉之前开设的对立仓位。还要检查在同样的方向中是否已经建了一个仓位。用以下这部分代码进行这个设置:

//  选择当前交易品种仓位:
  if(PositionSelect(Symbol()) == true)
  {
//  检查我们是否需要平掉一个反向仓位:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }
为了能够访问仓位,首先必须对其进行选择,使用当前符号的 PositionSelect() 函数进行这一操作。如果该函数返回为 true,那么仓位就是存在,已经对其进行成功选择,您就可以对其进行处理。

对立仓位的平仓,需要使用 CheckPositionClose 函数:

//---------------------------------------------------------------------
//  检查我们是否需要平仓:
//---------------------------------------------------------------------
//  返回值:
//    0 - 没有开启的仓位
//    1 - 已经有了信号同方向上的仓位
//---------------------------------------------------------------------
int CheckPositionClose(int _signal)
{
  long    position_type = PositionGetInteger(POSITION_TYPE);
  if(_signal == 1)
  {

//  如果已经有了买入仓位,则返回:
    if(position_type == (long)POSITION_TYPE_BUY)
    {
      return(1);
    }
  }
  if(_signal==-1)
  {

//  如果已经有了卖出仓位,则返回:
    if( position_type == ( long )POSITION_TYPE_SELL )
    {
      return(1);
    }
  }

//  平仓:
  CTrade  trade;
  trade.PositionClose(Symbol(), 10);
  return(0);
}
首先,检查在趋势方向中是否已经建仓。如果已建仓,函数返回 1,当前仓位未平。如果在对立趋势方向中已建仓,那么您必须将其平掉。采用上述的 PositionClose 方法进行设置。由于没有已建仓位,将返回零。

当完成了现有仓位所有必须的检查和操作,就需要检查是否存在新信号。用以下这部分代码进行这个设置:

//  检查是否有买入信号:
if(CheckBuySignal(current_signal, prev_signal)==1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK), 0, 0);
}
如果有买入信号,就用当前价格的 SYMBOL_ASK 建指定交易量的买入持仓。由于所有的仓位都用对立信号平仓,就不再使用获利和止损。EA交易”始终身处市场中“。 

在实际交易中,建议使用防御性的止损,以防止出现未预料的情况,比如与 DC 服务器失去联系以及其他不可抗力情况。

卖出信号的所有设置都相同:

//  检查是否有卖出信号:
if(CheckSellSignal(current_signal, prev_signal) == 1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID), 0, 0);
}
唯一不同的就是卖出价格 - SYMBOL_BID

由函数对信号的存在进行检查,CheckBuySignal 函数 - 买入,CheckSellSignal函数 - 卖出。这两个函数非常简单明了:

//---------------------------------------------------------------------
//  检查信号是否改为买入:
//---------------------------------------------------------------------
//  返回值:
//    0 - 无信号
//    1 - 有买入信号
//---------------------------------------------------------------------
int CheckBuySignal(int _curr_signal, int _prev_signal)
{

//  检查信号是否改为买入:
  if((_curr_signal==1 && _prev_signal==0) || (_curr_signal==1 && _prev_signal==-1))
  {
    return(1);
  }
  return(0);
}

//---------------------------------------------------------------------
//  检查是否有卖出信号:
//---------------------------------------------------------------------
//  返回值:
//    0 - 无信号
//    1 - 有卖出信号
//---------------------------------------------------------------------
int CheckSellSignal(int _curr_signal, int _prev_signal)
{

//  检查信号是否改为卖出:
  if((_curr_signal==-1 && _prev_signal==0) || (_curr_signal==-1 && _prev_signal==1))
  {
    return(1);
  }
  return(0);
}

我们在这里检查的,是趋势是否已经改变成相反方向,或者趋势方向是否已经出现。如果满足了任何一个条件,函数就会返回信号存在。

总体而言,EA 交易的这种方案提供了一个非常广义的结构,可以轻松升级或拓展,满足更复杂的计算法。

其他 EA 交易的构建方式完全相同。重大差异值存在于外部参数的代码块之中 - 它们必须与已经使用过的趋势指标相对应,必须在创建指标处理的时候作为自变量进行传递。

让我们来看看根据历史数据创建的第一个 EA 交易结果。我们会使用 EURUSD 的历史数据,日柱的区间是 01.04.2004 至 06.08.2010。 在策略测试程序中用默认参数运行 EA 交易,然后就会得出以下结果:

图 8. 使用 MATrendDetector 指标的 EA 交易测试结果

图 8. 使用 MATrendDetector 指标的 EA 交易测试结果

策略测试报告
MetaQuotes-演示 (Build 302)

设置
EA: MATrendExpert
交易品种: EURUSD
周期: 每日 (2004.04.01 - 2010.08.06)
输入: 手数=0.100000

MA 周期=200
经纪: MetaQuotes Software Corp.
货币: USD
初始存入: 10 000.00

结果
柱: 1649 订单号: 8462551
总净利润: 3 624.59 毛利: 7 029.16 净损失: -3 404.57
获利系数: 2.06 预计获利: 92.94
回收系数: 1.21 夏普比率: 0.14

平衡亏损:
平衡亏损绝对值: 2 822.83 平衡亏损最大值: 2 822.83 (28.23%) 平衡亏损相对值: 28.23% (2 822.83)
市值亏损:
市值亏损绝对值: 2 903.68 市值亏损最大值: 2 989.93 (29.64%) 市值亏损相对值: 29.64% (2 989.93)

总交易: 39 短线交易(获利%): 20 (20.00%) 长线交易(获利%): 19 (15.79%)
总成交: 78 盈利交易(总交易的%): 7 (17.95%) 亏损交易(总交易的%): 32 (82.05%)

最大获利交易: 3 184.14 最大亏损交易(总交易的%): -226.65

平均获利交易: 1 004.17 平均亏损交易(总交易的%): -106.39

最大连续盈利 ($): 4 (5 892.18) 最大连续亏损 ($): 27 (-2 822.83)

最大连续盈利(次数): 5 892.18 (4) 最大连续盈利(次数): -2 822.83 (27)

平均连续盈利: 2 平均连续亏损: 8


总体而言还不错,除了从测试最初到 22.09.2004 的这一部分。无法保证这部分是否会在未来重复。当您看到这一段时期的图表,就会看到在一个有限的范围内,存在明显的横向移动。在这些情况下,我们简单的趋势专家就不是那么胜任了。将交易放置在这段时期之中,就得到了以下的图像:

图 9. 横向移动部分

图 9. 横向移动部分

在这个图表上还有 SMA200 移动平均线。

现在,让我们看看,如果以相同的间隔和默认参数,用几条移动平均线构成的指标来构建 EA 交易,在哪些地方会更”先进“?

图 10. 使用 FanTrendDetector 指标的 EA 交易测试结果

图 10. 使用 FanTrendDetector 指标的 EA 交易测试结果

策略测试报告
MetaQuotes-演示 (Build 302)

设置
EA: FanTrendExpert
交易品种: EURUSD
周期: 每日 (2004.04.01 - 2010.08.06)
输入: 手数=0.100000

MA1 周期=200

MA2 周期=50

MA3 周期=21
经纪: MetaQuotes Software Corp.
货币: USD
初始存入: 10 000.00

结果
柱: 1649 订单号: 8462551
总净利润: 2 839.63 毛利: 5 242.93 净损失: -2 403.30
获利系数: 2.18 预计获利: 149.45
回收系数: 1.06 夏普比率: 0.32

平衡亏损:
平衡亏损绝对值: 105.20 平衡亏损最大值: 1 473.65 (11.73%) 平衡亏损相对值: 11.73% (1 473.65)
市值亏损:
市值亏损绝对值: 207.05 市值亏损最大值: 2 671.98 (19.78%) 市值亏损相对值: 19.78% (2 671.98)

总交易: 19 短线交易(获利%) 8 (50.00%) 长线交易(获利%) 11 (63.64%)
总成交: 38 盈利交易(总交易的%): 11 (57.89%) 亏损交易(总交易的%): 8 (42.11%)

最大获利交易: 1 128.30 最大亏损交易(总交易的%): -830.20

平均获利交易: 476.63 平均亏损交易(总交易的%): -300.41

最大连续盈利 ($): 2 (1 747.78) 最大连续亏损($): 2 (-105.20)

最大连续盈利(次数): 1 747.78 (2) 最大连续盈利(次数): -830.20 (1)

平均连续盈利: 2 平均连续亏损: 1

好多了!看看在之前 EA 交易结果中出现问题的那部分,图像如下:

图 11. 平移部分的 FanTrendExpert 结果

图 11. 平移部分的 FanTrendExpert 结果

图 9相比较 - 很显然,趋势改变的错误警告数量减少了。但是成交数量也减少了一半,这是合乎逻辑的结果。在分析两个 EA 交易的平衡/市值曲线时,您可以看到,在获得最大收益的方面,很多成交的收盘都低于优化值。因此,对 EA 交易进行的下一个升级,就是改善成交收盘的计算式。但这不属于本文讨论的范围。读者可以自己来完成。


5. EA 交易测试结果

让我们来测试所有的 EA。下面提供了从 1993 到 2010 年,EURUSD 货币对的历史波动区间,时间周期为 D1。

图12. MATrendExpert 测试

图 12. MATrendExpert 测试

图 13. FanTrendExpert 测试

图 13. FanTrendExpert 测试

图 14. ADXTrendExpert 测试 (ADXTrendLevel = 0)

图 14. ADXTrendExpert 测试 (ADXTrendLevel = 0)

图 15. ADXTrendExpert 测试 (ADXTrendLevel = 20)

图15. ADXTrendExpert 测试 (ADXTrendLevel = 20)

图 16.NRTRTrendExpert 测试

图 16. RTRTrendExpert 测试

图 17.Heiken Ashi 测试

图 17. Heiken Ashi 测试

让我们来看看这些测试结果。

领先两个最常用的 EA,一个是移动平均线,一个是扇形移动平均线。的确,这些 EA 只是简单地使用了上一个时间区间的价格平滑系列,但最接近跟随趋势(以及价格)规则。由于我们使用了比较“重“的 200 日移动平均线,市场波动的影响似乎消失了。

这些 EA 的成交数量低,但这并非是它们的缺点,因为仓位保留可能会持续几个月的时间,它所跟随的是 200 天的趋势。注意看 MATrendExpert 如何在三个趋势区间中交替:盈利、平移(在 EA 中)和资金损失,很有意思。

ADX 指标的趋势判断方法也呈现出很好的结果。PeriodADX 稍作改变,其值变成了 17,在整个历史过程中给出了更加一致的结果。区域强度的过滤效应并不显著。也许,您需要调整 ADXTrendLevel 参数,或者根据目前的市场波动,对其进行更动态的设置。有几个亏损周期,因此,需要采用其他的措施对平衡进行补偿。

无论是在测试的整个范围,还是在随机选择的长线间隔,NRTR 指标实际显示都是零利润。从某种程度而言,这表示这是一种非常稳定的趋势判断方法。也许,调整参数会让这个 EA 交易实现获利,比如进行必要的优化。

以 Heiken Ashi 为基础创建的 EA 交易明显没有盈利。尽管它的历史数据看起来还不错,也许是因为进行实时重新绘制的缘故。其测试结果非常不理想。也许,使用这个指标的平滑版本会得到更好的结果 - 平滑 Heiken Ashi,不会出现过于强烈的重新绘制倾向。

的确,如果一个系统中的用动态止损位建仓,创建一个目标水平,那么所有 EA 交易都会因之受益。此外,拥有一个资金管理系统也是个好办法,不仅可以最小化损失,还可以提高长期间隔的利润。


总结

由此可见,编写判断趋势的代码并不是那么困难。重要的是要亲自去做,还要有一个非常合理的想法,探索一些市场规则。如果您在这些规则方面的基础越深,您在根据这些规则建立交易系统时就会越自信。而这个交易系统也会保持很长的时间。

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

附加的文件 |
experts.zip (11.42 KB)
indicators__3.zip (9.59 KB)
MetaTrader应用商店2013年第三季度业绩 MetaTrader应用商店2013年第三季度业绩

又过了一个季度,我们已决定统计MetaTrader 应用商店的业绩 - MetaTrader平台最大的交易机器人和技术指标商店。 直至报告季度末期,有500多名开发者已经将他们的1200个产品放入MetaTrader 应用商店。

在 MQL5 中创建多色彩指标 在 MQL5 中创建多色彩指标

我们会于本文中研究如何创建多色彩指标或是将现在指标转换为多色彩指标。MQL5 允许以便利的方式呈现信息。如今已不再需要查看大量带有指标的图表来执行 RSI 或 Stochastic 指标分析了,只是根据指标值为烛形图涂不同的颜色会简单许多。

使用WinInet.dll通过网络在终端间进行数据交互 使用WinInet.dll通过网络在终端间进行数据交互

本文描述了通过HTTP请求来操作网络,以及使用一个中间服务器进行终端间数据交互的方法。引入一个MqlNet类库,在MQL5环境中操作因特网上的资源。监视不同经纪商的报价,在终端内和其他交易者进行信息交流,在互联网上查找信息——这些是本文将介绍的一些例子。

MetaTrader 5 中进行测试的原理 MetaTrader 5 中进行测试的原理

MetaTrader 5 中三种测试模式有何区别?应该特别注意什么?如何测试在几个工具上同时进行交易的 EA?在测试期间何时及如何计算指标值?如何处理事件?如何在测试期间以一种仅开盘价模式同步处理来自不同工具的指标柱?本文旨在回答这些问题以及很多其他问题。