
エキスパートアドバイザの取引結果の評価
初めにテストの手順について少しお話したいと思います。テスト開始前に、テストのサブシステムはエキスパートアドバイザを起ち上げ、そこにユーザーによって指定されたパラメータを設定し、init()関数を呼び出します。それから、テスターは生成されたシーケンスを『再生』し、毎回start()関数を呼び出します。テストシーケンスが終了する時、テスターはdeinit()関数を呼び出します。その際、エキスパートアドバイザでは、テストの過程で形成された全取引履歴を利用することができます。この時、エキスパートアドバイザの動作を分析することができます。
下記のCalculateSummary関数は、ストラテジーテスターの標準レポートで与えられるデータの計算結果を提供します。
void CalculateSummary(double initial_deposit) { int sequence=0, profitseqs=0, lossseqs=0; double sequential=0.0, prevprofit=EMPTY_VALUE, drawdownpercent, drawdown; double maxpeak=initial_deposit, minpeak=initial_deposit, balance=initial_deposit; int trades_total=HistoryTotal(); //---- initialize summaries InitializeSummaries(initial_deposit); //---- for(int i=0; i<trades_total; i++) { if(!OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; int type=OrderType(); //---- initial balance not considered if(i==0 && type==OP_BALANCE) continue; //---- calculate profit double profit=OrderProfit()+OrderCommission()+OrderSwap(); balance+=profit; //---- drawdown check if(maxpeak<balance) { drawdown=maxpeak-minpeak; if(maxpeak!=0.0) { drawdownpercent=drawdown/maxpeak*100.0; if(RelDrawdownPercent<drawdownpercent) { RelDrawdownPercent=drawdownpercent; RelDrawdown=drawdown; } } if(MaxDrawdown<drawdown) { MaxDrawdown=drawdown; if(maxpeak!=0.0) MaxDrawdownPercent=MaxDrawdown/maxpeak*100.0; else MaxDrawdownPercent=100.0; } maxpeak=balance; minpeak=balance; } if(minpeak>balance) minpeak=balance; if(MaxLoss>balance) MaxLoss=balance; //---- market orders only if(type!=OP_BUY && type!=OP_SELL) continue; SummaryProfit+=profit; SummaryTrades++; if(type==OP_BUY) LongTrades++; else ShortTrades++; //---- loss trades if(profit<0) { LossTrades++; GrossLoss+=profit; if(MinProfit>profit) MinProfit=profit; //---- fortune changed if(prevprofit!=EMPTY_VALUE && prevprofit>=0) { if(ConProfitTrades1<sequence || (ConProfitTrades1==sequence && ConProfit2<sequential)) { ConProfitTrades1=sequence; ConProfit1=sequential; } if(ConProfit2<sequential || (ConProfit2==sequential && ConProfitTrades1<sequence)) { ConProfit2=sequential; ConProfitTrades2=sequence; } profitseqs++; AvgConWinners+=sequence; sequence=0; sequential=0.0; } } //---- profit trades (profit>=0) else { ProfitTrades++; if(type==OP_BUY) WinLongTrades++; if(type==OP_SELL) WinShortTrades++; GrossProfit+=profit; if(MaxProfit<profit) MaxProfit=profit; //---- fortune changed if(prevprofit!=EMPTY_VALUE && prevprofit<0) { if(ConLossTrades1<sequence || (ConLossTrades1==sequence && ConLoss2>sequential)) { ConLossTrades1=sequence; ConLoss1=sequential; } if(ConLoss2>sequential || (ConLoss2==sequential && ConLossTrades1<sequence)) { ConLoss2=sequential; ConLossTrades2=sequence; } lossseqs++; AvgConLosers+=sequence; sequence=0; sequential=0.0; } } sequence++; sequential+=profit; //---- prevprofit=profit; } //---- final drawdown check drawdown=maxpeak-minpeak; if(maxpeak!=0.0) { drawdownpercent=drawdown/maxpeak*100.0; if(RelDrawdownPercent<drawdownpercent) { RelDrawdownPercent=drawdownpercent; RelDrawdown=drawdown; } } if(MaxDrawdown<drawdown) { MaxDrawdown=drawdown; if(maxpeak!=0) MaxDrawdownPercent=MaxDrawdown/maxpeak*100.0; else MaxDrawdownPercent=100.0; } //---- consider last trade if(prevprofit!=EMPTY_VALUE) { profit=prevprofit; if(profit<0) { if(ConLossTrades1<sequence || (ConLossTrades1==sequence && ConLoss2>sequential)) { ConLossTrades1=sequence; ConLoss1=sequential; } if(ConLoss2>sequential || (ConLoss2==sequential && ConLossTrades1<sequence)) { ConLoss2=sequential; ConLossTrades2=sequence; } lossseqs++; AvgConLosers+=sequence; } else { if(ConProfitTrades1<sequence || (ConProfitTrades1==sequence && ConProfit2<sequential)) { ConProfitTrades1=sequence; ConProfit1=sequential; } if(ConProfit2<sequential || (ConProfit2==sequential && ConProfitTrades1<sequence)) { ConProfit2=sequential; ConProfitTrades2=sequence; } profitseqs++; AvgConWinners+=sequence; } } //---- collecting done double dnum, profitkoef=0.0, losskoef=0.0, avgprofit=0.0, avgloss=0.0; //---- average consecutive wins and losses dnum=AvgConWinners; if(profitseqs>0) AvgConWinners=dnum/profitseqs+0.5; dnum=AvgConLosers; if(lossseqs>0) AvgConLosers=dnum/lossseqs+0.5; //---- absolute values if(GrossLoss<0.0) GrossLoss*=-1.0; if(MinProfit<0.0) MinProfit*=-1.0; if(ConLoss1<0.0) ConLoss1*=-1.0; if(ConLoss2<0.0) ConLoss2*=-1.0; //---- profit factor if(GrossLoss>0.0) ProfitFactor=GrossProfit/GrossLoss; //---- expected payoff if(ProfitTrades>0) avgprofit=GrossProfit/ProfitTrades; if(LossTrades>0) avgloss =GrossLoss/LossTrades; if(SummaryTrades>0) { profitkoef=1.0*ProfitTrades/SummaryTrades; losskoef=1.0*LossTrades/SummaryTrades; ExpectedPayoff=profitkoef*avgprofit-losskoef*avgloss; } //---- absolute drawdown AbsoluteDrawdown=initial_deposit-MaxLoss; }
正しい計算の為には、初期デポジットの値を知る必要があります。その為には、init()関数でテスト開始時に残高の値を与える、AccountBalance()関数を呼び出す必要があります。
void init()
{
ExtInitialDeposit=AccountBalance();
}
上記のCalculateSummary関数では、標準レポートのように、利益はデポジットの通貨で計算されます。そしてその他の取引結果は、『最大利益取引』や『最大連続損失』のように、利益をベースに計算され、同様に金額での測定があります。私たちがポイントで利益を算出することを望む場合、これはとても簡単に行うことができます。
... //---- market orders only if(type!=OP_BUY && type!=OP_SELL) continue; //---- calculate profit in points profit=(OrderClosePrice()-OrderOpenPrice())/MarketInfo(OrderSymbol(),MODE_POINT); SummaryProfit+=profit; ...
WriteReport関数を使って、取得した結果をレポートファイルへ書き込むことができます。
void WriteReport(string report_name) { int handle=FileOpen(report_name,FILE_CSV|FILE_WRITE,'\t'); if(handle<1) return; //---- FileWrite(handle,"Initial deposit ",InitialDeposit); FileWrite(handle,"Total net profit ",SummaryProfit); FileWrite(handle,"Gross profit ",GrossProfit); FileWrite(handle,"Gross loss ",GrossLoss); if(GrossLoss>0.0) FileWrite(handle,"Profit factor ",ProfitFactor); FileWrite(handle,"Expected payoff ",ExpectedPayoff); FileWrite(handle,"Absolute drawdown ",AbsoluteDrawdown); FileWrite(handle,"Maximal drawdown ", MaxDrawdown, StringConcatenate("(",MaxDrawdownPercent,"%)")); FileWrite(handle,"Relative drawdown ", StringConcatenate(RelDrawdownPercent,"%"), StringConcatenate("(",RelDrawdown,")")); FileWrite(handle,"Trades total ",SummaryTrades); if(ShortTrades>0) FileWrite(handle,"Short positions(won %) ", ShortTrades, StringConcatenate("(",100.0*WinShortTrades/ShortTrades,"%)")); if(LongTrades>0) FileWrite(handle,"Long positions(won %) ", LongTrades, StringConcatenate("(",100.0*WinLongTrades/LongTrades,"%)")); if(ProfitTrades>0) FileWrite(handle,"Profit trades (% of total)", ProfitTrades, StringConcatenate("(",100.0*ProfitTrades/SummaryTrades,"%)")); if(LossTrades>0) FileWrite(handle,"Loss trades (% of total) ", LossTrades, StringConcatenate("(",100.0*LossTrades/SummaryTrades,"%)")); FileWrite(handle,"Largest profit trade ",MaxProfit); FileWrite(handle,"Largest loss trade ",-MinProfit); if(ProfitTrades>0) FileWrite(handle,"Average profit trade ",GrossProfit/ProfitTrades); if(LossTrades>0) FileWrite(handle,"Average loss trade ",-GrossLoss/LossTrades); FileWrite(handle,"Average consecutive wins ",AvgConWinners); FileWrite(handle,"Average consecutive losses",AvgConLosers); FileWrite(handle,"Maximum consecutive wins (profit in money)", ConProfitTrades1, StringConcatenate("(",ConProfit1,")")); FileWrite(handle,"Maximum consecutive losses (loss in money)", ConLossTrades1, StringConcatenate("(",-ConLoss1,")")); FileWrite(handle,"Maximal consecutive profit (count of wins)", ConProfit2, StringConcatenate("(",ConProfitTrades2,")")); FileWrite(handle,"Maximal consecutive loss (count of losses)", -ConLoss2, StringConcatenate("(",ConLossTrades2,")")); //---- FileClose(handle); }
レポートの作成例は以下のようになります。
void deinit() { if(!IsOptimization()) { if(!IsTesting()) ExtInitialDeposit=CalculateInitialDeposit(); CalculateSummary(ExtInitialDeposit); WriteReport("MACD_Sample_Report.txt"); } }
この例でわかるように、レポートはテストの後だけでなく、実際に動作しているエキスパートアドバイザの非初期化時にも作成することができます。もし顧客ターミナルに取引履歴が部分的にダウンロードされた場合、初期デポジットのサイズを知るにはどうしたら良いかという疑問が沸いてきます。CalculateInitialDeposit関数がこの問題を解決してくれます。
double CalculateInitialDeposit() { double initial_deposit=AccountBalance(); //---- for(int i=HistoryTotal()-1; i>=0; i--) { if(!OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; int type=OrderType(); //---- initial balance not considered if(i==0 && type==OP_BALANCE) break; if(type==OP_BUY || type==OP_SELL) { //---- calculate profit double profit=OrderProfit()+OrderCommission()+OrderSwap(); //---- and decrease balance initial_deposit-=profit; } if(type==OP_BALANCE || type==OP_CREDIT) initial_deposit-=OrderProfit(); } //---- return(initial_deposit); }
このようにして、MetaTrader 4の顧客ターミナルでは、レポートが作成されます。
自力で計算したデータと比較することができます。
Initial deposit 10000 Total net profit -13.16 Gross profit 20363.32 Gross loss 20376.48 Profit factor 0.99935416 Expected payoff -0.01602923 Absolute drawdown 404.28 Maximal drawdown 1306.36 (11.5677%) Relative drawdown 11.5966% (1289.78) Trades total 821 Short positions(won %) 419 (24.821%) Long positions(won %) 402 (31.592%) Profit trades (% of total) 231 (28.1364%) Loss trades (% of total) 590 (71.8636%) Largest profit trade 678.08 Largest loss trade -250 Average profit trade 88.15290043 Average loss trade -34.53640678 Average consecutive wins 1 Average consecutive losses 4 Maximum consecutive wins (profit in money) 4 (355.58) Maximum consecutive losses (loss in money) 15 (-314.74) Maximal consecutive profit (count of wins) 679.4 (2) Maximal consecutive loss (count of losses) -617.16 (8)
この記事に添付されたSummaryReport.mq4ファイルは、experts\includeディレクトリに保存し、#includeディレクティブを使用し連結させることをお勧めします。
#include <SummaryReport.mq4> double ExtInitialDeposit;
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1403



- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索