本文将提供一种在策略测试程序中测试对冲 EA 的思路。如你所知,策略测试程序有其自身的限制,它无法打开其他交易品种的任何订单。所有想要测试自己的对冲 Expert Advisor 的用户都需要进行现场测试。但这会限制我们的能力吗?我可以肯定的说,每位对冲交易者都需要在实际交易之前测试自己的 EA。所以我给你们提供的是一种生成虚拟策略测试行为(就像测试程序)的思路,希望它能帮助我们突破 mt4 策略测试程序的限制,也希望它能在未来的应用中发挥作用。
虚拟测试程序的想法是我在 mq4 中使用 "Files" 函数时出现在我脑海里的。我想我可以提取文件中的一些重要数据来制定一个虚拟交易方案,“这也许能成为测试对冲 EA 的解决之道?”让我们来试一试。
我的虚拟测试程序并不需要任何其他外部程序或软件。所有操作都能通过 mq4 参数来完成。这个虚拟测试程序的概念是让打开或关闭对冲订单的指定参数告知我们收集必要的数据。例如开盘价、开盘时间、收盘价、收盘时间以及所有其他重要数据。收集必要的数据后,将它们与任何兼容类型的最新价格变动值进行比较,例如开盘价和最后买盘价,或开盘价和最后卖盘价。当这些值符合关闭对冲条件时,我们会得出利润计算方法,这个方法会指导我们去收集一组新数据。
这些数据组将导出到一个文件以备后用。完成此测试并将所有数据类型都收集到文件中后,我们就能看到“对冲 EA 的运作方式”了。我认为我们可以从这些文件中获取数据,将其绘制为表现曲线的指标,从而完成对冲 EA 的虚拟测试。
根据这个概念,我假定我们可以获取类似于实际策略测试程序结果的测试结果。顺便提一下,这仅仅是一种创建对冲 Expert Advisor 测试程序的思路。我不能保证它和实际测试程序完全一致。但我希望它会成为未来应用的有用参考。
现在我们开始吧。
开始前我们先来简单地谈一谈“对冲”(转自我自己的博客,博客名称 pipsmaker,可点击这里访问
什么是对冲?简单来说就是同时打开两个货币对的两个反向交易。对冲的目的是降低交易风险,如果一个的价格上扬,另一个必然下跌,无需担心什么,因为我们同时拥有买入和卖出订单,这样,即便一笔交易亏损了,另一笔交易也会盈利,从而实现了所谓的“低风险”。嘉盛市场中有很多种反向交易方式。
对冲交易中,以下几点是毋庸置疑的。
此外,你可能想知道对冲者如何从这种交易方式中盈利。别担心,两个货币对之间始终会发生重叠,这个关联本质上不是始终不变的形式,一个货币对经常会发生延迟现象,一个开始变动,然后另一个跟着变动,还是用龟兔赛跑作比方 - 兔子休息一会,等乌龟来赢它。这就是对冲者能够从中获取丰厚利润的原因。现在很多人使用对冲方式在嘉盛赚钱,没什么可担心的。对冲,等待,在出现正利润值时平仓。就是这样。
在开始对虚拟测试程序进行编程之前,让我们试着在实验中理解这个对冲概念。如果没有这个对冲概念,我们永远不会知道那种类型的数据需要导出、记录和计算。这些数据会告诉我们应以虚拟方式生成的订单类型。在本实验中,我将设定如下对冲规则。
根据这些规则,虚拟订单需要将两个货币对的每日开盘价用作订单开盘价。要计算当日利润,每日内每小时价格(作为变动价格)应记录为订单平仓价格的数据(要求卖出和买入报价),并应与价格变动时间一同记录(确保变动价格来自同一个时间值)。根据每日开仓对冲的概念,我将把所有需要的数据划分为两种文件类型,即两个货币对的每日开盘价和变动价。两种数据类型都将导出为使用单独名称的字符串文件,例如 GBPJPYD1.csv 和 GBPJPYTick.csv。
由于我们想要虚拟测试程序尽可能与实际情况类似,因此采用了价格变动数据,所以必须处理这两个步骤。
对欧元兑日元 EURJPY 也应执行这两个步骤。
但我认为我们可以将这两个步骤合并到一个 EA 中,此 EA 应将两种类型的数据导出到两个单独的文件中。然后,当此 EA 完成了数据记录流程后,新的用于生成虚拟交易的 EA 将从所有已导出文件中获取英镑兑日元 GBPJPY 和欧元兑日元 EURJPY 的所有数据,以执行虚拟测试显示。
根据上述方法,我得出了一个结论,即我们可通过以下三个步骤实现突破此测试限制的梦想。
那么,让我们开始第一个步骤。
以下是 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); } //+------------------------------------------------------------------+
这个步骤最令人兴奋。这个步骤是使对冲 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---------------------------------------------//
执行虚拟订单并记录对冲结果之后,我们可以收集这些数据以展示我们的对冲概念。为此我决定将所有记录数据导出为一个指标,以在单独的窗口中绘制表现曲线,就像 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); } //+------------------------------------------------------------------+
在你下载我的代码的副本之前,我们来做一份简要的“使用说明”,作为一份迷你用户手册。
要让我们的期待变成现实,以下五个简单的步骤是切不可忽略的。它们是:
这是我的实验规则的表现曲线。
哎!看上去不怎么样。但我觉得你的表现曲线会更好些。
很高兴现在我们在对冲 EA 测试方面步入了一片新的天地。对冲者再也无需担心测试程序限制的问题了。但我还得说一下,本文中的对冲概念仅仅是个示例,目的仅在于缩短测试流程时间。要让虚拟测试程序使用你的对冲策略,你需要列出重要数据,例如每天的开盘价和收盘价、最高价、最低价或任何其他类型的数据。如果你采用连动交易,还需要导出各个特定时间的所有关联值。通过此列表,你会了解应记录哪些数据、应计算哪些数据和应将哪些数据作为结果发送出去。为了缩短你的数据处理时间,我建议把你的测试周期分为多个部分,这比一次性处理全部数据来的更合理。比如要测试 EA 一年内的数据,最好把这段时间分为四个部分,每个部分三个月。希望你的表现曲线漂亮得像一个穿着红衣的性感美女,也希望这篇文章至少有一章一节能帮到作为对冲者的你,或者至少能启发你最终获得一个美妙的对冲结果。最后,希望你喜欢这篇文章。以下是一个两分钟的视频,记录了我的虚拟对冲表现。(2007.03.19 至 2007.04.19)
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程