English Русский Español Deutsch 日本語 Português
交易货币对篮子时出现的测试形态。第 I 部

交易货币对篮子时出现的测试形态。第 I 部

MetaTrader 5交易 | 29 八月 2017, 09:46
5 921 0
Andrei Novichkov
Andrei Novichkov

概述

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

研究工具

为了我们的工作, 我们将使用之前开发的 "组合 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 Ltd译自俄文
原文地址: 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)
通用EA交易: 访问交易品种的属性 (第8部分) 通用EA交易: 访问交易品种的属性 (第8部分)
文章的第八部分包含了 CSymbol 类的描述, 它是一个特别的对象,可以访问任何交易资产。当在 EA 交易中使用时, 这个类提供了很多交易品种的属性,而简化了EA交易的编程,并扩展了它的功能。
跨平台智能交易系统: 时间过滤器 跨平台智能交易系统: 时间过滤器
本文探讨如何实现跨平台智能交易系统的各种时间过滤方法。时间过滤器类负责检查给定时间是否处于特定时间配置设置的范围内。
用于一组指标信号的朴素贝叶斯分类器 用于一组指标信号的朴素贝叶斯分类器
本文通过运用多个独立指标的信号, 分析贝叶斯公式在提高交易系统可靠性方面的应用。理论计算可由一款简单的通用 EA 进行验证, 配置为使用任意指标。
图形界面 XI: 渲染控件 (统合构建14.2) 图形界面 XI: 渲染控件 (统合构建14.2)
在新版本的函数库中, 所有控件将在 OBJ_BITMAP_LABEL 类型的单独图形对象上绘制。我们还将继续描述代码的优化: 讨论函数库核心类的变化。