请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3364694 新帖:15

趋势有多长?

不做外汇索罗斯发表于:4 月 17 日 19:12回复(1)

目录

  • 简介
  • 定义任务
  • 实现
  • 测试
  • 总结
  • 结论


简介

在不同的时段确定市场条件是外汇交易的基本原则,交易者成功与否依赖于他们的价格预测是否准确。针对这一主题已经有了很多文章,例如 "几种在MQL5中寻找趋势的方法",它描述了用于确定市场趋势的方法,以及相关的指标和EA交易。我前面也有一篇文章 "10种趋势策略的比较分析",也是致力于趋势的,在其中开发和测试了跟随趋势的策略。在本文中,我们也会选择几种确定趋势的方法,目标是评估趋势相对于平盘市场的持续时间。大众广泛接受的趋势与平盘市场的比例是30%对70%,我们将会验证它。


定义任务

让我们确定一下任务和我们研究的条件,

  1. 要选择用于确认趋势和平盘市场的方法来进行随后的有质量和有数量的分析,这需要系统可以显示两种市场条件。理论上,它们应当拥有内建的质量指标,例如趋势强度或者对没有趋势的清晰定义。
  2. 要能够在不同的时段确定和评估比例,用于不同的单个或者多种商品的市场,例如货币市场,股票或者期货市场。
  3. 要开发一个对读者有用的工具,用于在用户特定的条件下做独立研究。
  4. 要根据获得的数据在不同的条件下做比较性分析; 用于寻找相互关联。

实现

1. 选择用于确定趋势的方法。

1.1. 让我们从 ADX 的研究开始,这是一个经典的趋势强弱指标。我们将使用TrendLevel. 的水平来评估趋势或者平盘市场。让我们假定,如果主线上升超过这个水平,就存在趋势。图 1 展示了一个使用这种方法确定有趋势区域平盘区域的实例。市场状态将根据给定样本中 ADX 值高于 TrendLevel 的烛形数量来计算。

图1 使用 ADX 来确定有趋势/平盘区域

1.2. 下面,让我们考虑 趋势强度与方向指标(Trend Power and Direction Indicator), 根据我们的目标,只选择一个布林指标(Bollinger Indicator)并把显示的颜色数量减为两种(红色和绿色)。图 2 清晰显示了很强的市场趋势,其中烛形使用指定的颜色来标记出来。

图2 使用布林带来确定有趋势/平盘区域。

1.3. 在第三个地方,我们将使用趋势百分比(Percentage of Trend), 我们也会修改它,删除第二个周期数并增加指示趋势的颜色。使用这个指标的结果显示在图3中

 

图3 使用趋势百分比(Percentage of Trend)来确定有趋势/平盘区域。

1.4. 另一种确定趋势/平盘区域的方法是RSIFilter。为了更简单的计算,RSI 指标以柱形图方式显示,垂直栏显示了指标值进入了预先设置的超买/超卖区域。在这里,最初的指标也作了修改: 平盘状态不作显示,而柱形图高度值的缓冲区在这种市场条件下等于0。这是为了更加方便地确定趋势 (在这种情况下缓冲区值等于1). 指标的实例显示在图4中。

图 4  使用 RSIFilter 来确定趋势/平盘区域。

1.5. 最后,我们将考虑使用来自"使用MQL5寻找趋势的集中方法"这篇文章中的方法 — 使用 ZigZagTrendDetector 指标来识别趋势/平盘状态。在本例中没有改变,它的实现显示在图5中

图 5  使用ZigZagTrendDetector指标确定趋势/平盘区域.

2. 用于计算市场条件工具的开发和实现。显示结果

每种用于确定趋势/平盘区域方法的结果都将根据几个不同时段显示在汇总的表格中,为了使显示更清晰,我使用了基于图形化界面系列文章的 EasyAndFastGUI 开发库。开发了一个特定的CTrendCountUI类用于显示结果。为了更好的表现,图6显示了最初用于记录所有计算的模板。

 

图6 显示测试计算结果的模板

您可以在屏幕截图中很容易地看到,第一列显示了用于计算趋势的方法,第一行显示了用于多时段选项的计算。为了节约空间和保证可读性,我没有包含界面的完整实现,只是提供了用于配置和显示上述模板的函数:

//+------------------------------------------------------------------+
//| 输出和信息面板的配置                               |
//+------------------------------------------------------------------+
void SetInfoPanel()
  {
//---

   UI.CreateMainPanel("趋势计数器");
   UI.CreateStatusBar(1,25);
   UI.m_status_bar.ValueToItem(0,"启用于 "+Symbol());
   UI.CreateCanvasTable();
//---

   UI.m_canvas_table.SetValue(0,0,"Value");
   UI.m_canvas_table.SetValue(0,1,"ADX");
   UI.m_canvas_table.SetValue(0,2,"BB");
   UI.m_canvas_table.SetValue(0,3,"PoT");
   UI.m_canvas_table.SetValue(0,4,"RSI");
   UI.m_canvas_table.SetValue(0,5,"ZZ");
//---

   UI.m_canvas_table.SetValue(1,0,"M1");
   UI.m_canvas_table.SetValue(2,0,"M5");
   UI.m_canvas_table.SetValue(3,0,"M15");
   UI.m_canvas_table.SetValue(4,0,"M30");
   UI.m_canvas_table.SetValue(5,0,"H1");
   UI.m_canvas_table.SetValue(6,0,"H4");
   UI.m_canvas_table.SetValue(7,0,"H6");
   UI.m_canvas_table.SetValue(8,0,"D1");
   UI.m_canvas_table.SetValue(9,0,"W1");

   UI.m_canvas_table.UpdateTable(true);
   UI.CreateLabel("工作中...");
  }

下一步是创建用于上面计算方法的计算算法,大多数实现都是类似的,所以我们将只会分析它们中的一个。让我们集中在计算方面,

我们从第一个描述的方法开始 - ADX, GetADXCount() 函数.

//+------------------------------------------------------------------+
//| 使用 ADX 计算市场条件的函数                               |
//+------------------------------------------------------------------+
bool GetADXCount()
  {
//--- 模块 1
   double adx[],result[],count;
   int to_copy,bars;
   int tf_size=ArraySize(Ind_Timeframe);
   ArrayResize(Ind_Handle,tf_size);
   ArrayResize(result,tf_size);
   ArrayInitialize(adx,0.0);
   ArrayInitialize(result,0.0);
   ArraySetAsSeries(adx,true);
//---

   for(int i=0;i<tf_size;i++)
     {
      count=0;
      Ind_Handle[i]=iADX(Symbol(),Ind_Timeframe[i],InpInd_ADXPeriod);
      if(Ind_Handle[i]==INVALID_HANDLE)
        {
         Print(" 取得指标句柄失败");
         return(false);
        }
      //--- 模块 2

      bars=Bars(Symbol(),Ind_Timeframe[i]);
      to_copy=(bars<NumCandles)?bars:NumCandles;
      //---

      if(CopyBuffer(Ind_Handle[i],0,0,to_copy,adx)<=0)
         return(false);
      //--- 模块 3

      for(int j=0;j<to_copy;j++)
        {
         if(adx[j]>TrendLevel)
            count++;
        }
      result[i]=(count/to_copy)*100;
      IndicatorRelease(Ind_Handle[i]);
     }
//--- 模块 4

   for(int i=1;i<=tf_size;i++)
      UI.m_canvas_table.SetValue(i,1,DoubleToString(result[i-1],2));
   UI.m_canvas_table.UpdateTable(true);
   return(true);
  }

让我们探讨一下上面列出的代码模块。

  • 模块 1. 变量和数组的初始化,它们会减到用于测试的时段的数量。
  • 模块 2. 一定数量烛形构成的样本,用于测试和获得百分比结果。请注意,在更高时段或者历史数据较少的产品,可能没有足够的柱作为样本,所以您需要确定历史深度,并且在有必要的情况下调整请求的数据。
  • 模块 3. 对于 ADX 指标,当当前柱的数值高于 TrendLevel 时,我们将增加计数器。 
  • 模块 4. 我们把测试结果加到之前在 SetInfoPanel() 函数准备的表格中。

以后,只有模块3 (计数条件) 的代码需要修改。

GetColorBBCount() 函数。

for(int j=0;j<to_copy;j++)
        {
         if(bb[j])
            count++;
        }
result[i]=(count/to_copy)*100;

选中缓冲区的数值为非零值,意思是样本中当前的烛形属于有趋势的,否则这就是平盘时段。

GetPoTCount() 函数。

'趋势百分比' 指标的参数如下:

//--- 趋势百分比的参数

input int                  InpPeriodPoT=20;
input double               UpTrendLevel=0.8;
input double               DnTrendLevel=0.2;

这样,趋势存在与否的指示就是指标值超过 UpTrendLevel 和 DnTrendLevel 水平,对应地,计算方法将看起来是这样的:

 for(int j=0;j<to_copy;j++)
        {
         if(pot[j]>=UpTrendLevel || pot[j]<=DnTrendLevel)
            count++;
        }
 result[i]=(count/to_copy)*100;

GetRSICount() 函数

因为函数以柱形图形式表现,1说明存在趋势,而0说明没有趋势,条件和 GetColorBBCount() 是一样的:

 for(int j=0;j<to_copy;j++)
        {
         if(rsi[j])
            count++;
        }
 result[i]=(count/to_copy)*100;

GetZZCount() 函数.

与前面的函数类似,指标也是相同的柱形图形式。

 for(int j=0;j<to_copy;j++)
        {
         if(zz[j])
            count++;
        }
 result[i]=(count/to_copy)*100;

GetAverage() 函数.

计算所有方法和时段的总体结果。

//+------------------------------------------------------------------+
//| 计算所有数据平均值的函数                                  |
//+------------------------------------------------------------------+
string GetAverage(double &arr[])
  {
   double sum=0.0;
   int size=ArraySize(arr);
   for(int i=0;i<size;i++)
      sum+=arr[i];
   sum/=size;
   return(DoubleToString(sum,2));
  }
//+------------------------------------------------------------------+

结果是,数据的计算和输出有以下的表现形式:

//+------------------------------------------------------------------+
//| EA交易初始化函数                           |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetInfoPanel();
   if(GetADXCount() && 
      GetColorBBCount() && 
      GetPoTCount() && 
      GetRSICount() && 
      GetZZCount()
      )
      UI.m_label1.LabelText("已完成. "+IntegerToString(NumCandles)+" 个柱的结果. 平均有趋势时间段 - "+GetAverage(avr_result)+"%");
   else
     {
      UI.m_label1.LabelText("错误");
     }
//---
   return(INIT_SUCCEEDED);
  }


测试

对于测试,我们决定使用几个不同的交易资产,以最大化研究条件的范围:

  • 货币对 - EURUSD, USDCHF, USDJPY, GBPUSD;
  • 期货 (GOLD-6.17);
  • 股票市场 (#MCD);

在所有时段中,测试都将使用2000个柱的样本进行,这里是测试参数,对于所有交易品种都是一样的。

//+------------------------------------------------------------------+
//| EA交易的输入参数                             |
//+------------------------------------------------------------------+

input int                  NumCandles=2000;              //分析的柱数
//---- ADX 参数
input string               com1="";                      //---- ADX 参数
input int                  InpInd_ADXPeriod=14;          //ADX 周期数
input double               TrendLevel=20.0;
//--- ColorBBCandles 参数
input string               com2="";                      //---- ColorBBCandles 参数
input int                  BandsPeriod=20;               // BB 平均周期数
input double               BandsDeviation=1.0;           // 偏差
input ENUM_MA_METHOD       MA_Method_=MODE_SMA;          // 指标平均方法
input Applied_price_       IPC=PRICE_CLOSE_;             // 价格常数
//--- 趋势百分比的参数
input string               com3="";                      //---- 趋势百分比参数
input int                  InpPeriodPoT=20;
input double               UpTrendLevel=0.7;
input double               DnTrendLevel=0.3;
//--- RSIFilter 参数
input string               com4="";                      //---- RSIFilter 参数
input uint                 RSIPeriod=10;                 // 指标周期数
input ENUM_APPLIED_PRICE   RSIPrice=PRICE_CLOSE;         // 价格
input uint                 HighLevel=60;                 // 超买水平
input uint                 LowLevel=40;                  // 超卖水平
//--- ZigZagTrendDetector 参数
input string               com5="";                      //---- ZigZagTrendDetector 参数
input int                  ExtDepth=5;
input int                  ExtDeviation= 5;
input int                  ExtBackstep = 3;


1. 货币对的测试。

在四个货币对上的测试结果显示,平均有60%的时间是有趋势的,并且同时,使用不同的方法和时段没有很大的区别。

图 7. 四个货币对的测试结果。

2. 期货中的测试。

在期货的测试中显示,更高的时段显示的结果和整体趋势差别较大,这是因为一开始我们把样本的大小设为2000个柱,但是在D1和W1上没有足够的历史数据,所以计算是基于最大可用历史的。 

图7 在期货GOLD-6.17上的测试结果.

3. 股市上的测试。

这里和上面的交易品种一样,有类似的倾向。

 

图8 #MCD 股票, 测试结果.

在测试中,我开始思考选择2000个柱的样本是否合适,也许应该对不同大小样本计算平均趋势数值?这里,有另外一个有趣的因素可以做研究 — 趋势/平盘比例是怎样跟随时间变化的?通过研究这个比例的变化动态,我们可以了解趋势改变的现实,持续时间,频率,等等。所以,我们确定研究每个趋势确定方法中样本数值和平均趋势数值之间的依赖关系。这将会为以上的问题提供答案。我们将使用 Spearman 的等级相关系数来寻找依赖关系, 

我们对两个数字的相互关联感兴趣: 测试的柱数 (X) 和趋势在时间中的百分比 (Y). 对于每个 XY, 都赋予等级,在取得等级的基础上,我们使用公式来计算它们的差异d和系数:

其中 n 是衡量 XY 数据对的数量,系数值可以在-1到1之间变化。正的数值指示的是研究的数值有直接依赖性,而负值的意思是有反向的依赖性。零值表示没有依赖性。

作为例子,让我们重点看在 EURUSD 货币对上,使用 ADX 来确定趋势方向的方法,其他的计算都类似,它们的结果将被加到表格中以便更好更清晰。在我们的例子中,趋势时间的平均值是 is n=7, 预先选择测试的柱数是 (100, 200, 300, 500, 1,000, 1,500, 2,000). 下图显示了这些数值的比较结果,它们的范围,范围内的偏差以及用于计算柱数系数:

Rx X Y Ry D(Rx-Ry) D2
1  100 75.78 1 0 0
2  200 77.72 2 0 0
3  300 79.04 3 0 0
4  500 79.77 5 -1 1
5  1,000 79.49 4 -1 1
6  1,500 81.32 6 0 0
 2,000 81.44 7 0 0

通过把这些数值插入到公式中,我们取得了系数 ρ=1-(6*2)/(7*(49-1)=0.96. 

下一步,我们检查图8中在关键系数值表格中n=7时取得的数值,就能够在测试中得到结论,在趋势的平均值和测试样本选择的柱数之间有很强的正相关联系。

图8  Spearman 等级关联系数的关键数值。

下面,我们计算在 EURUSD 货币对,所有趋势识别方法中的 Spearman 系数,并把结果显示在表格中。

趋势识别方法 Spearman 系数值
 ADX 0.96
 ColorBBCount 0.89
 趋势百分比 0.68
 RSI 过滤器 -0.68
 ZigZagTrendDetector -0.07 

结果说明了什么呢?前三种识别趋势的方法给出的结果是在历史深度上的平均趋势值有直接依赖,而 RSI 过滤器是相反的, 它显示了反向的关系,而 ZigZag 指标显示,最终结果不依赖于样本序列的大小。让我们计算测试中其他交易品种的系数并把结果汇总到表格中。 

趋势识别方法 EURUSD USDCHF USDJPY GBPUSD GOLD-6.17 #MCD
 ADX 0.96 -0.61 -0.75 0.96 -0.86 0.93
 ColorBBCount 0.89 0.46 -0.11 -0.14 -0.64 0.79
 趋势百分比 0.68 0.93 0.11 0.29 0.89 1
 RSI 过滤器 -0.68 1 0.04 0.79 -1 0.89
 ZigZagTrendDetector -0.07 0 0.96 0.96 0.36 0.32

根据取得的结果,对于每个交易品种可以得出以下结论。

  • EURUSD. 在5种分析方法的3种里面有强关联: 历史深度越深,市场上趋势状态的比例就越大。
  • USDCHF. 和前面的交易品种类似: 在早期,市场的趋势状态比例比现在更高。
  • USDJPY. 在这里,改变样本的大小和最终的数值没有关系。市场的变化和动态没有很大的改变。
  • GPBUSD. 5种方法中的四种体现出趋势的比例和样本大小有直接的依赖关系。我们也可以得出结论,在这个货币对中,过去的趋势状态的比例比现在要高。
  • GOLD-6.17. 我们没有发现样本大小的依赖关系。
  • #MCD. 在所有5种方法里都显示出依赖关系 (其中四种有主导性). 这也说明在过去有强大的和长期的趋势,而历史离现在越近,在选择的市场中趋势的持续时间和强度都越弱了。

为了验证所获得结果的可信性,让我们在更大的时段和更长的历史中研究测试交易品种的图表。这会让我们知道趋势变化的改变,在研究中,让我们选择每周时段并且尝试识别趋势和平盘阶段的特点。

1. EURUSD

研究下面的图片,它显示了 EURUSD 货币对从1995年到现在的状态。 

每周 EURUSD 图表, 1995-2017

在这里,有三个区域指示出市场的特点:

  • 第一个区域显示了长期的下跌趋势,含有少许反弹。注意趋势的持续时间和强弱。
  • 第二个区域的方向是和第一个区域几乎完全相反的, 但是强弱和持续时间与第一个区域很类似。我们可以看到,这两段趋势变化发生在从1995年到2007年中期。
  • 标出的第三个和第四个区域中, 趋势变化的强弱和持续时间大幅下降,从历史的角度,现代的趋势看起来更像是双向的波动。

这些发现可以看出,Spearman 等级关联系数的计算确实真实显示了当前的市场状态和趋势/平盘比例的变化。


2. USDJPY

这个交易品种的关联系数的计算显示,趋势和时间中的变化都没有关联。图 10 清晰显示了,从1995年早期开始,有一个明显的, 很强的上涨变化, 随后是明显很短的趋势,而幅度也较小。在图片的第二部分 (下降趋势后有一个上升趋势),那里有明显的持续了数个月的频繁的平盘变化。 


图10 每周图表 USDJPY 1995-2017.

所以,在这个例子中 Spearman 等级关联系数也正确描述出时间长度和趋势/平盘比例没有关联。


3. #MCD.

在股市中,所有确定趋势的物种方法都显示出关联性。图 11 显示了,在过去,该股票是不断上涨的,并且没有明显的回调或者平盘状态,另外,我们可以清楚看到从过去到现在趋势/平盘区域的比例随着股票价格变化有怎样的变化

图 11 #MCD 股票, 2003-2017.

所以,在这里也显示出 Spearman 的等级关联系数计算完全被验证,并且显示的特点是趋势/平盘比例从有趋势到平盘状态的发展变化。总的趋势显示出,市场的变化从有趋势到没有,它们变得更加飘忽不定,更加难以预测。这可以验证出本文中获得的结果: 趋势/平盘比例从有方向转到双向波动;早期的趋势比现在更加明显。


结论

为了更好地展示测试,所有测试的交易品种的综合数值显示在表格中。

测试的交易品种 趋势时间的平均值, %
 EURUSD  62.54
 USDCHF  62.27
 USDJPY  63.05
 GBPUSD  60.68
 GOLD-6.17  59.55
 #MCD  59.71

根据测试结果的汇总表格,我们可以得到下面的结论: 对于给定的测试条件,平均市场有趋势的状态大约是 60%, 而随着时间从过去到现在,趋势/平盘比例逐渐发展到平盘更多,逐渐达到了30:70的初始假想水平。

结果告诉了我们什么呢?

  • 市场变得更加波动,趋势/平盘状态的变化也更加频繁。
  • 长期的趋势变成了短期的波动; 它们的变化比以前更加不清晰了,
  • 对应地,市场的动态,变化和阶段变得更加复杂了。

结论

附件中的档案包含了所有列出的文件,它们都位于对应的文件夹中,对于它们的正确操作,您只需把它们保存到终端根目录的 MQL5 文件夹下。

文章中使用的程序:

#
 名称
类型
描述
1
TrendCount.mq5 EA交易
 测试工具 
2
TrendCountUI.mqh 开发库  用户界面类
3 ColorBBCandles.mq5 指标  趋势强弱和方向指标 (基于布林带)
4 percentageoftrend.mq5 指标  趋势/平盘状态计算指标。
5 rsifilter.mq5 指标  RSI 指标 (柱形图) 含有预先设置的超买/超卖水平。
6 ZigZagTrendDetector.mq5  指标   通过使用标准之字转向指标来确定趋势的指标。


全部回复

0/140

量化课程

    移动端课程