外汇EA编写教程:交易货币对篮子时出现的测试形态。第 I 部

概述

在之前有关交易中应用货币对篮的文章中, 我们研究了交易原则, 技术分析手段以及可以通过这些手段检测的形态。当然, 若无某些形态参数的确认, 盲目追随这些方法是不可行的。例如, 我们需要澄清超卖/超买等级的具体价位。在此, 我们将查证检测到的形态参数, 并尝试为交易者制定建议。

研究工具

为了我们的工作, 我们将使用之前开发的 “组合 WPR”。我们经常在之前的文章系列中应用它, 且已证明它有助于检测大多数的形态。

将 WPR 周期从 14 增加到 20, 以便略微平滑指标图表。这可令您 “缩小” 图表, 而不会损失显示品质。

我们将研究三个时间帧: D1, H4 和 H1。您可以使用此处所描述的方法获取其它周期的结果。 

基本的术语和原则可以 在此 找到。

研究形态

我们开始研究 在此处 描述的形态 #3。形态十分简单。它等同于一个单独的、众所周知的长线货币对。交易货币对时应用如下方式:

当蜡烛收盘时, 若组合 WPR 下穿超买等级, 或是上穿超买等级, 交易者将收到篮内所有货币对的入场信号

我们在哪里可以找到这些超卖/超买等级?若在单独的货币对上使用标准 WPR, 我们可以轻松地回答这个问题:

  • 超买等级: – 20%
  • 超卖等级: – 80%

这为我们的研究奠定了起点。我们将使用这些数据来澄清组合 WPR 等级的位置。结果将不仅有助于检查问题中的形态, 而且也有助于其它类似的情况。应用的方法同样也很方便。

指标线应高于超买等级或低于超卖等级, 突破其中之一即可。我们来分析历史数据, 定义潜在的入场数量。我们现阶段不打算使用指标。代之, 我们将应用以前开发的 testIndexZig-Zag1.mq5 和 testWPReur.mq5 指标。在 testWPReur.mq5 当中, 我们简单地根据篮子的组件替换数据。我们稍微简化 testIndexZig-Zag1.mq5 指标源代码, 因为我们已经知道指标的最高点和最低点 (从 100% 到 -100%):

#property copyright "版权所有 2016, MetaQuotes 软件公司"
#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
//--- 绘制最后的最高柱线
#property indicator_label5  "LastHighBar"
#property indicator_type5   DRAW_LINE
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1
//--- 绘制最后的最低柱线
#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;

string               name;

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);
   name = _Symbol + TimeFrameToShortString(Period()) + ".txt";
   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=100,mn=-100;
      double lg;
      lg=mx-mn;
      lg/=100;
      double levels[100];
      int    count[100];
      ArrayInitialize(count,0);
      for(int i=1; i<101; i++) levels[i-1]=NormalizeDouble(lg*i + mn,_Digits);
      for(int i=0;i<rates_total;i++)
        {
         if(ZigZagBuffer[i]==0 || ZigZagBuffer[i]==EMPTY_VALUE) continue;
         else 
           {
            for(int j=0; j<100; j++) 
              {
               if(ZigZagBuffer[i]<levels[j]) 
                 {
                  count[j]++;
                  break;
                 }
              }
           }
        }
      for(int i=0; i<100; i++)
        {
         FileWrite(h,i,levels[i],count[i]);
        }
      FileClose(h);
      Print("工作完成: ",name);
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+


string TimeFrameToShortString(ENUM_TIMEFRAMES period)
{
   switch (period )
   {
      case PERIOD_M1:  return ("M1");
      case PERIOD_M2:  return ("M2");
      case PERIOD_M3:  return ("M3");
      case PERIOD_M4:  return ("M4");
      case PERIOD_M5:  return ("M5");
      case PERIOD_M6:  return ("M6");
      case PERIOD_M10: return ("M10");
      case PERIOD_M12: return ("M12");
      case PERIOD_M15: return ("M15");
      case PERIOD_M20: return ("M20");
      case PERIOD_M30: return ("M30");
      case PERIOD_H1:  return ("H1");
      case PERIOD_H2:  return ("H2");
      case PERIOD_H3:  return ("H3");
      case PERIOD_H4:  return ("H4");
      case PERIOD_H6:  return ("H6");
      case PERIOD_H8:  return ("H8");
      case PERIOD_H12: return ("H12");
      case PERIOD_D1:  return ("D1");
      case PERIOD_W1:  return ("W1");
      case PERIOD_MN1: return ("MN1");
   }
   return ("");
} 

如早前所述, 该指标的主要代码由 这篇文章 中的尊敬同事 Dmitry Fedoseev 开发并提供给社区。提到的两个指标可以在下面的 test.zip 存档中找到。我们已有了必要的工具, 现在我们来查找必要的数据。

可能的交易数量

组合 WPR 的范围在 -100% 到 +100% 之间, 所以我们现在假设超买等级为 +60%, 而超卖等级为 -60%, 符合标准值。我们来看看指标超出超买/超卖等级的次数。为此, 我们要用到此处 描述的方法:

  • 将 testIndexZig-Zag1.mq5 指标应用于组合的 WPR 图表 (testWPReur.mq5)。我们的目标是确定超过 +70% 和 +80%, 或 -70% 和 -80% 的极值数量, 如下图所示。注意问题区域标记为蓝色矩形。现在, 这些极值已包含在计算中, 虽然我们将来会对这些值进行整理:

  • 所应用的 testIndexZig-Zag1.mq5 指标将 testWPReur.mq5 指标的范围划分为 1% 的间隔, 并定义了每个间隔内的极值数。结果将被发送到文件。重复计算所有选定的时间帧。之后, 我们修改了 testWPReur.mq5 中的篮内数据, 并继续处理下一个货币篮。

为了更便捷, 将所获得的所有篮子和所选时间帧上的数据按表格排列。关于欧元篮子的表格片段如下所示。我们来澄清表格的行和列内数值的含义:

  • 编号— 索引编号。
  • 指标 — 指标值的 %。例如, 值为 -96 的行表示组合 WPR 处于 -96% 到 -98%的间隔。
  • 欧元 — 每个选定的时间帧内含有极值数的三列。例如, 已经提到的编号为 1 的行, 其中含有历史上组合 WPR 指标落于 96% 到 98% 间隔内的极值数量: D1 – 零个, H4 和 H1 – 每个时间帧一个。
  • 历史深度 — 用于计算的历史深度。
  • 交易计数 (80%) — 每个时间帧的可能入场总数。例如, H4 上的欧元篮子提供了 83 个可能的入场, 这意味着组合 WPR 指标超过 80% 或低于 -80% 的次数。
  • 交易计数 (70%) — 组合 WPR 在 70% 的参数相同。
  • 交易总数 (80%) — 所有篮子和时间帧的组合 WPR 值在 80% 处的潜在入场总数。
  • Trade total (70%) — 在 70% 处相同。
EUR —-
编号 指标 时间帧 —-
D1 H4 H1 —-
0 -98 2 3 4 —-
1 -96 0 1 1 —-
2 -94 0 0 1 —-
3 -92 0 3 3 —-
4 -90 1 4 5 —-
5 -88 3 4 10 —-
6 -86 1 2 7 —-
7 -84 2 8 7 —-
8 -82 1 8 21 —-
9 -80 4 6 22 —-
—- —- —- —- —- —-
95 92 0 2 6 —-
96 94 0 1 4 —-
97 96 0 0 3 —-
98 98 0 3 0 —-
99 100 0 0 0 —-
历史深度 2000.11.09 2005.04.12 2006.01.17 —-
—-
交易计数 (80%) 25 83 165 —-
交易计数 (70%) 45 207 449 —-
交易总数 (80%) 3793
交易总数 (70%) 7885

该表可以在随附的 Pair.zip 存档中找到。

最后两个表格行包含搜索值。即使考虑到部分信号已经过整理, 还是有相当多的可能入场数量。所以, 我们现在让超卖/超买等级保留在同一个地方。请记住, 所有已发现 (和已经存在的) 值都是概率性的, 并可以调整。

形态形成

我们来定义我们所需的识别入场形态的形状。

  • 如果组合 WPR 指标下穿超买等级 +60%, 交易者卖出一篮子货币对。蜡烛收盘时, 指标值不低于 +50%。下降的指标线应不低于 +70%。这一点的第二个选项是 +80% 以上, 超买等级为 +70%。
  • 买入一篮子货币对的情况与所述对称。

上述图像中高亮显示的所有三种形态均满足这些条件。我们收到一个清晰的 “美丽” 形态, 其数值和条件可以转换为算法。

这意味着我们需要一款智能交易系统。

测试形态的智能交易系统

首先, 我们来处理买入/卖出篮子。在 这篇文章 里, 您可以找到交易货币篮子的详细信息, 并研究包含每个篮子实用建议的表格。我们来使用这个表格, 并在 EA 代码中实现相同的原则。

我们再次展示我们正在寻找的形态:

目标形态 无形态

假设超卖/超买等级可以在 60-70% 的范围内转移。我们来查验一下依据形态的交易数量, 交易时间, 回撤和潜在的盈利能力。我们现在不需要 EA 的稳定利润。我们的目标是进行第一步, 澄清形态的形状。因此, 我们不会发布标准测试报告, 因为我们对 EA 盈利能力不感兴趣, 而我们所需要的数据不包括在标准报告中。我们将重点放在展示获得的结果。

我们将从美元货币篮子开始分析, 将下一个 EA 放在之前所选时间帧的 EURUSD 上:

//+------------------------------------------------------------------+
//|                                                   testBasket.mq5 |
//|                                 版权所有 2017, MetaQuotes 软件公司|
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, MetaQuotes 软件公司"
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- 输入参数

#include <Trade//Trade.mqh>


#define LG 7

input int SELLPROFIT =   0;
input int SELL1LIMIT =  70;
input int SELL2FROM  =  60;
input int SELL2TO    =  50;

input int BUYPROFIT  =   0;
input int BUY1LIMIT  = -70;
input int BUY2FROM   = -60;
input int BUY2TO     = -50;

input int WPR=20;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum BSTATE 
  {
   BCLOSE = 0,
   BBUY   = 1,
   BSELL  = 2
  };

string pair[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY"};
bool bDirect[]={false,false,false,false,true,true,true};
datetime TimeParam[3];

double dWpr[3];
ulong  Ticket[LG];
double TradeResult[LG];
double TradeCurrency;
double Drw;
string sLog;

double TradeTotalResult[LG];
double TradeTotalCurrency;
int    iTradeCount;
double mDrw;

int h1[LG];
BSTATE bstate;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetValue1(int shift)
  {
   double dBuf[1];
   double res=0.0;
   for(int i=0; i<LG; i++)
     {
      CopyBuffer(h1[i],0,shift,1,dBuf);
      if(bDirect[i]==true)
         res+=dBuf[0];
      else
         res+=-(dBuf[0]+100);
     }//end for (int i = 0; i < iCount; i++)      
   res=res/LG;
   return (NormalizeDouble((res + 50) * 2, _Digits) );
  }

//+------------------------------------------------------------------+
//| 智能系统初始化函数                                                  |
//+------------------------------------------------------------------+
int lh;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   EventSetTimer(1);

   for(int i=0; i<LG; i++)
     {
      h1[i]=iWPR(pair[i],0,WPR);
     }
   bstate=BCLOSE;

   ArrayInitialize(TradeTotalResult,0);
   ArrayInitialize(dWpr,EMPTY_VALUE);
   TradeTotalCurrency=0;
   iTradeCount=0;
   mDrw=1000000;

   lh=INVALID_HANDLE;
   string lname = _Symbol + "_" + TimeFrameToShortString(Period() );
   string t1, t = lname;
   int i=0;
   for(;;) 
     {
      t+=".html";
      long lg=FileFindFirst(t,t1);
      if(lg==INVALID_HANDLE) 
        {
         lh= FileOpen(t,FILE_WRITE | FILE_TXT | FILE_ANSI);
         Print("CREATE ",t);
         break;
        }
      FileFindClose(lg);
      t=lname+"_"+IntegerToString(i++);
     }

   FileWriteString(lh,"<!DOCTYPE html PUBLIC /"-//W3C//DTD XHTML 1.0 Strict//EN/" /"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd/">/r/n");
   FileWriteString(lh,"<html xmlns=/"http://www.w3.org/1999/xhtml/">/r/n");
   FileWriteString(lh,"<head>/r/n");
   FileWriteString(lh,"<meta http-equiv=/"Content-Type/" content=/"text/html; charset=utf-8/"/>/r/n");
   FileWriteString(lh,"<title>"+lname+"</title>/r/n");
   FileWriteString(lh,"</head>/r/n<body>/r/n");
   FileWriteString(lh,"<H2>"+_Symbol+" "+TimeFrameToShortString(Period())+"</H2>/r/n");
   FileWriteString(lh,"<H3>形态参数:</H3>/r/n");
   FileWriteString(lh,"<table width=/"100%/" cellspacing=/"0/" cellpadding=/"5/">/r/n");
   FileWriteString(lh,"<thead>/r/n<tr>/r/n<th>BUY</th>/r/n<th>SELL</th>/r/n</tr>/r/n</thead>/r/n<tbody>/r/n<tr>/r/n");
   t=StringFormat("点 1: %d 点 2 从: %d 至: %d 平仓在: %d",BUY1LIMIT,BUY2FROM,BUY2TO,BUYPROFIT);
   FileWriteString(lh,"<td style=/"text-align:center;/">/r/n<ul>/r/n<li>"+t+"</li>/r/n</ul>/r/n</td>/r/n");
   t=StringFormat("点 1: %d 点 2 从: %d 至: %d 平仓在: %d",SELL1LIMIT,SELL2FROM,SELL2TO,SELLPROFIT);
   FileWriteString(lh,"<td style=/"text-align:center;/">/r/n<ul>/r/n<li>"+t+"</li>/r/n</ul>/r/n</td>/r/n");
   FileWriteString(lh,"</tr>/r/n</tbody>/r/n</table>/r/n");
   FileWriteString(lh,"<H2>"+"测试结果"+"</H2>/r/n");
   FileWriteString(lh,"<table border=/"1/" width=/"100%/" cellspacing=/"0/" cellpadding=/"5/">/r/n");
   FileWriteString(lh,"<thead>/r/n<th>编号</th>/r/n<th>类型</th>/r/n<th>WPR(P1/P2)</th>/r/n<th>时间(开始/结束/长度)</th>/r/n<th>回撤/<br/>盈利</th>/r/n<th>货币对盈利</th>/r/n</tr>/r/n</thead>/r/n<tbody>/r/n");

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void PushWpr(double wpr) 
  {
   dWpr[2] = dWpr[1]; dWpr[1] = dWpr[0];
   dWpr[0] = wpr;
  }
//+------------------------------------------------------------------+
//| 智能系统即时报价函数的                                               |
//+------------------------------------------------------------------+
void OnTick()
  {

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Stat() 
  {

   double d=0;
   for(int i=0; i<LG; i++) 
     {
      PositionSelectByTicket(Ticket[i]);
      d+=PositionGetDouble(POSITION_PROFIT);
     }
   if(d<Drw) Drw=d;
   if(Drw<mDrw) 
     {
      mDrw=Drw;
      TimeParam[2]=TimeCurrent();
     }
  }
//+------------------------------------------------------------------+
//| 计时器函数                                                        |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(bstate!=BCLOSE) 
     {
      Stat();
     }
   if(IsNewCandle()) 
     {
      double res=GetValue1(0);
      PushWpr(res);
      if(dWpr[1]!=EMPTY_VALUE) 
        {
         if(bstate==BBUY && (dWpr[0]>=BUYPROFIT )) 
           {
            CloseAllPos();
            bstate=BCLOSE;
           }
         if(bstate==BSELL && (dWpr[0]<=SELLPROFIT )) 
           {
            CloseAllPos();
            bstate=BCLOSE;
           }
         if(bstate==BCLOSE && dWpr[0]<=SELL2FROM && dWpr[0]>=SELL2TO && dWpr[1]>=SELL1LIMIT) 
           {
            EnterSell(0.01);
            bstate=BSELL;
            TimeParam[0]=TimeCurrent();
            TradeCurrency=0;
            Drw=1000000;
            iTradeCount++;
            sLog=StringFormat("<tr>/r/n<td>%d</td>/r/n<td>卖出</td>/r/n<td>%.2f/<br/>%.2f</td>/r/n<td>%s/<br/>",iTradeCount,dWpr[1],dWpr[0],TimeToString(TimeCurrent()));
            return;
           }
         if(bstate==BCLOSE && dWpr[0]>=BUY2FROM && dWpr[0]<=BUY2TO && dWpr[1]<=BUY1LIMIT) 
           {
            EnterBuy(0.01);
            bstate=BBUY;
            TimeParam[0]=TimeCurrent();
            TradeCurrency=0;
            Drw=1000000;
            iTradeCount++;
            sLog=StringFormat("<tr>/r/n<td>%d</td>/r/n<td>买入</td>/r/n<td>%.2f/<br/>%.2f</td>/r/n<td>%s/<br/>",iTradeCount,dWpr[1],dWpr[0],TimeToString(TimeCurrent()));
            return;
           }
        }//if (stc.Pick(1) != EMPTY_VALUE)
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CloseAllPos() 
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);

   TimeParam[1]=TimeCurrent();
   string p="<td>";
   for(int i=0; i<LG; i++) 
     {
      TradeResult[i]=PositionGetDouble(POSITION_PROFIT)+PositionGetDouble(POSITION_SWAP);
      p+=StringFormat("%s = %.2f<br/>",pair[i],TradeResult[i]);
      TradeCurrency       += TradeResult[i];
      TradeTotalResult[i] += TradeResult[i];
      Trade.PositionClose(Ticket[i]);
     }
   p+="</td>/r/n";
   TradeTotalCurrency+=TradeCurrency;
   sLog += StringFormat("%s/<br/>%s</td>/r/n<td>%.2f/<br/>%.2f</td>/r/n",TimeToString(TimeParam[1]), TimeIntervalToStr(TimeParam[0], TimeParam[1]), Drw, TradeCurrency );
   sLog += p;
   FileWriteString(lh,sLog);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterBuy(double lot) 
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);

   for(int i=0; i<LG; i++) 
     {
      if(bDirect[i]) 
        { //发送买入
         Trade.Buy(lot,pair[i]);
         Ticket[i]=Trade.ResultDeal();
        }
      else 
        { //发送卖出
         Trade.Sell(lot,pair[i]);
         Ticket[i]=Trade.ResultDeal();
        }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void EnterSell(double lot) 
  {

   CTrade Trade;
   Trade.LogLevel(LOG_LEVEL_NO);

   for(int i=0; i<LG; i++) 
     {
      if(bDirect[i]) 
        { //发送卖出
         Trade.Sell(lot,pair[i]);
         Ticket[i]=Trade.ResultDeal();
        }
      else 
        { //发送买入
         Trade.Buy(lot,pair[i]);
         Ticket[i]=Trade.ResultDeal();
        }
     }
  }
//+------------------------------------------------------------------+
//| 智能系统逆初函数                                                    |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 摧毁计时器
   EventKillTimer();

   FileWriteString(lh,"</tbody>/r/n</table>/r/n");
   FileWriteString(lh,"<H2>总结果</H2>/r/n");
   FileWriteString(lh,"<table border=/"1/" width=/"100%/" cellspacing=/"0/" cellpadding=/"5/">/r/n");
   FileWriteString(lh,"<thead>/r/n<tr>/r/n<th>成交<br/>计数</th>/r/n<th>盈利</th>/r/n<th>最大回撤</th>/r/n<th>货币对盈利</th>/r/n</tr>/r/n</thead>/r/n<tbody>/r/n");
   string p = StringFormat("<tr><td>%d</td>/r/n<td>%.2f</td>/r/n<td>%.2f at<br/>%s</td>/r/n<td>",iTradeCount,TradeTotalCurrency,mDrw,TimeToString(TimeParam[2]));
   for(int i=0; i<LG; i++)
     {
      if(h1[i]!=INVALID_HANDLE) IndicatorRelease(h1[i]);
      p+=StringFormat("%s = %.2f<br/>",pair[i],TradeTotalResult[i]);
     }
   p+="</td>/r/n</tr>/r/n";
   FileWriteString(lh,p);
   FileWriteString(lh,"</tbody>/r/n</table>/r/n");
   FileWrite(lh,"</body>/r/n</html>"); //结束日志
   FileClose(lh);
  }
//+------------------------------------------------------------------+

bool IsNewCandle() 
  {

   static int candle=-1;

   int t1=0;
   switch(_Period)
     {
      case PERIOD_H1:  t1 = Hour();   break;
      case PERIOD_H4:  t1 = Hour4();  break;
      case PERIOD_D1:  t1 = Day();    break;
     }
   if(t1!=candle) {candle=t1; return(true);}
   return (false);
  }
int Hour4(){return((int)Hour()/4);}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int Day()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.day);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int Hour()
  {
   MqlDateTime tm;
   TimeCurrent(tm);
   return(tm.hour);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string TimeIntervalToStr(datetime dt1,datetime dt2) 
  {
   string tm;
   if(dt1 >= dt2)   tm = TimeToString(dt1 - dt2);
   else tm = TimeToString(dt2 - dt1,TIME_DATE|TIME_MINUTES|TIME_SECONDS);
   string ta[],ta1[];
   StringSplit(tm,StringGetCharacter(" ",0),ta);
   StringSplit(ta[0],StringGetCharacter(".",0),ta1);
   ta1[0] = IntegerToString( StringToInteger(ta1[0]) - 1970);
   ta1[1] = IntegerToString( StringToInteger(ta1[1]) - 1);
   ta1[2] = IntegerToString( StringToInteger(ta1[2]) - 1);
   return (ta1[0] + "." + ta1[1] + "." + ta1[2] + " " + ta[1]);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string TimeFrameToShortString(ENUM_TIMEFRAMES period)
  {
   switch(period)
     {
      case PERIOD_H1:  return ("H1");
      case PERIOD_H4:  return ("H4");
      case PERIOD_D1:  return ("D1");
     }
   return ("");
  }
//+------------------------------------------------------------------+

进行测试的第一个 EA 版本可以在附件的 testBasket.mq5 文件中找到。算法未包含任何特殊的内容, 尽管对报表非常重视。我们来澄清 EA 输入的意思:

  • SELLPROFIT。当组合 WPR 指标达到该值时, 所有卖出篮子的持仓平仓。省缺值为 0%。
  • SELL1LIMIT。这是点 1 处组合 WPR (见上图) 开始识别篮子卖出形态的最小值。省缺值为 70%。
  • SELL2FROM。点 2 处组合 WPR 识别篮子卖出形态的最大值。省缺, 60% 为超买等级。
  • SELL2TO。点 2 处组合 WPR 最终识别篮子卖出形态的最小值。省缺是 50%。
  • BUYPROFIT。当组合 WPR 指标达到该值时, 所有购买入篮子的持仓平仓。省缺为 0%。
  • BUY1LIMIT。这是点 1 处组合 WPR 识别篮子买入形态的最大值。省缺为 -70%。
  • BUY2FROM。点 2 处组合 WPR 识别篮子买入形态的最小值。省缺, 60% 是超卖等级。
  • BUY2TO。点 2 处组合 WPR 最终识别篮子买入形态的最大值。省缺是 -50%。
  • WPR。标准技术指标 WPR 的周期。省缺是 20。

接下来, 从 2016 年 1 月开始在测试器里测试 EA。所选测试日期取决于历史数据质量。我们将分析两种形态的形式。第一种已在上面描述过了, 且按省缺设置。第二种相对于第一种按以下方式平移:

  • SELLPROFIT。当组合 WPR 指标达到该值时, 所有卖出篮子的持仓平仓。此值为 0%。
  • SELL1LIMIT。这是点 1 处组合 WPR (见上图) 开始识别篮子卖出形态的最小值。此值为 80%。
  • SELL2FROM。点 2 处组合 WPR 识别篮子卖出形态的最大值。此值为 70%, 超买等级。
  • SELL2TO。点 2 处组合 WPR 最终识别篮子卖出形态的最小值。此值为 50%。
  • BUYPROFIT。当组合 WPR 指标达到该值时, 所有购买入篮子的持仓平仓。此值为 0%。
  • BUY1LIMIT。这是点 1 处组合 WPR 识别篮子买入形态的最大值。此值为 -80%。
  • BUY2FROM。点 2 处组合 WPR 识别篮子买入形态的最小值。此值为 -70%, 超卖等级。
  • BUY2TO。点 2 处组合 WPR 最终识别篮子买入形态的最大值。此值为 -50%。

结果为 html 报告。

EA 报表

EA 报告很容易理解。我们以欧元篮子报告为例, 来看一下报告的结构。

第一行包含图表名称标题和 EA 启动时所挂载的时间帧。

其次是 EA 单独用于买入和卖出篮子的形态参数: 点1 — 点 1 位置: SELL1LIMIT (BUY1LIMIT)。点 2 从: …至: … —  点 2 位置: SELL2FROM (BUY2FROM) 和 SELL2TO (BUY2TO)。平仓在 — SELLPROFIT (BUYPROFIT) 形态平仓点位置。

EURUSD H4

形态参数:

买入 卖出
  • 点 1: -80 点 2 从: -70 至: -50 平仓在: 0
  • 点 1: 80 点 2 从: 70 至: 50 平仓在: 0

形态参数后面是测试结果表格, 其中包含测试期间每笔交易的数据, 顺序如下:

  • 编号— 索引编号
  • 类型 — 交易类型: 卖出或买入篮子
  • WPR(P1/P2) — 组合 WPR 在点 1 /点 2 处的入场数据
  • 时间(开始/结束/长度) — 交易时间数据: 入场时间/离场时间/交易持续时间
  • 回撤/盈利 — 每次交易的最大回撤 / 最终利润。以存款货币为单位的数据。
  • 货币对盈利 — 组成货币篮子的单个货币对的利润。以存款货币为单位的数据。

从以下表格片段中我们可以看到, 第一笔交易持续了八个小时, 并带来了 16.34 美元的亏损。特别是, EURUSD 的订单以亏损 2.55 美元平仓:

编号 类型 WPR(P1/P2) 时间(开始/结束/长度) 回撤/
盈利
货币对盈利
1 卖出 86.26/
67.15
2016.03.23 20:00/
2016.03.24 04:00/
0.0.0 08:00:00
-21.70/
-16.34
EURUSD = -2.55
GBPUSD = -1.58
AUDUSD = -2.02
NZDUSD = -3.66
USDCAD = -2.15
USDCHF = -2.97
USDJPY = -1.41

包含测试周期内汇总数据的 “总结果” 表格顺序如下:

  • 成交计数 — 整个测试期间的交易数量。
  • 盈利 — 在整个测试期间获得利润。以存款货币为单位的数据。
  • 最大回撤 — 最大回撤和检测时刻。数据以存款货币和日期为单位。
  • 货币对的盈利 — 组成篮子货币的每个货币对的总利润。以存款货币为单位的数据。

这是直接来自报告的表格:

成交
计数
盈利 最大回撤 货币对的盈利
22 189.06 -52.37 于
2016.05.02 19:53
EURUSD = 52.43
GBPUSD = 24.50
AUDUSD = 45.88
NZDUSD = 13.94
USDCAD = 15.73
USDCHF = 12.26
USDJPY = 24.32

获取的报告附在 DocUSD.zip 中。

值得注意的是, D1 的业务意外地小。不过, 有令人鼓舞的信号:

  • EA 在 H4 和 H1 部分显示出积极的结果, 无需来自我们的任何努力。

虽然数据相当有限, 但我们仍然可以作出初步的结论, 今后可以澄清。

  1. 形态很少在日线时间帧内发现。这一趋势最有可能在高于 D1 的时间帧内继续。
  2. 超买/超卖等级为, 在超买的情况下 60% — 70%的范围内, 超卖情况下 -60%— -70%。高于 70% 和低于 -70% 没有多少交易。在这种情况下, 点 1 应该在 90% 或 90% 以下才能识别出这种情况, 这是罕见的。低于 60% 或高于 -60%, 点 2 为接近 40% 或 -40%, 接近潜在的横盘区域。该区域的特征在于指标读数波动性更大, 以及更多的假入场信号。

我们来完成 EA 并继续下一个货币篮子 — NZD。首先, 我们应该通过推导 “正回撤” 值来修改报表。这个概念背后的思路是什么?篮子根据指标读数平仓, 而非一定利润值或特定的交易价位。在平仓之前, 订单篮子中的订单可由 EA 监控的回撤。这些相同的订单也许显示出显著利润, 但却无法锁定, 因为指标尚未达到平仓的必要值。我们称之为 “正回撤”, 其最大值发送到报告。现在, 我们知道篮子的潜在利润, 以及移向积极一面的能力。

我们将此值添加到 “测试器结果” 表格的倒数第二列。”回撤/盈利” 列现在称为 “回撤/+回撤/盈利”。列的每个单元格的数据按照以下顺序排列: 回撤/正回撤/盈利。所有数据均以存款货币为单位。

此外, 最大正向回撤显示在总结果表格中。我们来引入一个额外的倒数第二 “最大正回撤” 列, 并显示整个测试期间的最大正向回撤。

下一个 EA 版本的源代码可以在下面附带的 testBasket1.mq5 文件中找到。

所获 NZD 篮子报告在 DocNZD.zip 存档中。结论如下:

  • 因此, 确认了较早提出的超买/超卖价位的假设。NZDUSD_H1_1.html 报告已添加到存档中, 其级别接近可能的行盘走势开始以及大量的假入场信号。后果非常明显。
  • 确定了 D1 上这种形态的交易数量很少。
  • H1 的结果也令人失望。我们可以假设时间帧的 “噪音级别” 是所有篮子货币对导致的假信号总和。

我们用剩下的篮子货币结束我们的研究: AUD, EUR, GBP, CAD, JPY 和 CHF。在附件 Doc.zip 存档中查找有关这些货币的报告。现在是时候总结结果了。

结果

  • 超买等级实际上在 60-70% 的范围内, 而超卖等级在 -60% 和 -70% 之间。该假设由所获得的报告和标准 WPR 指标上相应级别的位置确认。
  • 查验已在三个时间帧和八个篮子上进行。我们分析了两种超买/超卖线的突破形态:
    1. 形态的点 1 高于 80%。超卖等级为 70%。点 2 位于 70% 和 50% 之内。当指标 ≤ 0% 是篮子平仓。这里显示了入场卖出篮子的形态形式。买入入场的形式与负值对称。
    2. 形态的点 1 高于 70%。超卖等级在 60%。点 2 位于 60% 和 50% 之间。当指标 ≤ 0% 是篮子平仓。这里显示了入场卖出篮子的形态形式。篮子买入入场的形式与负值对称。
  • 我应当再次指出, D1 的交易很少, 本文不再提及。我们使用来自所有报告的 总结果 表格中的数据来形成其它时间帧的汇总表。该表格显示了 EA 以存款货币针对每个货币篮子进行交易, 以及上一段所描述的两种形态形式的交易结果:

     

    H1 时间帧
      AUD EUR USD GBP NZD CAD CHF JPY
    形态 1 -590 90 -37 -991 -141 -80 -118 -514
    形态 2 -259 -67 328 -714 -352 -446 -118 -272

     

    H4 时间帧
      AUD EUR USD GBP NZD CAD CHF JPY
    形态 1 286 -72 189 -400 104 60 -209 120
    形态 2 -208 25 40 80 172 10 -69 -176

     

    我们要注意 H1 令人沮丧的结果。这可能是由于这个时间帧上货币对的 “噪音” 级别很高。

  • H4 的结果更有前途。从现在开始, 我们会特别注意这个时间帧。

获得的结果不允许我们在两种形式的形态之间进行选择, 所以我们必须与两者一起工作。第二种形式的潜力对我来说似乎更大, 但这只是我的主观意见。我们稍后会做出最后的决定。

我们应该在 H1 完成我们的工作吗?不可以!您可能记得, 我们已在报告中引入了 “正回撤” 参数。将其与常规回撤比较我们可以看出如下:

  • 根据组合 WPR 读数 (当其值达到零时) 将篮子平仓的初始想法有所不足。限制亏损的难题还没有解决。由于我们处理一篮子订单, 因此按照存款货币分配止损和尾随止盈是合乎逻辑的。这将令我们防止在盈利时遭受即时亏损, 另一方面允许我们以合理的方式限制潜在的亏损。这种方法可导致 H1 上的积极结果, 并提高 H4 的盈利能力。而这并非我们现行计划的一部分, 但所提出的技术解决方案可能在将来是有用的。

结论

形态测试的第一阶段已经完成。获得的结果需要认真研究。在我们即将到来的工作中, 我们将集中精力整理信号, 并分析来自其它已知形态的附加信号。这将令我们获得新的信息, 并逐步完善已经获得的数据。

本文中使用的程序:

 # 名称 类型  描述
1 Pair.zip 存档 计算所有篮子货币在三个选定时间帧内可能的交易数量的结果。
2 testBasket.mq5 智能交易系统 用于测试的智能交易系统。
3 DocUSD.zip 存档 有关 testBasket.mq5 EA 针对 USD 篮子进行操作的 Html 报告。
 4 DocNZD.zip 存档 有关 testBasket1 EA 针对 NZD 篮子进行操作的 Html 报告。
5 testBasket1.mq5 智能交易系统 用于测试的 EA – 下一版本。
 6  Doc.zip 存档 有关 testBasket1 EA 针对其它篮子进行操作的 Html 报告。
7 test.zip  存档 含有 testIndexZig-Zag1.mq5 和 testWPReur.mq5 指标的存档。

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

附加的文件 |

testBasket.mq5
(26.57 KB)
DocUSD.zip
(14.3 KB)
DocNZD.zip
(27.8 KB)
testBasket1.mq5
(27.74 KB)
Doc.zip
(99.72 KB)
test.zip
(3.82 KB)
Pair.zip
(7.92 KB)

 

 


MyFxtop迈投-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投