![ヘッジ EA の検証でストラテジーテスタの限界を打ち破る](https://c.mql5.com/i/articles/overlay.png)
ヘッジ EA の検証でストラテジーテスタの限界を打ち破る
はじめに
本稿ではヘッジ EA をストラテジーテスタで検証する考えを提供します。ご存じのとおり、ストラテジーテスタには、別のシンボルについてのオーダーをオープンすることはできないという独自の限界があります。自分のヘッジ Expert Advisor を検証したいと思うユーザーは、本番で検証する必要があるのです。ですがこのことがわれわれの能力を制限するのでしょうか?ヘッジドレーダーはだれも本番のトレード以前に自分の EA を検証する必要がある、と私は思います。そのため、私は mt4 のストラテジーテスタの限界を打ち破るのに役立ち、将来の使用法に役立つと希望を持って、みなさんに仮想で戦略を検証動作(検証のような)を作成するという考えを提供しているのです。
仮想テスターのコンセプト
仮想テスターのアイデアが浮かんだのは、mq4の "Files" 関数で作業をしている時でした。仮想トレーディングスキームを設定するためにファイルから重要なデータを取り出すという考えが頭に浮かびました。「これはヘッジ EA を検証することについての答えになる可能性があるのでしょうか?」試してみようではありませんか。
私の仮想テスターは外部プログラムやソフトウェアを必要としません。すべてmq4 パラメータによって行うことができるのです。この仮想テスターのコンセプトは、ヘッジ注文のオープンやクローズの指定されたパラメータに必要なデータを集めるようわれわれに伝えさるというものです。始値、開始時刻、終値、終了時刻、その他すべての重要なデータです。必要なデータが設定されたら、それらは始値と最終ビッド、または始値と最終アスクのような互換性あるタイプの直近のティック値を比較するのに使用されます。そのような値は、ヘッジ終了条件を満たしたあと、データの新しいグループを集めさせる利益計算方法にわれわれを導くのです。
こういったデータグループはのちに使用するためにファイルにエクスポートされます。検証が完了し、データタイプがすべてファイルに集められると、『ヘッジ EA がどのように動作するか』確認します。パフォーマンス曲線のインディケータとしてデータをプロットするために、ファイルからデータを取得することでわれわれのヘッジ EA の視覚検証が完了したと考えられます。
このコンセプトで、現実のストラテジーテスタ結果に似た検証結果が得られると思われます。これがヘッジ Expert Advisor に対してテスターを作成する唯一の方法です。私は現実のテスターとまったく同じ結果が得られることは保証しません。ただのちに使用するために良い結果となるよう願うのみです。
それでは始めます。
ヘッジトレーディングの簡単な意味
スタート前に『ヘッジ』(ここにある私自身の pipsmaker という名前のブログからの抜粋です)。
ヘッジングを簡単に言うと、同時に 2 種類の通貨ペアの逆方向のトレードを 2 つオープンすることです。これはトレードリスクを低減します。片方が上昇すれば片方は確実に下降しますが、心配することはありません。なぜなら、同時に売りと買いの注文を出し、そのため片方で損を出してももう片方は利益を得るのです。それが『リスクを低減する』と言われるゆえんです。Forex 界では多種類の逆トレードスタイルがあります。
- EURUSD と GBPUSD のようにつねに同じように変動する2組の通貨ペアで、同時に EURUSD の買いと GBPUSD の 売りをオープンするのがヘッジングです。
- EURUSD と USDCHF のように常に逆に変動する 2 組の通貨ペアについては、EURUSD の買い、そしてUSDCHF の買いをオープンすることもヘッジすることになります。
- または、同時に EURUSD の買いと売りをオープンすることもヘッジですが、これは『裁定』と呼ばれることもあります。
ヘッジ取引では疑いようのないことがいくつかあります。
- 相関関係とは、2 つの通貨間の統計的測定を言います。相関係数が取る範囲は -1 と +1 の間です。+1 の相関は2つの通貨ペアが 100% 同方向に変化することを意味します。-1 の相関は2つの通貨ペアが 100% 逆方向に変化することを意味します。相関ゼロは通貨ペア間の関係が完全にランダムであることを示します。(より詳しくはここを読んでください)こちらの無料ウェブサイト mataf.com,からも相関値を入手することができます。そこでは数多くの興味深いトレードパラメータも提供されています。
- ロットサイズ比率;同じ方向にも逆方向にも変動しない2つの通貨ペアに対して、ロット比率はひじょうに必要です。というのもそれら自身の変動性や変動能力は、片方がカメ、もう片方がうさぎとすれば、うさぎとカメほど異なるためです。より激しく変動するペア、すなわちうさぎペアによる影響を受け、カメペアに大きなロットサイズを設定することで、うさぎペアが負の動きをする場合に損失を低く抑え、ロット比率はリスクを低減します。そうすると、正のカメからより利益を得ることができる、別の言い方をすると、損失うさぎが出す損をカメの利益で置き換えが可能なのです。ヘッジのテクニックにより、負の側にトレードを一つだけオープンすることで出す以上の損失は受けなくてすむのです。
ところで、ヘッジャーがそのトレードスタイルからどのように利益を得るのだろう、と思ったことはありますか?ご心配なく。2組の通貨ペアにはつねに重複があります。事実上、相関関係は不変のスキームではありません。頻繁に1組による遅延があり、1組はそれを追います。ここでもまたうさぎとカメのようにです。うさぎが休憩し、カメが打ち負かし勝利するのを待つのです。ヘッジャーがそこからすてきな利益を得るのはそのためです。そして今、数多くの人が Forex で利益を得るためにヘッジスタイルを利用しています。心配することは何もありません。ヘッジし、待ち、利益が示されたらクローズする。それだけです。
ヘッジのコンセプト
仮想テスターをコード化するにさきがけ、実験でヘッジのコンセプトを理解しておきます。ヘッジコンセプトの知識なしでは、どのタイプのデータをエクスポートし、レコードし、計算するかわかりません。こういったデータはどのタイプのオーダーがバーチャルで作成されるか示してくれます。この実験では、以下のようにヘッジのルールを設定します。
- 1日の始まりに毎日ヘッジを開始する。
- $100 に到達すれば終了する(ロットサイズ 1 と 2 を取ります)。
- 毎時ティック価格データを収集する***
- 目標利益に到達していなくても、翌日の始めにはそのデータをクリアする。
- EURJPY を 2 ロットだけ買い、GBPJPY を 1 ロット売る。
このルールに従い、仮想オーダーには毎日注文の始値として使用される開始価格(両ペアの)が必要です。1日の利益を計算するには、日々毎時のティック価格が終値(売りに対するアスク、買いに対するビッド)に対するデータとして、ティック時刻と共に記録される必要があります(ティック価格が同じ時刻値からのものであることを確認するため)。日次でヘッジを開始するコンセプトにより、必要なデータを2種類のファイルタイプに分けます。2組のペアの日次開始値とティック値のファイルです。2種類のデータタイプはどちらも文字列ファイルとして別の名前でエクスポートします。GBPJPYD1.csv と GBPJPYTick.csv などです。
仮想テスターが現実のテスターにできるだけ似たものとなるよう、望んだティックデータのために、以下の 2 ステップを行う必要があります。
- GBPJPY の始値を毎日ファイルにエクスポートするためのスクリプト作成
- GBPJPY のティック価格を毎日ファイルにエクスポートするためのスクリプト作成
どちらのステップも EURJPY に対しても行う必要があります。
ですが、私は両方を1件の expert advisor に融合することができると思います。この EA は 2 タイプのデータを 別個のファイル 2 件にエキスポートするのです。この EA がデータ記録処理を終えたら、仮想取引を作成する新規の EA が仮想検証を行うためにエキスポートされた全ファイルから GBPJPY と EURJPY 両方のデータをすべて取得します。
検証の限界を打ち破る 3 ステップ
上記の考えにより、私は、この限界を破るというわれわれの夢は以下の3ステップで実現可能であるという結論に達しました。
- 価格データを取り、EA を使用してそれをファイルに出力する。
- 結果をファイルとしてエクスポートする別の EA で仮想取引を作成する。
- 結果を別ウィンドウでインディケータとして再検討する。
では、第1ステップを始めます。
ステップ1:価格データをエクスポートする。
以下はアタッチされたシンボルのファイルへの日次始値をエクスポートする Expert Advisor です。GBPJPY に対して "GBPJPYD1.csv" 、EURJPY に対して "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 など数多くのインディケータのように、別ウィンドウにパフォーマンス曲線をプロットするためのインディケータとして記録されたデータをエクスポートすることにしました。そして、2 番目の 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); } //+------------------------------------------------------------------+
利用方法
私のコードのコピーをダウンロードする前に、ミニユーザーマニュアルとして、『利用方法』を簡潔に説明します。
期待を現実のものとするためには、必須の簡単なステップが5つあります。以下がそれです。
- テスター(仮想モードである必要はありません)では、"Expert Advisor:" メニューで symbol-D1.mq4 を選択します。そして "Symbol:" でお好みのヘッジペアの最初のヘッジシンボルを選択し、日時で期間を設定し、このシンボルが売り注文なら"For_OP_SELL" 値を真に、このシンボルが買い注文なら偽に設定します。「周期」メニューで毎時の周期を選択し、記録処理を実行するために『開始する』をクリックします。
- 2番目のヘッジシンボルに対してもステップ1同様に行います。このシンボルのオーダータイプに合わせるため、***パラメータ "For_OP_SELL" の変更を忘れないでください***。
- VirtualHedge.mq4 を選択し、変数をすべて設定したら、検証シンボルを選択します(お望みのシンボルはなんでも結構です)。ただ、ヘッジのパフォーマンスを確認するにはビジュアルモードが必要です。
- ヘッジのパフォーマンスを示す関連ファイルをすべてディレクトリ "program files/metatrader 4/tester/files"から "program files/metatrader 4/experts/files" へコピーします。(すべてをコピーするのに2つ以上のファイルを必要とする場合は、 GBPJPY_EURJPY_result1.csv と p.csv を含みます。)
- performance.mq4 を現在アクティブとなっている任意のチャートにアタッチし、現実に近いヘッジのパフォーマンスを確認します。
以下が私の実験ルールによるパフォーマンス曲線です。
おやおや!見やすいものではないですね。みなさんの結果はもっと良いものになっていると思います。
おわりに
私はヘッジ EA を検証する新たな一歩が踏み出せたことをうれしく思っています。テスターの限界はもうヘッジャーにとっても問題ではありません。ところで、 本稿におけるヘッジングのコンセプトは一例にすぎず、検証時間のプロセスを短くするために作成したものです。 みなさんのヘッジ戦略のために仮想テスターを作成するには、始値、終値、高値、安値その他タイプの重要なデータをリストアットする必要があります。相関関係を使ってトレードする場合は、各指定時刻のすべての相関値もエクスポートする必要があります。このリストにより、どのデータが記録され、計算され、また結果として出力されるべきか知ることになります。データ処理時間を短縮するために、検証期間期を小さく区切ることをお薦めします。そのほうが全期間を一度に処理するよりも都合よくいきます。たとえば、を1年間で検証したい場合、3か月ごとの4期間に分けるほうがよいと言えます。みなさんのパフォーマンス曲線が赤色でセクシーな女性のように表示されること、そのために本稿がヘッジャーのみなさんのすくなくともその結果の一部に役立つよう、またすくなくともみなさんがすばらしいヘッジ結果を出そうという気にさせることを願っています。最後にみなさんが本稿を楽しんでいただけますように!以下は私の仮想ヘッジパフォーマンスを記録した2分間ビデオです。(2007年03月19日~2007年04月19日)
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/1493
![初心者向け MQL4 言語テクニカルインディケータと組み込み関数](https://c.mql5.com/2/15/466_27.gif)
![トレーディングにおける数学:トレード結果の推定方法](https://c.mql5.com/2/14/442_223.gif)
![インディケータ代替一目均衡表–設定, 用例](https://c.mql5.com/2/14/339_13.jpg)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索