简介
在我们前面的文章中,谈到了当交易货币篮子时,会有形态出现,我们精力集中在组合的震荡指标上。我们使用组合的WPR指标作为例子,结果我们取得了一些形态,分析了它们的优点和缺点,并且对在真实交易中使用它们的实用性得出了结论。
但是,这还不够。基于震荡指标的组合指标不能满足所有交易者对货币篮子进行技术分析的需要,交易者的工具箱应该要加强,加上组合的跟随趋势的指标以及它们自己的形态。只有在研究它们之后,我们才能确定技术工具是否已经完备。
让我们开发一个测试指标来解决这个问题。我们已经进行过这个任务,所以我们可以使用前面文章中的代码来做最小的修改。但是,我们首先应该探讨组合跟随趋势指标的一些特点,我们将使用前面文章中读者已经熟悉的术语。
组合跟随趋势指标的特点
组合跟随趋势指标不能基于任何趋势跟随的父指标,因为有某些限制。
限制 #1. 组合指标应该放到单独的窗口中,在价格图表窗口中显示此指标没有意义。因为平均的原则就是用于构建组合指标,而不清楚它们在这种情况下应该如何显示。另外,使用的测量单位和在图表上使用的是不同的。所以,不管是移动平均,还是布林带,再或者是其它的图表指标都无法用作父指标。
限制 #2. 组合的指标只能显示一个货币的状态;所以,您需要两个组合指标来显示一个货币对的当前状态。它们中的每一个都位于独立窗口中,我们需要两个额外的窗口。这样分开的原因是缩放尺度的差别。基于震荡指标的组合指标永远在预先定义的限制之内变化,然而,对于跟随趋势指标就不是这样,它们没有已知的最大值或者最小值。这就意味着之前描述过的方法,包括把移动平均应用到两个组合指标的读数上是没有意义的。这样的联合计算在组合跟随趋势指标的时候是不适合的。
父指标列表的限制使我们在最大程度上使用组合的跟随趋势指标,例如,根据预先的检验,在 MetaTrader 5 菜单中的跟随趋势指标列表中,只有 ADX 和 StdDev 符合我们的需要,
但是这不是我们放弃任务的理由,我们将使用提供的工具并开始使用我们已经知道的组合指标 — 篮子货币指数。
使用移动平均的篮子货币指数
让我们开发 testIndexMA.mq5 测试指标,和在这里描述的指标类似,并且加上了移动平均:
//+------------------------------------------------------------------+ //| testDistance.mq5 | //| 2016 MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 input color clr= clrGreen; input color clrMA = clrMagenta; input int maperiod = 10; //MA 周期数 double ind[],ma[]; //+------------------------------------------------------------------+ //| 自定义指标初始化函数 | //+------------------------------------------------------------------+ //int h,h1; int OnInit() { //--- 指标缓冲区映射 ArraySetAsSeries(ind,true); SetIndexBuffer(0,ind); IndicatorSetString(INDICATOR_SHORTNAME,"testdistance"); IndicatorSetInteger(INDICATOR_DIGITS,2); PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); PlotIndexSetInteger(0,PLOT_LINE_STYLE,STYLE_SOLID); PlotIndexSetInteger(0,PLOT_LINE_WIDTH,2); PlotIndexSetInteger(0,PLOT_LINE_COLOR,clr); PlotIndexSetString(0,PLOT_LABEL,"_tstdistance_"); ArraySetAsSeries(ma,true); SetIndexBuffer(1,ma); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE ); PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID ); PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 1 ); PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrMA ); PlotIndexSetString (1, PLOT_LABEL, "_tstdistance_MA" ); //--- return(INIT_SUCCEEDED); } string pair[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY"}; bool bDirect[]={false,false,false,false,true,true,true}; int iCount=7; double GetValue(int shift) { double res=1.0,t; double dBuf[1]; for(int i=0; i<iCount; i++) { t=CopyClose(pair[i],PERIOD_CURRENT,shift,1,dBuf); if(!bDirect[i]) dBuf[0]=1/dBuf[0]; res*=dBuf[0]; }//end for (int i = 0; i < iCount; i++) return (NormalizeDouble(MathPow (res, 1/(double)iCount), _Digits) ); } //+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ 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 &tick_volume[], const long &volume[], const int &spread[]) { if(prev_calculated==0 || rates_total>prev_calculated+1) { int rt=rates_total; for(int i=1; i<rt; i++) { ind[i]= GetValue(i); } rt -= maperiod; for (int i = 1; i< rt; i++) { ma[i] = GetMA(ind, i, maperiod, _Digits); } } else { ind[0]= GetValue(0); ma[0] = GetMA(ind, 0, maperiod, _Digits); } //--- 返回 prev_calculated 的值用于下一次调用 return(rates_total); } void OnDeinit(const int reason) { string text; switch(reason) { case REASON_PROGRAM: text="指标通过调用 ExpertRemove() 函数终止运行";break; case REASON_INITFAILED: text="这个数值表示 OnInit() 处理函数 "+__FILE__+" 返回了非零数值";break; case REASON_CLOSE: text="终端已经被关闭"; break; case REASON_ACCOUNT: text="账户已经改变";break; case REASON_CHARTCHANGE: text="交易品种或者时段已经改变";break; case REASON_CHARTCLOSE: text="图表被关闭";break; case REASON_PARAMETERS: text="输入参数已经改变";break; case REASON_RECOMPILE: text="程序 "+__FILE__+" 被重新编译";break; case REASON_REMOVE: text="程序 "+__FILE__+" 被从图表上删除";break; case REASON_TEMPLATE: text="图表上应用了新的模板";break; default:text="其它原因"; } PrintFormat("%s",text); } //+------------------------------------------------------------------+ double GetMA(const double& arr[], int index , int period, int digit) { double m = 0; for (int j = 0; j < period; j++) m += arr[index + j]; m /= period; return (NormalizeDouble(m,digit)); }使用这组输入数据,指标画出了带有移动平均的美元指数。把第49行和第50行这样修改:
string pair[]={"EURUSD", "EURJPY", "EURCHF", "EURGBP", "EURNZD", "EURCAD", "EURAUD"}; bool bDirect[]={true,true,true,true,true,true,true};再次编译文件 testIndexMA2.mq5. 结果我们会得到类似的指标,显示了欧元指数。把它放到 EURUSD H1:
我们对指标的绝对值并不感兴趣。让我们对移动平均指标的交叉点来计数,计算潜在的入场点。就像在前面的文章中所说的,这些点应该在烛形关闭的时候固定,我们正是这样做的。使用垂直线标记侦测到的进场点: 蓝色表示买入而红色表示卖出,结果明显是正面的,但是,利润相对较小并且不稳定,所以应该增加获利的能力。首先,不要忘记货币对的第二个货币,并把美元指数指标加到一个独立的窗口中:
用垂直线标记移动平均和美元指数图的交叉。让我们分析结果,
- 指数图与移动平均的交叉显示了可能的趋势反转,如果在货币对的第二个货币中也有类似(或者反转)的交叉,那就会利润更高。例如,对于 EURUSD, 如果美元指数向上与移动平均交叉,欧元指数应该向下与移动平均交叉。这种情况的信号就是一个货币走强而同时另一个货币走弱。
- 如果两个货币的交叉点是在同一个方向,就不要进入市场。在那种情况下,很可能是平盘。
- 交叉点应该清晰可见。我们在前面的文章中已经探讨过。
这样就得到了第一个实用的结论: 当进入市场时要考虑到两种货币的指数。推荐当一个货币走弱,而另一个货币走强的时候进入市场,其中一个信号就是指数图与移动平均交叉。但是,这个信号还不够: 首先,要等待第二个货币走向相反方向。
还是有延迟的问题: 对于货币对中的两个货币,在指数图和移动平均交叉点之间可能的最大距离是多少?显然,最小(并且最佳)距离是0。很难使用最大延迟来给出清楚的答案,尽管很明显应该使用某个距离。如果一个货币走弱和另一个货币的走强时间相差较远,进入市场就是危险的,在这种情况下,我们会面对背离和趋势的走弱。
所以,我们应该考虑根据组合的跟随趋势指标来进入市场。为了能够更准确的评估潜在入场点,让我们转到上面谈到的指标绝对值的问题。
使用之字转向(ZigZag)快速分析
对于我们未来的工作, 让我们使用来自这篇文章的之字转向指标,它是由我很尊敬的同僚Dmitry Fedoseev写的。让我们把 iUniZigZagPriceSW.mq5 放置到美元指数图上:
在这里,之字转向显示为一条蓝色的粗线。我们的目标是分析和组织之字转向片段的长度。使用这种方法,我们可能就可以得到美元指数的"摆动幅度",
让我们修改一点指标的代码:
//+------------------------------------------------------------------+ //| iUniZigZagSW.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 6 #property indicator_plots 3 //--- 最高点绘图 #property indicator_label1 "High" #property indicator_type1 DRAW_LINE #property indicator_color1 clrGreen #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- 最低点绘图 #property indicator_label2 "Low" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGreen #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- 之字转向绘图 #property indicator_label3 "ZigZag" #property indicator_type3 DRAW_SECTION #property indicator_color3 clrRed #property indicator_style3 STYLE_SOLID #property indicator_width3 1 //--- 方向绘图 #property indicator_label4 "Direction" #property indicator_type4 DRAW_LINE #property indicator_style4 STYLE_SOLID #property indicator_width4 1 //--- LastHighBar(最近的高点柱形) 绘图 #property indicator_label5 "LastHighBar" #property indicator_type5 DRAW_LINE #property indicator_style5 STYLE_SOLID #property indicator_width5 1 //--- LastLowBar(最近的低点柱形) 绘图 #property indicator_label6 "LastLowBar" #property indicator_type6 DRAW_LINE #property indicator_style6 STYLE_SOLID #property indicator_width6 1 #include <ZigZag\CSorceData.mqh> #include <ZigZag\CZZDirection.mqh> #include <ZigZag\CZZDraw.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum EDirection { Dir_NBars=0, Dir_CCI=1 }; //--- 输入参数 input EDirection DirSelect=Dir_NBars; input int CCIPeriod = 14; input ENUM_APPLIED_PRICE CCIPrice = PRICE_TYPICAL; input int ZZPeriod=14; input string name="index-usd-zz.txt"; CZZDirection*dir; CZZDraw*zz; //--- 指标缓冲区 double HighBuffer[]; double LowBuffer[]; double ZigZagBuffer[]; double DirectionBuffer[]; double LastHighBarBuffer[]; double LastLowBarBuffer[]; //+------------------------------------------------------------------+ //| 自定义指标初始化函数 | //+------------------------------------------------------------------+ int h; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { switch(DirSelect) { case Dir_NBars: dir=new CNBars(ZZPeriod); break; case Dir_CCI: dir=new CCCIDir(CCIPeriod,CCIPrice); break; } if(!dir.CheckHandle()) { Alert("指标 2 下载错误"); return(INIT_FAILED); } zz=new CSimpleDraw(); //--- 指标缓冲区映射 SetIndexBuffer(0,HighBuffer,INDICATOR_DATA); SetIndexBuffer(1,LowBuffer,INDICATOR_DATA); SetIndexBuffer(2,ZigZagBuffer,INDICATOR_DATA); SetIndexBuffer(3,DirectionBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(4,LastHighBarBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(5,LastLowBarBuffer,INDICATOR_CALCULATIONS); h=FileOpen(name,FILE_CSV|FILE_WRITE|FILE_ANSI,','); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(CheckPointer(dir)==POINTER_DYNAMIC) { delete(dir); } if(CheckPointer(zz)==POINTER_DYNAMIC) { delete(zz); } } //+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int ind=0; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[] ) { int start; if(prev_calculated==0) { start=0; } else { start=prev_calculated-1; } for(int i=start;i<rates_total;i++) { HighBuffer[i]=price[i]; LowBuffer[i]=price[i]; } int rv; rv=dir.Calculate(rates_total, prev_calculated, HighBuffer, LowBuffer, DirectionBuffer); if(rv==0)return(0); zz.Calculate(rates_total, prev_calculated, HighBuffer, LowBuffer, DirectionBuffer, LastHighBarBuffer, LastLowBarBuffer, ZigZagBuffer); if(ind<= 10) ind++; if(ind == 10) { double mx=0,mn=1000000; double lg; for(int i=0;i<rates_total;i++) { if(ZigZagBuffer[i]==0 || ZigZagBuffer[i]==EMPTY_VALUE) continue; if(ZigZagBuffer[i] > mx) mx = ZigZagBuffer[i]; if(ZigZagBuffer[i] < mn) mn = ZigZagBuffer[i]; } lg=mx-mn; PrintFormat("Min index: %.05f Max index: %.05f Length: %.05f",mn,mx,lg); lg/=100; double levels[100]; int count[100]; ArrayInitialize(count,0); for(int i=1; i<101; i++) levels[i-1]=NormalizeDouble(lg*i,_Digits); mn=0; for(int i=0;i<rates_total;i++) { if(ZigZagBuffer[i]==0 || ZigZagBuffer[i]==EMPTY_VALUE) continue; if(mn==0) mn=ZigZagBuffer[i]; else { lg=MathAbs(mn-ZigZagBuffer[i]); for(int j=0; j<100; j++) { if(lg<levels[j]) { count[j]++; break; } } mn=ZigZagBuffer[i]; } } for(int i=0; i<100; i++) { PrintFormat("%d level: %.05f count: %d",i,levels[i],count[i]); FileWrite(h,i,levels[i],count[i]); } FileClose(h); } return(rates_total); } //+------------------------------------------------------------------+指标开始工作并且在第十个订单分时定义出最大可能的之字转向片段大小,把这个大小设为 100%, 我们就能计算百分之一的大小以及之字转向片段中其他部分的数值。结果我们会得到一个数组,包含了之字转向片段中从1%到100%最大的片段数字。结果显示在文件中和 Libre Office Calc 图表 (从 ZZdata.zip 档案中下载)中。让我们显示文件的开始部分以及对应的图表部分:
数量 | 片段长度 |
片段数量 |
---|---|---|
0 |
0.01193 | 2975 |
1 |
0.02387 |
850 |
2 |
0.0358 |
197 |
3 |
0.04773 |
54 |
4 |
0.05967 |
17 |
- 当跟随趋势进入市场时,如果把之字转向应用到篮子货币指数的组合指标时,片段的大小超过某个值,我们就要小心。这里片段的大小是指之字转向在价格(Y)轴上的投影。
我们应该定义这种"关键"片段的大小,为此,在上面的数据中应用统计方法,您可以自己进行分析,参考您的风险设置。以我的观点,这个"关键"片段的长度应该不到 0.03.
在这个例子中,我们分析了整个可用的历史,但是,如果我们想要得到最近的市场变化,我们应该使用短一点的时间段 (年, 季或者月),
可以在所有的货币篮子和大多数时段中进行类似的分析。看到图表在不同的“货币”- “时段”中的变化应该是很有趣的。开发者们将会立即注意到算法是很容易在代码中实现的,当然,您应当不只依赖于一个信号,要寻找确认。上面展示的技术可以应用到很多通用的趋势指标中,但是,它对处理振荡指标是没有意义的。
这样,我们向通过使用组合指标提高进场质量的道路上又前进了一步,因为是需要找到信号的确认的, 这正是我们现在将要做的。
联合使用不同的组合指标
你也许知道,我们已经有了一个基于WPR的组合指标,我们已经在前一篇文章中详细剖析过它, 包括它的代码和可用的形态。这一次,让我们尝试把它和组合指数指标联合使用。结果希望是非常有效的,因为很多交易系统都是用相同方法构建: 跟随趋势指标+振荡指标。
让我们使用来自前面文章的 testDistance.mq5, testWPR.mq5 和 testWPRjpy.mq5 指标,并把它们一起和组合欧元指数指标 testDistance.mq5 放到图表上。在我们前面的文章中,我们已经研究了 EURJPY, 所以, 应该重写 testWPRjpy 指标用来操作 USD。我们将保留指标的名称,这样就不用修改 testDistance.mq5 指标了。所有这部分的指标都可以在 wpr.zip 档案中找到。
组合 WPR 指标是用来画出我们货币对中货币的组合 WPR 指标之间差距的 (这些在原来的文章中都详细描述过),我们的目标是侦测前面所描述过的形态,在一个单个指标中使用另一个指标作为过滤器:
潜在的入场点(并非所有)在图片上标记出来了。进场点在组合 WPR (顶部窗口) 和组合欧元指数 (底部窗口) 显示了同一方向的形态,是更加可信的。这里有进场点1和6。这些是指标和移动平均的交叉点。
入场点 2 特别有趣,因为组合 WPR 构建了几乎完美准确的形态穿过了超卖线。其它的入场点没有给我们提供足够的确认。尽管,在这些点位在真实的市场入场不会造成亏损,我还是不会在真实交易中冒这些险。
根据组合指标在货币篮子的组合WPR之间绘制差距的方法是合理的吗?如果把针对欧元的组合WPR和组合欧元指数一起使用会不会更加准确呢?让我们试试看,使用 testWPR.mq5 来替换 testDistance.mq5:
这里我们可以看到针对欧元的组合 WPR 指标,它被证明了吗?在这种情况下,是的。指标已经修正了我们的进场位置点2和点6一个烛体 (箭头用于指定方向)。进场点 1 还没有得到足够的确认,进场点5和3不推荐。点位4已经由指标修正,合并到点位7了。
可以看到,取得的结果说明货币篮子的组合WPR指标比货币对中两个货币组合WPR指标差别更好。但是,这只在这个特定的例子中是正确的。我想推荐这两种组合振荡指标都要应用,直到收集到大量的统计数据。
您可能要问“这有什么特别的吗?几个指标的组合使用对于我们并不新鲜,为什么要在这片文章中谈呢?另外,那与模式无关啊"。
但是,文章的这个部分的目标是回答三个问题:
- 在这种情况下什么是最好类型的振荡指标?
- 使用这些方法有什么明显的限制吗?
- 识别出的形态和确认在使用不同类型的振荡指标时正确性如何?
我们已经尽我们的能力回答这三个问题了,另外,测试结论的方法也已经清楚。
结论
在本文中,我们已经讨论了在交易货币篮子时出现的最简单的形态,但是这是否意味着整个主题结束了吗?远非如此。还有很多有趣的问题呢,
让我再次重复明显的事情: 文章中附加的指标不是用来做真实交易的! 它们还不稳定,只能用做演示的目的。
文章中使用的程序:
# | 名称 |
类型 |
描述 |
---|---|---|---|
1 | testIndexMA.mq5 | 指标 | 带有移动平均的测试组合美元指数指标。 |
2 | testIndexMA2.mq5 | 指标 | 带有移动平均的测试组合欧元指数指标。 |
3 | testIndexZig-Zag1.mq5 | 指标 | 可以衡量和记录片段长度的测试之字转向指标。 |
4 | testWPR.mq5 | 指标 | 用于欧元的测试组合 WPR 指标。 |
5 | testWPRjpy.mq5 | 指标 | 用于美元的测试组合 WPR 指标。 |
6 | testDistance.mq5 | 指标 | 画出两个指标差距的测试组合指标。这里的是 testWPR.mq5 和 testWPRjpy.mq5 (EUR 和 USD). |