English Русский Español Deutsch 日本語 Português
突破策略测试程序在测试对冲 EA 时的限制

突破策略测试程序在测试对冲 EA 时的限制

MetaTrader 4测试者 | 16 三月 2016, 12:29
3 435 0
chayutra sriboonruang
chayutra sriboonruang

简介

本文将提供一种在策略测试程序中测试对冲 EA 的思路。如你所知,策略测试程序有其自身的限制,它无法打开其他交易品种的任何订单。所有想要测试自己的对冲 Expert Advisor 的用户都需要进行现场测试。但这会限制我们的能力吗?我可以肯定的说,每位对冲交易者都需要在实际交易之前测试自己的 EA。所以我给你们提供的是一种生成虚拟策略测试行为(就像测试程序)的思路,希望它能帮助我们突破 mt4 策略测试程序的限制,也希望它能在未来的应用中发挥作用。



虚拟测试程序的概念

虚拟测试程序的想法是我在 mq4 中使用 "Files" 函数时出现在我脑海里的。我想我可以提取文件中的一些重要数据来制定一个虚拟交易方案,“这也许能成为测试对冲 EA 的解决之道?”让我们来试一试。

我的虚拟测试程序并不需要任何其他外部程序或软件。所有操作都能通过 mq4 参数来完成。这个虚拟测试程序的概念是让打开或关闭对冲订单的指定参数告知我们收集必要的数据。例如开盘价、开盘时间、收盘价、收盘时间以及所有其他重要数据。收集必要的数据后,将它们与任何兼容类型的最新价格变动值进行比较,例如开盘价和最后买盘价,或开盘价和最后卖盘价。当这些值符合关闭对冲条件时,我们会得出利润计算方法,这个方法会指导我们去收集一组新数据。

这些数据组将导出到一个文件以备后用。完成此测试并将所有数据类型都收集到文件中后,我们就能看到“对冲 EA 的运作方式”了。我认为我们可以从这些文件中获取数据,将其绘制为表现曲线的指标,从而完成对冲 EA 的虚拟测试。

根据这个概念,我假定我们可以获取类似于实际策略测试程序结果的测试结果。顺便提一下,这仅仅是一种创建对冲 Expert Advisor 测试程序的思路。我不能保证它和实际测试程序完全一致。但我希望它会成为未来应用的有用参考。

现在我们开始吧。



对冲交易的简要含义

开始前我们先来简单地谈一谈“对冲”(转自我自己的博客,博客名称 pipsmaker,可点击这里访问

什么是对冲?简单来说就是同时打开两个货币对的两个反向交易。对冲的目的是降低交易风险,如果一个的价格上扬,另一个必然下跌,无需担心什么,因为我们同时拥有买入和卖出订单,这样,即便一笔交易亏损了,另一笔交易也会盈利,从而实现了所谓的“低风险”。嘉盛市场中有很多种反向交易方式。

  • 对于走势始终相同的两个货币对,例如欧元兑美元 EURUSD 英镑对美元 GBPUSD,同时做多 EURUSD 和做空 GBPUSD 就是对冲。
  • 对于走势始终相反的两个货币对,例如欧元兑美元 EURUSD 美元兑瑞郎 USDCHF,同时做多 EURUSD 和做多 USDCHF 也是对冲。
  • 甚至同时做多和做空 EURUSD 也是对冲,这种方式有时被称作“套汇”。

对冲交易中,以下几点是毋庸置疑的。

  1. 关联:是对两个货币之间关系的统计度量。关联:是对两个货币之间关系的统计度量。关联系数 范围为 -1 到 +1。关联系数 +1 意味着两个货币对将始终朝同一个方向运动。关联系数 -1 意味着两个货币对将始终朝反方向运动。关联系数 0 意味着货币对之间的关系完全是随机的。(单击此处了解更多)。也可访问免费网站 mataf.com 获取关联值,网站还提供更多有意思的交易参数。
  2. 手数比率:要交易两个既非同向走势也非逆向走势的货币对,手数比率就相当有用了,由于两者自身的波动和变化能力有天壤之别,如果把一个比作乌龟,那另一个就是兔子。手数比率可降低风险,它受更强力的变动对或兔子对的影响,当兔子对负向移动时,可通过将更高的手数放在乌龟对上以保证损失较小。然后你可以从正向乌龟处获得更多盈利,或者换句话说,可以用乌龟的盈利覆盖兔子的亏损。因此,对冲技巧可以确保你的亏损不会超过仅打开一个负向交易时的亏损。

此外,你可能想知道对冲者如何从这种交易方式中盈利。别担心,两个货币对之间始终会发生重叠,这个关联本质上不是始终不变的形式,一个货币对经常会发生延迟现象,一个开始变动,然后另一个跟着变动,还是用龟兔赛跑作比方 - 兔子休息一会,等乌龟来赢它。这就是对冲者能够从中获取丰厚利润的原因。现在很多人使用对冲方式在嘉盛赚钱,没什么可担心的。对冲,等待,在出现正利润值时平仓。就是这样。



对冲概念

在开始对虚拟测试程序进行编程之前,让我们试着在实验中理解这个对冲概念。如果没有这个对冲概念,我们永远不会知道那种类型的数据需要导出、记录和计算。这些数据会告诉我们应以虚拟方式生成的订单类型。在本实验中,我将设定如下对冲规则。

  • 每日开盘时建仓进行对冲
  • 达到 $100 时平仓(手数为 1 和 2)
  • 每小时收集价格变动数据***
  • 新的一天开始时,清除前一天的数据,即便前一天没有达到目标利润
  • 仅买入 2 手欧元兑日元 EURJPY,并卖出 1 手英镑兑日元 GBPJPY

根据这些规则,虚拟订单需要将两个货币对的每日开盘价用作订单开盘价。要计算当日利润,每日内每小时价格(作为变动价格)应记录为订单平仓价格的数据(要求卖出和买入报价),并应与价格变动时间一同记录(确保变动价格来自同一个时间值)。根据每日开仓对冲的概念,我将把所有需要的数据划分为两种文件类型,即两个货币对的每日开盘价和变动价。两种数据类型都将导出为使用单独名称的字符串文件,例如 GBPJPYD1.csv 和 GBPJPYTick.csv。

由于我们想要虚拟测试程序尽可能与实际情况类似,因此采用了价格变动数据,所以必须处理这两个步骤。

  • 做一个脚本,用于将英镑兑日元 GBPJPY 的每日开盘价导出到一个文件中
  • 做一个脚本,用于将英镑兑日元 GBPJPY 的每日变动价导出到一个文件中

对欧元兑日元 EURJPY 也应执行这两个步骤。

但我认为我们可以将这两个步骤合并到一个 EA 中,此 EA 应将两种类型的数据导出到两个单独的文件中。然后,当此 EA 完成了数据记录流程后,新的用于生成虚拟交易的 EA 将从所有已导出文件中获取英镑兑日元 GBPJPY 和欧元兑日元 EURJPY 的所有数据,以执行虚拟测试显示。



突破测试限制的三个步骤

根据上述方法,我得出了一个结论,即我们可通过以下三个步骤实现突破此测试限制的梦想。

  1. 获取价格数据,并使用 EA 将它们输出到一些文件中。
  2. 用另一个单独的 EA 生成虚拟交易,此 EA 也将把结果导出为一个文件。
  3. 在单独的窗口中将结果作为指标进行查看

那么,让我们开始第一个步骤。



步骤 1:导出价格数据

以下是 Expert Advisor 将附加交易品种的每日开盘价导出到一个文件中(对于英镑兑日元,文件名为“GBPJPYD1.csv”,对于欧元兑日元,文件名为“EURJPYD1.csv”),同时还会将变动价格导出到一个文件中,文件名类似于“symbolT. csv”(与 D1 文件相同)。请阅读注释以了解 EA 的工作方式。

注意:此 EA 创建的所有文件都将导出到“MetaTrader 4/tester/files”目录。

//+------------------------------------------------------------------+
//|                                                    symbol-D1.mq4 |
//|                                                    A Sexy Trader |
//|                                  http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
#include <stdlib.mqh>
 
extern string StartDate = "2007.03.17"   //set the starting date to get the same time data
             ,StopDate  = "2007.06.28";  //set the testing time limit to protect the over 
                                         //data recording process
 
extern bool For_OP_SELL = true;/*This is to guide for the right type of data to be collected
                                 ->if For_OP_SELL = true 
                                   the daily Open will be collected as an order open price
                                 ->if For_OP_SELL = false , means for OP_BUY,
                                   the daily Open+SPREAD will be collected instate.
                               */
                               
string name,tname;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
 
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//-------------------------------------------------------------------+
//| Some Essential Parameters In This EA                             |
//-------------------------------------------------------------------+
   int day                   // a variable to mark that today Open has been collected 
      ,ho                    // handle of the file recording the Open price 
      ,ht                    // handle of the file recording the tick price
      ,x=1                   /* the order of D1 file increasing every time D1 data is 
                             equal to 4086 characters and generate the new recording file*/
      ,xt=1                  // same as x but for tick data
      ,bartime               // a variable to mark that current bar's Open has been collected 
      ; 
   double ot                 // latest Open Time
         ,op                 // latest Open price
         ,lt                 // latest tick time
         ,ltk                // latest tick price
         ;
   string OStr               // the string to collect the daily open
         ,TStr               // the string to collect the tick value
         ;
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {   
    
    /*---------------------Only collect the data in the specific time period.----------------------*/  
    if(TimeToStr(TimeCurrent(),TIME_DATE)>=StartDate && TimeToStr(TimeCurrent(),TIME_DATE)<=StopDate)  
    {
       name=Symbol()+x+"D1.csv";       // setup the name of daily open price file
       tname=Symbol()+xt+"T.csv"       // the name of tick price file
            ;
//----       
      if(day!=TimeDay(Time[0]))        // the new day has come
       {
         ot=Time[0];                   // get the new bar time
         if(For_OP_SELL)op=Open[0];    // get the new order open price for SELL Symbol 
                                                 
         else           op=Open[0]+MarketInfo(Symbol(),MODE_SPREAD)*Point;
                                       // else if collecting for the BUY Symbol
         
 
         OStr=OStr+TimeToStr(Time[0],TIME_DATE)+",";//collect the new time data separate each data with ","
         OStr=OStr+DoubleToStr(op,Digits)+",";      //now the new open price
       
       //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~now it's time to export as a file 
       
       ho=FileOpen(name ,FILE_CSV|FILE_WRITE);  // open a file to record the daily open
       if(ho>0)                                 // if the file Symbol()+x+"D1.csv" exist
       {
        FileWrite(ho,OStr);                     // write the collected data
        FileClose(ho);                          // close the file every time your file process done
        if(StringLen(OStr)==4086){x++;OStr="";} /* if the data contains 4086 characters set new x to*/
       }                                        /*   create the new file and prepare the new string for 
                                                   the new file 
                                                */            
        
       Print(TimeToStr(Time[0],TIME_DATE));     // print the collected day time 
       
       int thex=FileOpen(Symbol()+"x.csv",FILE_CSV|FILE_WRITE);
                                                // create a file to record "how many x?" for D1 file for further usage
       if(thex>0)                               // if the file exist
       {
        string xs=DoubleToStr(x,0);             // transform x into string 
        FileWrite(thex,xs);                     // write the x value
        FileClose(thex);                        // close file (every time you finish)
       } 
        
        
        day=TimeDay(Time[0]);                   // now mark today as collected 
       }
 
//--------------------------------FOR TICK VALUE
       /*Because of the decision of collecting the tick data hourly*/
       if(bartime!=Time[0])                     // the new hourly bar has come
                                                // and to make it more flexible when you decided 
                                                      // to collect data in another time frame
       {
         lt=TimeCurrent();                      // get the tick time 
         
         if(!For_OP_SELL) ltk=Bid;              // the tick price for sell order 
         else             ltk=Ask;              // in case for buy order
         
         TStr=TStr+TimeToStr(lt,TIME_DATE|TIME_MINUTES)+",";
                                                // insert the data into the collected string 
         TStr=TStr+DoubleToStr(ltk,Digits)+","; //
         
       //~~~~~~~~                               // now export the data 
       ht=FileOpen(tname,FILE_CSV|FILE_WRITE);  // open a file to record the tick value
       if(ht>0)                                 // if the file Symbol+xt+"T.csv" exist
       {
        FileWrite(ht,TStr);                     // write the collected tick with time 
        FileClose(ht);                          // finish.
        if(StringLen(TStr)==4080){xt++;TStr="";}// prepare for new file if this file reached 4080 character
       }
       
       int thext=FileOpen(Symbol()+"xt.csv",FILE_CSV|FILE_WRITE);
                                                // record the xt value .. same as D1 file 
                                                
       if(thext>0)                              // if the file exist
       {
        string xts=DoubleToStr(xt,0);           // transform into string 
        FileWrite(thext,xts);                   // write xt
        FileClose(thext);                       // done
       } 
     bartime=Time[0];                           // mark as current hourly bar Open price has been collected
     }  
   }    
   else if(TimeToStr(TimeCurrent(),TIME_DATE)>StopDate)  // if out of specified period  
   Print("Done.");                                       // let us know it all done.
//----
   return(0);
  }
//+------------------------------------------------------------------+

步骤 2:生成虚拟交易

这个步骤最令人兴奋。这个步骤是使对冲 EA 可以被策略测试程序所测试。具体情况请参见以下脚本。别忘了阅读注释以了解它的运作方式。与第一个 EA 中类似,结果文件将被导出到“MetaTrader 4/tester/files”目录中。

//|                                                 VirtualHedge.mq4 |
//|                                                    A Sexy Trader |
//|                                  http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
#include <stdlib.mqh>
 
extern string  StartDate         = "2007.03.17";    // specify the testing period the same as collected data
extern string  StopDate          = "2007.06.27";
extern string  BaseSymbol        = "GBPJPY";        // the base symbol          
extern string  HedgeSymbol       = "EURJPY";        // the hedge symbol
 
extern int     Base_OP           = OP_SELL;         // by the rules always sell GBPJPY
extern int     Hedge_OP          = OP_BUY;          // and always buy EURJPY
 
extern double  BaseLotSize       = 1.0;             // take the lot size of 1 for GBPJPY
extern double  HedgeLotSize      = 2.0;             // and take 2 lots for EURJPY
 
extern double  ExpectProfit$     = 100;             // always close when reach $100
 
extern bool    PrintDetails      = true;            // print the order opening and closing detail ?
 
int BSP                                      // the spread of GBPJPY 
   ,HSP                                      // the spread of EURJPY
   ,BOP=-1                                   // OrderType of GBPJPY
   ,HOP=-1                                   // OrderType of EURJPY
   ,day=0                                    // for marking that today hedge was executed
   ,hr                                       // handle of the file for exporting the hedge result
   ,p=1
   ,BC                                       // the base-symbol's contract size
   ,HC                                       // the hedge-symbol's contract size
   ,floating=0                               // for marking as there are some orders currently floating
   ,Pointer=0                                // the Pointer of each string data
   ,AL                                       // Account Leverage
   ,on                                       // order number
   ;
double BOpen                                 // open price of base symbol
      ,HOpen                                 // open price of hedge symbol
      ,BLots                                 // base symbol lotsize
      ,HLots                                 // base symbol lotsize
      ,lastTick                              // to mark the last tick for calculating the current profit
      ,BPF                                   // base symbol order profit
      ,HPF                                   // hedge symbol order profit
      ,TPF                                   // the total profit
      ,CurBalance                            // the array to collect the hedge results
      ,CurB=0                                // the current balance
      ,BTick                                 // Base tick
      ,HTick                                 // Hedge tick
      ,BD1Time                               // Base daily open time 
      ,HD1Time                               // Hedge daily open time 
      ,BTTime                                // Base tick time 
      ,HTTime                                // Hedge tick time 
;
 
string CurTrade                              // a string to show the current performance as a comment 
      ,BORD                                  // a string to specify the type of arrows and text of base symbol
      ,HORD                                  // same thing but for hedge symbol                   
      ,hobstr                                // the whole string of the base symbol open price data
      ,bstr                                  // the string of all daily data of base Symbol 
      ,hstr                                  // the string of all daily data of hedge Symbol
      ,btstr                                 // the string of tick data of base Symbol
      ,htstr                                 // the string of tick data of hedge Symbol
      ,pstr                                  // the string for exporting result data
      ;                   
 
color SELLCL=DeepSkyBlue                     // the color of arrows to mark that sell order was executed
     ,BUYCL=HotPink                          // the color of arrows to mark that buy order was executed
     ,BCL                                    // the color of base symbol, varies by the Base_OP parameter
     ,HCL                                    // the color of hedge symbol, varies by the Hedge_OP parameter
     ;
     
bool closed=true                             // for marking as all trades were closed
     ,trimb=true                             // get rid of taken data of base Symbol or not?
     ,trimh=true                             // get rid of taken data of hedge Symbol or not?
     ,trimbd1=true                           // get rid of taken daily data of base Symbol or not?
     ,trimhd1=true                           // get rid of taken daily data of hedge Symbol or not?
     ;     
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
   CurBalance=AccountBalance();                      //mark the initial deposit
   CurB=AccountBalance();                            //mark the current balance
   pstr=pstr+DoubleToStr(CurBalance,2)+",";          //collect the performance string
   
   AL=AccountLeverage();                             //get the Account Leverage
   
   BSP=MarketInfo(BaseSymbol  ,MODE_SPREAD);         //get the spread
   HSP=MarketInfo(HedgeSymbol ,MODE_SPREAD);
   
   BC =MarketInfo(BaseSymbol  ,MODE_LOTSIZE);        //get the contract size for calculating profit
   HC =MarketInfo(HedgeSymbol ,MODE_LOTSIZE);
   
   BOP=Base_OP;                                      //get the OrderType 
   HOP=Hedge_OP;
   
   BLots=BaseLotSize;                                //get the lot size
   HLots=HedgeLotSize;
   
   string RName=BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv";    
                                                     //name the performance file
                                                     //this file is needed to copy to 
                                                     /* Program files/MetaTrader 4 /Experts/files */
   
   hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);         //open the file to export the initial deposit
 
   if(hr>0)                                          //if the file exist
   {
    FileWrite(hr,pstr);                              //export the initial deposit
    FileClose(hr);                                   //close file
   }
   
   if(Base_OP==OP_SELL){BCL=SELLCL;BORD="sell";}     //specify the parameter leading to create 
   else                {BCL=BUYCL; BORD="buy";}      // the trading arrow and text
   if(Hedge_OP==OP_BUY){HCL=BUYCL; HORD="buy";}
   else                {HCL=SELLCL;HORD="sell";}
   
   getdata(BaseSymbol);                              //get all data of BaseSymbol
   getdata(HedgeSymbol);                             //all data of HedgeSymbol
                                                     //the function located at the bottom 
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
string RName=BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv"; //name the performance file
 
//--------------------------Only perform the show of specified period--------------------------// 
if(TimeToStr(TimeCurrent(),TIME_DATE)>=StartDate && TimeToStr(TimeCurrent(),TIME_DATE)<=StopDate)
                                                                               //mark as no any order now
{
 if(day!=TimeDay(Time[0]))                                  //the new day has come
 {
  
  {
   if(BOpen!=0 && HOpen!=0)                                 //check if yesterday hedge still floating
   {
 
    if(Base_OP==OP_BUY)                                     //the base-symbol's current profit
     {
      BPF=((BTick-BOpen)*BLots*BC)/BOpen;                   
     }
    else
    {
     BPF=((BOpen-BTick)*BLots*BC)/BOpen;
    }
    
    if(Hedge_OP==OP_BUY)                                    //the hedge-symbol's current profit
    {  
     HPF=((HTick-HOpen)*HLots*HC)/HOpen;
    }
    else
    {
     HPF=((HOpen-HTick)*HLots*HC)/HOpen;
    }
    
    TPF=BPF+HPF;                                            //the total current profit
    
    CurB+=TPF;
    
    CurBalance=CurB;                                        //get the latest AccountBalance
    
    pstr=pstr+DoubleToStr(CurBalance,2)+",";                //insert into the performance string
    
    floating=0;                                             //mark as no any order now
 
    BOpen=0;                                                // block the profit calculation process
    HOpen=0;
    
    if(BOpen==0 && HOpen==0)                                //make sure there is no any order now
    {
     closed=true;                                           //mark all orders were closed
     
     CreateObject("R : "+on,OBJ_TEXT,Time[0],Close[0],0,0,DarkViolet,"","Cleared With Profit Of : "+DoubleToStr(TPF,2));
                                                            //create the cleared all orders text
     
     if(PrintDetails)Print("Cleared Hedge With Profit : "+DoubleToStr(TPF,2));
                                                            //print latest action if PrintDetails allowed
                                                            
      hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);             //open the result file to prepare for file writing
      if(hr>0)
      { 
       FileWrite(hr,pstr);                                  //export the data
       FileClose(hr);
      }
      if(StringLen(pstr)>4086){p++;pstr="";}                //set the new file name if pstr is larger than 4086 characters
    
                                                            
                                                            
      int thep=FileOpen("p.csv",FILE_CSV|FILE_WRITE);       //this file is needed to copy to 
                                                            /* Program files/MetaTrader 4 /Experts/files */  
                                                            //too
                                                            
                                                            // record the p value as a file 
                                                
       if(thep>0)                                           // if the file exist
       {
        string ps=DoubleToStr(p,0);                         // transform into string 
        FileWrite(thep,ps);                                 // write p
        FileClose(thep);                                    // done
       } 
    
    }  
   }   
   
   if(floating==0)                                          //now all yesterday hedge were cleared 
   {
    trimb=true; trimh=true;                                 //allow getting tick data for today to work
       
            
    //----------GETTING TODAY ORDER OPEN PRICE AND TIME----------//  
         if(trimbd1)                                              //getting the daily base price allowed
         {
             Pointer=StringFind(bstr,",",0);                      //find the nearest "," from the beginning of string
             BD1Time=StrToTime(StringSubstr(bstr,0,Pointer));     //get the time value located before the "," syntax
             bstr=StringSubstr(bstr,Pointer+1,0);                 //trim off the taken data
             
             Pointer=StringFind(bstr,",",0);                      //find the nearest "," from the trimmed data string
             BOpen=StrToDouble(StringSubstr(bstr,0,Pointer));     //get the PRICE value located before the "," syntax
             bstr=StringSubstr(bstr,Pointer+1,0);                 //trim off the taken data again to prepare for next time 
         }
         if(trimhd1)                                              //getting the daily hedge price allowed
         {
             Pointer=StringFind(hstr,",",0);                      //all processes are the same as bstr above
             HD1Time=StrToTime(StringSubstr(hstr,0,Pointer));     //...
             hstr=StringSubstr(hstr,Pointer+1,0);                 //...   
             
             Pointer=StringFind(hstr,",",0);                      //...
             HOpen=StrToDouble(StringSubstr(hstr,0,Pointer));     //...
             hstr=StringSubstr(hstr,Pointer+1,0);                 //...
         }
    //--------GETTING TODAY ORDER OPEN PRICE AND TIME ENDED--------//  
 
       
       if(BOpen!=0 && HOpen!=0 && CurBalance>(BLots+HLots)*BC/AL)     
               //make sure the data taken is not zero and margin balance still available    
       {
       floating=1;            //mark as hedge sent
       
       closed=false;          //mark as all orders opened
       
       on++;                  //new hedge orders have opened 
        
        if(PrintDetails)      //if PrintDetails allowed
        {
         Print(on+" Opened : "+BaseSymbol+""+DoubleToStr(BLots,2)+" lots @ "+DoubleToStr(BOpen,Digits)+".");
         Print(on+" Opened : "+HedgeSymbol+""+DoubleToStr(HLots,2)+" lots @ "+DoubleToStr(HOpen,Digits)+".");
        }
       }
       else                                                 //in case can not send hedge
       {
        Comment("Can Not Open The Trade : No Margin Available");
       }                                                    //let us know 
       
       
       if(closed==false)                                    //hedge sent
       {
                                                            //create the buy and sell arrow and text
                                                            //this function is located at the bottom of the EA
       CreateObject("B : "+on,OBJ_ARROW,Time[0],Open[0]-20*Point,0,0,BCL,BORD,"");
 
       CreateObject("H : "+on,OBJ_ARROW,Time[0],Open[0]+30*Point,0,0,HCL,HORD,"");
       }
   }   
  } 
  
  day=TimeDay(Time[0]);                                     //mark as proceeded
 }  
 
//-------------------------For Each Tick----------------------------// 
 
if(lastTick!=Hour())                 //the new hour has come
{                                                    
  
   if(trimb && StringFind(btstr,",",0)>0)                         //getting base tick allowed
   {
             Pointer=StringFind(btstr,",",0);                     //same process as getting daily data above
             BTTime=StrToTime(StringSubstr(btstr,0,Pointer));
             btstr=StringSubstr(btstr,Pointer+1,0);
             
             Pointer=StringFind(btstr,",",0);
             BTick=StrToDouble(StringSubstr(btstr,0,Pointer));
             btstr=StringSubstr(btstr,Pointer+1,0);  
   }
   if(trimh && StringFind(htstr,",",0)>0)                         //if getting hedge tick allowed
   {    
             Pointer=StringFind(htstr,",",0);
             HTTime=StrToTime(StringSubstr(htstr,0,Pointer));
             htstr=StringSubstr(htstr,Pointer+1,0);
             
             Pointer=StringFind(htstr,",",0);
             HTick=StrToDouble(StringSubstr(htstr,0,Pointer));
             htstr=StringSubstr(htstr,Pointer+1,0);  
   }
  
  
  
  if(TimeDay(BD1Time)==TimeDay(BTTime) && TimeDay(HD1Time)==TimeDay(HTTime))
                                                            //only if the tick is form the same day
  {    
   trimbd1=true; trimhd1=true;                              //allow to get the next day value
   
   if(TimeHour(BTTime)==TimeHour(HTTime))                   //and same time hour
   {                                             
     
     trimb=true; trimh=true;                                //allow to get the next tick value
 
     if(BOpen!=0 && HOpen!=0)                               //if the calculation process allowed
     {
      
      if(Base_OP==OP_BUY)
       {
        BPF=((BTick-BOpen)*BLots*BC)/BOpen;                 //the base-symbol's current profit
       }
      else
      {
       BPF=((BOpen-BTick)*BLots*BC)/BOpen;
      }
    
      if(Hedge_OP==OP_BUY)
      {  
       HPF=((HTick-HOpen)*HLots*HC)/HOpen;                  //the hedge-symbol's current profit
      }
      else
      {
       HPF=((HOpen-HTick)*HLots*HC)/HOpen;
      }
     
      TPF=BPF+HPF;                                          //the total current profit
      
      CurTrade=DoubleToStr(TPF,2);                          //show the current profit
     
      if(TPF > ExpectProfit$)                               //when they need to close  
      {                                                 
       
       BOpen=0;                                             //set the new value of order open price 
       HOpen=0;                                             //and block the profit calculation process
      
       CurTrade="No Any Hedge Order Now.";                  //Hedge was closed 
       
       
       floating=0;                                          //mark as hedge was closed
       CurB+=TPF;
       
       CurBalance=CurB;                                     //get the last balance equity
       
       pstr=pstr+DoubleToStr(CurBalance,2)+",";             //insert into performance string
       
       CreateObject("R : "+on,OBJ_TEXT,Time[0],Close[0],0,0,YellowGreen,"",
                                 "Close With Profit Of : "+DoubleToStr(TPF,2));
                                                            //create the closed text
                                                            
       if(PrintDetails)                                     //Print the last Close detail
       {                                                    
         Print(on+" Closed "+BaseSymbol+" @ "+DoubleToStr(BTick,Digits));
         Print(on+" Closed "+HedgeSymbol+" @ "+DoubleToStr(HTick,Digits));
         Print(on+" Closed Hedge With Profit : "+DoubleToStr(TPF,2));  
       }                                                   
       hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);    //open it again to prepair for file writing
       
       if(hr>0)
       { 
        FileWrite(hr,pstr);                         //export the data
        FileClose(hr);
       }
      
      if(StringLen(pstr)>4086){p++;pstr="";}        //set the new file name if pstr is larger than 4086 characters
       
      
       thep=FileOpen("p.csv",FILE_CSV|FILE_WRITE);
                                                    // record the p value again
                                                
       if(thep>0)                                   // if the file exist
       {
        ps=DoubleToStr(p,0);                        // transform into string 
        FileWrite(thep,ps);                         // write p
        FileClose(thep);                            // done
       } 
      
      }
     }
     
   }
   else                                             //in case the data values are not from the same tick time  
   {
     if(BTTime>HTTime){trimb=false;}                //freeze the offside data to can not be trim off
     
     else             {trimh=false;}
   }
  }
  else                                              //in case tick data is offside from today
  {
     if(BTTime>BD1Time){trimb=false;}               //freeze the offside data to can not be trim off
     else
     if(BTTime<BD1Time){trimbd1=false;}
      
     if(HTTime>HD1Time){trimh=false;}
     else 
     if(HTTime<HD1Time){trimhd1=false;}
  } 
}
 
 
 
lastTick=Hour();                                     //mark as latest tick proceeded
}   
   
   
   Comment("\nBOpen : "+DoubleToStr(BOpen,Digits)    //show the current situation
          ,"\nHOpen : "+DoubleToStr(HOpen,Digits) 
          ,"\nBOT : "+TimeToStr(BD1Time,TIME_DATE)
          ,"\nHOT : "+TimeToStr(HD1Time,TIME_DATE)
          ,"\nBTick : "+DoubleToStr(BTick,Digits)
          ,"\nHTick : "+DoubleToStr(HTick,Digits)
          ,"\nBTT : "+TimeToStr(BTTime,TIME_DATE|TIME_MINUTES)
          ,"\nHTT : "+TimeToStr(HTTime,TIME_DATE|TIME_MINUTES)
          ,"\nfloating : "+floating
          ,"\nclosed : "+closed
          ,"\ntrimb : "+trimb
          ,"\ntrimh : "+trimh
          ,"\n"
          ,"\nCurOrderNo. : "+on
          ,"\nCurProfit : "+CurTrade
          ,"\nCurBalance : "+DoubleToStr(CurBalance,2)
          );
//----
   return(0);                                               //ALL DONE.
  }
//+------------------------------------------------------------------+
//| A Function To Make This Virtual Tester Looks Like The Real One   |
//+------------------------------------------------------------------+
void CreateObject(string name,int type, int time1, double price1, int time2, 
                             double price2, color cl,string ordtype,string txt)
{
    
    if(type==OBJ_TREND)
    {
    ObjectCreate(name,type,0,time1,price1,time2,price2);
    ObjectSet(name,OBJPROP_COLOR,HotPink);
    }
    
    if(type==OBJ_ARROW)
    {
    ObjectCreate(name,type,0,time1,price1);
    ObjectSet(name,OBJPROP_COLOR,cl);
     if(ordtype=="sell")ObjectSet(name,OBJPROP_ARROWCODE,221);
     else              ObjectSet(name,OBJPROP_ARROWCODE,222);
    }
 
    if(type==OBJ_TEXT)
    {
     ObjectCreate(name,type,0,time1,price1);
     ObjectSetText(name,txt,8,"Comic Sans MS",cl);
    }
}
//+------------------------------------------------------------------+
//| GETTING ALL DATA FROM FILES FUNCTION                             |
//+------------------------------------------------------------------+
void getdata(string sym)
{
Comment("Collecting Data.","\n\nPlease Wait........");  //let us know that getting data still in process
      
      int x =FileOpen(sym+"x.csv",FILE_CSV|FILE_READ)   //get how many files of D1 and tick
         ,xt=FileOpen(sym+"xt.csv",FILE_CSV|FILE_READ)
         ,pter=0,s=0,v=0
         ,lastME=0,t=0
         ;
      double ME,U;
      string str,str2;
      int xa=StrToInteger(FileReadString(x))
         ,xta=StrToInteger(FileReadString(xt))
         ,xtc=1
         ;
      FileClose(x);
      FileClose(xt);   
   
      if(xta>xa)xtc=xta;                                     //make it run only in one for loop
      else      xtc=xa;
      
      pter=0;s=0;
      for(int i=1;i<=xtc;i++)                                //the loop to get all string data
      {
       string name=sym+i+"T.csv"                             //set the data file name
             ,d1  =sym+i+"D1.csv"
             ;
       int h=FileOpen(name,FILE_CSV|FILE_READ)               //open all files to read
          ,d=FileOpen(d1  ,FILE_CSV|FILE_READ);
       
       //------------------------------------------------------------
       string source=FileReadString(h);                      //read the tick string
       FileClose(h);
       
        if(sym==BaseSymbol)                                  //if get the data of base symbol 
        {
         btstr=btstr+source;
        }
        else                                                 //if hedge symbol 
        {
         htstr=htstr+source;
        }
 
       //------------------------------------------------------------
       if(d>0)                                               //read the daily data 
       {
         string d1s  =FileReadString(d);FileClose(d);
         
         if(sym==BaseSymbol)                                 //if get base daily data
         {
          bstr=bstr+d1s;
         }
         else                                                //if get hedge data
         {
          hstr=hstr+d1s;
         }
       }
      }
}
//------------------------------------ALL DONE---------------------------------------------//


步骤 3:查看结果

执行虚拟订单并记录对冲结果之后,我们可以收集这些数据以展示我们的对冲概念。为此我决定将所有记录数据导出为一个指标,以在单独的窗口中绘制表现曲线,就像 CCI、RSI 或 ATR 等诸多指标那样。第二个 EA 中的所有文件都应复制到“MetaTrader 4/experts/files”目录中。

要完成这条曲线,需要以下指标。

//+------------------------------------------------------------------+
//|                                                  performance.mq4 |
//|                                                    A Sexy Trader |
//|                                         http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
 
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Goldenrod
//---- input parameters
extern string    BaseSymbol="GBPJPY";
extern string    HedgeSymbol="EURJPY";
//---- buffers
double ExtMapBuffer1[]                 //this is the indicator buffer
      ,curve[8888888]                  //this array is for collecting the result from the performance file
      ;
 
int handle;                            //maybe no need to explain anymore
string data;
int len=0
   ,i=0
   ,j=0
   ,p
   ,pv
   ,pter=0
   ;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
   
   IndicatorShortName(BaseSymbol+"~"+HedgeSymbol+"");
//----
   
   p =FileOpen("p.csv",FILE_CSV|FILE_READ);                       //get how many result files were exported
   pv=StrToInteger(FileReadString(p));
   FileClose(p);
   
   for(int i=1;i<=pv;i++)                                         //the loop to get all exported result as a string
   {
      string name = BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv";//get the name of the performance file
      handle=FileOpen(name,FILE_CSV|FILE_READ);                   //search for the file to open to read
      if(handle>0)                                                //if the file is exist
      {   
          data=data+FileReadString(handle);                       //get the whole data
          FileClose(handle);                                      //close it
      }
   
   }
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int    counted_bars=IndicatorCounted(),i=0,s=-1;
   
//----
   len=StringLen(data);                              //get the lenght of the data string
  
   pter=0;                                           //set the pointer which use for searching the ","
                                                     /*please be informed that the file csv was collected 
                                                     the result as a string like this 
                                                     1000.54,1100.54,1200.54,1300.54,1400.54
                                                     but the fact is we want only the real data is like this
                                                     1000.54
                                                     1100.54
                                                     1200.54
                                                     1300.54
                                                     1400.54
                                                     so the "," is the key to get the data as above
                                                     and it can be done by finding out the data befor ","
                                                     and get rid off it from the whole data 
                                                     to shift the next data into the front
                                                     and insert each taken off data into the curve array 
                                                     */
   
   for(i=len;i>=0;i--)                               /*the loop to define how many room this array should build
   {                                                   to contain the performance data
                                                     */
    if(StringFind(data,",",0)>0)                     //if there is a nearest "," from the front
    {
     s++;                                            //indicate the room number  
     pter=StringFind(data,",",0);                    //get the point where the first "," exist
     curve[s]=StrToDouble(StringSubstr(data,0,pter));//insert the first data of the whole string
     data=StringSubstr(data,pter+1,0);               //cut the inserted data off
    }                           
    else          break;                             //no data to count anymore , break the loop
 
   ArrayResize(curve,s+1);                           //resize the curve array for furthur usage
//----
   for(i=0,j=s;i<=s;i++,j--)                         //the plotting process bigin
   {
    if(curve[j]>0)ExtMapBuffer1[i]=curve[j];         //plot the performance curve
   }
//----                                               //all the things done.
 
   return(0);
  }
//+------------------------------------------------------------------+


如何使用它们

在你下载我的代码的副本之前,我们来做一份简要的“使用说明”,作为一份迷你用户手册。

要让我们的期待变成现实,以下五个简单的步骤是切不可忽略的。它们是:

  1. 在测试程序中(无需执行可视化模式),在“Expert Advisor:”菜单中选择symbol-D1.mq4,并在“Symbol:”菜单中选择你喜爱的对冲对的第一个对冲交易品种,如果此交易品种用于卖出订单,将日期-时间周期和“For_OP_SELL”值设置为 true,如果此交易品种用于买入订单,则设为 false,之后在“Period:”菜单中选择每小时周期,单击“开始”运行记录流程。
  2. 针对第二个对冲符号执行步骤 1 中的流程,***切勿忘记更改“For_OP_SELL”参数***以与此符号的订单类型匹配。
  3. 选择VirtualHedge.mq4设置所有变量,并选择测试交易品种(任何你想要的交易品种)。但这需要可视化模式来查看对冲表现。
  4. 将所有显示对冲表现的相关文件“program files/metatrader 4/tester/files”复制到“program files/metatrader 4/experts/files"目录。(可能包括 GBPJPY_EURJPY_result1.csv 和 p.csv,如果有多个结果文件,则需要全部复制。)
  5. performance.mq4连接到任何当前活动图表中,以查看实况般的对冲表现。

这是我的实验规则的表现曲线。

哎!看上去不怎么样。但我觉得你的表现曲线会更好些。



总结

很高兴现在我们在对冲 EA 测试方面步入了一片新的天地。对冲者再也无需担心测试程序限制的问题了。但我还得说一下,本文中的对冲概念仅仅是个示例,目的仅在于缩短测试流程时间。要让虚拟测试程序使用你的对冲策略,你需要列出重要数据,例如每天的开盘价和收盘价、最高价、最低价或任何其他类型的数据。如果你采用连动交易,还需要导出各个特定时间的所有关联值。通过此列表,你会了解应记录哪些数据、应计算哪些数据和应将哪些数据作为结果发送出去。为了缩短你的数据处理时间,我建议把你的测试周期分为多个部分,这比一次性处理全部数据来的更合理。比如要测试 EA 一年内的数据,最好把这段时间分为四个部分,每个部分三个月。希望你的表现曲线漂亮得像一个穿着红衣的性感美女,也希望这篇文章至少有一章一节能帮到作为对冲者的你,或者至少能启发你最终获得一个美妙的对冲结果。最后,希望你喜欢这篇文章。以下是一个两分钟的视频,记录了我的虚拟对冲表现。(2007.03.19 至 2007.04.19)

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/1493

附加的文件 |
performance.mq4 (5.71 KB)
symbol-D1.mq4 (8.23 KB)
VirtualHedge.mq4 (24.81 KB)
通用的 Expert Advisor 模板 通用的 Expert Advisor 模板
本文将帮助交易新手创建可灵活调整的 Expert Advisor。
群集指标在嘉盛市场中的实际应用 群集指标在嘉盛市场中的实际应用
群集指标是一系列将货币对分成独立的货币的指标。指标允许跟踪相对货币波动,确定形成新的货币趋势的潜能,接收交易信号,以及跟踪中期和长期仓位。
面向初学者的 MQL4 语言。技术指标和内置函数 面向初学者的 MQL4 语言。技术指标和内置函数
这是&ldquo;面向初学者的 MQL4 语言&rdquo;系列的第三篇文章。现在我们将学习使用内置函数和用于技术指标的函数。后者对于以后开发你自己的 Expert Advisor 和指标至关重要。另外,我们将通过一个简答的例子,解释如何追踪进入市场的交易信号,以及如何正确使用指标。在文章的末尾,你将学到一些关于语言本身的新鲜有趣的内容。
使用分形绘制水平突破位 使用分形绘制水平突破位
本文描述了使用上/下分形显示支撑/阻力位的指标创建。