トレーダーのライフハック: テストの比較レポート
コンテンツ
- イントロダクション
- 必要な操作
- 1. エントリーパラメータをテストするEAを選択。
- 2. Common.ini について
- 2.1. common.ini -> original.ini
- 2.2. 正規表現を使用して[共通] セクションを検索。
- 2.3. 4つのファイルを作成する: myconfiguration1.ini、myconfiguration2.ini、myconfiguration3.ini、myconfiguration4.ini
- 2.4. ini ファイルの編集 (個々 の [テスター] セクションおよび [共通] セクションをコピー)
- 3. 解析および選択したEAの mq5 ファイルを編集
- 3.1#3
- 3.2. "#include"を含む
- 3.3."double OnTester()"のインクルード
- 3.4. 複雑な例: コードに DistributionOfProfits.mqh や OnTester() が既に含まれている
- 4. EAをスレーブターミナルのフォルダーにコピー。
- 5. スレーブターミナル
- 6. 比較レポート
- 結論
イントロダクション
異なるシンボルで複数回に渡りEAをテストし、結果を比較することは意外と面倒を伴う作業です。このアプローチを変更し、同時にEAのテストをすることをお勧めします。この場合、テスト結果は 1 つの場所に保存することができ、視覚的に比較することができます。
このソリューションは、すでに以前の記事で説明されています。
シナリオのテスト。
- (Win API) をテストするためのEAの選択
- EAコードを解析し、グラフィカルなレポート ライブラリの呼び出しを追加 (Win API、mql5 をおよび正規表現)
- メイン ターミナルの common.ini の解析と各ターミナル (Win API、mql5 をおよび正規表現) のcommon.ini の準備
- 個々 の common.ini をターミナル (Win API) のフォルダーにコピーします。
- 個々 の common.ini をターミナル (Win API) のフォルダーにコピーします。
- スレーブターミナルの解析レポート
- 1つの一般的なレポートにスレーブターミナルの結果を追加
必要な操作
EAを起動する前に、マスターとスレーブターミナルを同期する必要があります。
- マスターとスレーブターミナルは、同じトレード口座で接続する必要があります。
- すべてのスレーブターミナルの設定で Dll の使用を許可します。\Portable キーでターミナルを起動した場合 (他のファイル マネージャーまたはエクスプ ローラーを使用) ターミナルのインストール ディレクトリをエントリーターミナル"terminal64.exe"を起動し、"DLL のインポートを許可する"を設定します。
- "DistributionOfProfits.mqh"ライブラリは、スレーブターミナルのすべてのデータ フォルダー (folder\MQL5\Include\DistributionOfProfits.mqh データ) に追加する必要があります。
1. インプットパラメータ。テストするEAを選択します。
このコンピューターには4つのコアがあるので、4つのテスト エージェントを実行できます。したがって、同時に (数秒の遅延で)、1つターミナル エージェントあたり 4 つ実行できます。よって、4つのグループがエントリーパラメータで利用可能です。
パラメータ:
- メタト レーダー #ххх インストールのフォルダー
- ターミナル #xxx のテストのシンボル
- ターミナル #xxx のテスト期間
- ターミナルのファイルの正確な名前
- ミリ秒単位でスリープ-スレーブターミナルの開始間の一時ストップ
- テストの (年、月、日のみ) 開始年月日
- テストの終わりの日付 (年、月、日のみ)
- 最初の資産
- レバレッジ
基本的なアルゴリズムの開始する前に、スレーブターミナルと AppData フォルダーにデータ ディレクトリのインストール先のフォルダーのリンクをする必要があります。ここでは、簡単なスクリプトの例を使いますCheck_TerminalPaths.mq5:
//+------------------------------------------------------------------+ //| Check_TerminalPaths.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //|プログラム開始関数のスクリプト | //+------------------------------------------------------------------+ void OnStart() { //--- Print("TERMINAL_PATH = ",TerminalInfoString(TERMINAL_PATH)); Print("TERMINAL_DATA_PATH = ",TerminalInfoString(TERMINAL_DATA_PATH)); Print("TERMINAL_COMMONDATA_PATH = ",TerminalInfoString(TERMINAL_COMMONDATA_PATH)); } //+------------------------------------------------------------------+
スクリプトは、3 つのパラメータを出力します。
- TERMINAL_PATH-フォルダーのターミナルを実行しています。
- TERMINAL_DATA_PATH-ターミナルのデータが格納されているフォルダー
- コンピューターにインストールされているすべてのクライアントターミナル用 TERMINAL_COMMONDATA_PATH-共通のフォルダー
3 つの端子の例 ( ポータブルキーが実行されている)。
//メインのモードでターミナルを起動します。 TERMINAL_PATH = C:\Program Files\MetaTrader 5 TERMINAL_DATA_PATH = C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075 TERMINAL_COMMONDATA_PATH = C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\Common //メインのモードでターミナルを起動します。 TERMINAL_PATH = D:\MetaTrader 5 3 TERMINAL_DATA_PATH = C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\0C46DDCEB43080B0EC647E0C66170465 TERMINAL_COMMONDATA_PATH = C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\Common //ポータブル モードでターミナルを起動します。 TERMINAL_PATH = D:\MetaTrader 5 5 TERMINAL_DATA_PATH = D:\MetaTrader 5 5 TERMINAL_COMMONDATA_PATH = C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\Common
AppData 内のフォルダーの対応について、ターミナル フォルダーと以前の記事で続きを読むことができます。
EAは、"ファイルを開く"システム ダイアログ ( GetOpenFileNameW関数) を使用して選択されます。
ファイルを開くダイアログの呼び出しの詳細については、以前の記事で説明した"トレーダーのライフハック: 4つのバックテストが 1つよりも優れている": 4.2を参照してください。EAのシステムの"ファイルを開く"ダイアログ ボックスを選択します。
OpenFileName 関数の現在のバージョン (ファイル GetOpenFileNameW.mqh、バージョン 1.003) の変化:
//+------------------------------------------------------------------+ //|[開く] ダイアログ ボックスを作成します | //+------------------------------------------------------------------+ string OpenFileName(const string filter_description="Editable code", const string filter="\0*.mq5\0", const string title="Select source file") { string path=NULL; if(GetOpenFileName(path,filter_description+filter,TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Experts\\",title)) return(path); else { PrintFormat("Failed with error: %x",kernel32::GetLastError()); return(NULL); } }
ファイルの検索フィルターを設定する方が便利になりました。また、*.mq5 形式のファイルの検索に注意をしてください (コンパイルされた以前の記事 * ex5 ファイルを検索)。
2. Common.ini について
CopyCommonIni() 関数の 'tests.mq5の比較' 操作をする時間です。
スレーブターミナルは、構成ファイルを指定することによって起動されます。4 つの *.ini ファイルを作成する必要があります。: myconfiguration1.ini、myconfiguration2.ini、myconfiguration3.ini、myconfiguration4.ini。EAを起動した端末の common.ini ファイルに基づいて myconfigurationХ.ini ファイルが作成されます。Common.ini ファイルへのパス:
TERMINAL_DATA_PATH\config\common.ini
myconfiguration.ini ファイルを編集するためのアルゴリズムは、このようになります。
- Common.ini を TERMINAL_COMMONDATA_PATH\Files\original.ini (WinAPI CopyFileW) フォルダーにコピーします。
- Original.ini ファイルのセクション [共通] (mql5 を + 正規表現) を検索します。
メインターミナル (ターミナルがサインインしていない mql5.community) のようなセクションがどのように記述されます。
[Common] Login=5116256 ProxyEnable=0 ProxyType=0 ProxyAddress= ProxyAuth= CertInstall=0 NewsEnable=0 NewsLanguages=
- 4ファイルの生成: myconfiguration1.ini, myconfiguration2.ini, myconfiguration3.ini и myconfiguration4.ini (MQL5)
- 4つのファイル ([共通] のコピーと個々 の [テスター] セクション) を編集 (mql5)
2.1. common.ini -> original.ini
おそらく、最も簡単なコード: 変数に"データ フォルダー"と"共有データ フォルダー"パスを受信し、"original.ini"の値を持つ変数を初期化
string terminal_data_path=TerminalInfoString(TERMINAL_DATA_PATH); //データ フォルダーへのパス string common_data_path=TerminalInfoString(TERMINAL_COMMONDATA_PATH);//共通のデータ フォルダーへのパス string original_ini="original.ini"; string arr_common[]; //--- string full_name_common_ini=terminal_data_path+"\\config\\common.ini"; //common.ini ファイルへの完全パス string full_name_original_ini=common_data_path+"\\Files\\"+original_ini; //original.ini ファイルへの完全パス //---original.ini-> common.ini if(!CopyFileW(full_name_common_ini,full_name_original_ini,false)) { PrintFormat("Failed with error: %x",kernel32::GetLastError()); return(false); }
"Common.ini""original.ini"CopyFileW 関数の勝つ API を使用して構成ファイルをコピーします。
[共通] セクションをコピーするために正規表現を使用します。Common.ini ファイルは、ターミナルの改行文字が常に目に見えない文字で構成されていますので、通常のタスクではありません。2つの方法があります。
一度に 1 行を読み取り | 1つの変数にファイル全体を読み込む |
---|---|
|
|
ファイル"test_original.ini"をテストします。
[Charts] ProfileLast=Default MaxBars=100000 PrintColor=0 SaveDeleted=0 TradeLevels=1 TradeLevelsDrag=0 ObsoleteLasttime=1475473485 [Common] Login=1783501 ProxyEnable=0 ProxyType=0 ProxyAddress= ProxyAuth= CertInstall=0 NewsEnable=0 [Tester] Expert=test Symbol=EURUSD Period=H1 Deposit=10000 Model=4 Optimization=0 FromDate=2016.01.22 ToDate=2016.06.06 Report=TesterReport ReplaceReport=1 UseLocal=1 Port=3000 Visual=0 ShutdownTerminal=0
"Test_original.ini"ファイルは、"lines.mq5 を受信"スクリプトを使用して正規表現の使い方のトレーニングに使用できます。スクリプトの設定で 2 つの操作モードを選択できます。
- 一度に 1 行を読み取り、検索します
- 1つの変数にファイル全体を読み込む
2つのメソッドを比較する:
一度に 1 行を読み取り | 1つの変数にファイル全体を読み込む |
---|---|
Query: "Prox(.*)0" -"Prox"の検索 (greedy)"(.*) 倍ゼロ以上を発見した Unicode 文字列の別の区切り文字または改行文字を除く任意のシンボルに続く -" -"0"が見つかると検索を終了する必要があります | |
12: 0: ProxyEnable=0, 13: 0: ProxyType=0, | : 0: ProxyEnable=0ProxyType=0ProxyAddress=ProxyAuth=CertInstall=0NewsEnable=0[Tester]Expert=test Symbol=EURUSD Period=H1 Deposit=10000 Model=4 Optimization=0 FromDate=2016.01.22 ToDate=2016.06.06 Report=TesterReport ReplaceReport=1 UseLocal=1 Port=3000 Visual=0 ShutdownTerminal=0, |
2つの結果 | この結果には、多くの不必要な項目 (貪欲なリクエストの結果) があります。 |
Query: "Prox(.*?)0" - search for "Prox" -改行文字または Unicode 文字列の別の区切り文字を除く任意のシンボルに続いて、0 回以上見つかる (greedy)"(.*)" -"0"の数が見つかると検索を終了する必要があります | |
12: 0: ProxyEnable=0, 13: 0: ProxyType=0, | : 0: ProxyEnable=0, 1: ProxyType=0, 2: ProxyAddress=ProxyAuth=CertInstall=0, |
もう一度ここで 2 つの結果を見ます。 | この場合、3番目の結果は期待されるものではないです。 |
"[共通]"ブロック全体を抽出するため使用するメソッドでは、一度に 1 行を読み取ります。次のアルゴリズムと時間で 1 行を読み取ることにしました。
- searching for "[Common]" (MQL5);
- 行が見つかると、配列に書き込みます。
- "["シンボル正規表現を見つけるまで配列に行を書き込み続ける。
このアプローチの例は、"Receiving lines v.2.mq"スクリプトで実装されます。
//+------------------------------------------------------------------+ //| Receiving lines v.2.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.000" #property description "Singling the block \"[Common]\"" #property script_show_inputs #include <RegularExpressions\Regex.mqh> //--- input string file_name="test_original.ini"; //ファイル名 input string str_format="(\\[)(.*?)(\\])"; //--- int m_handel; bool m_found_Common=false; //"[共通]"の発見後、フラグが true になります //+------------------------------------------------------------------+ //|プログラム開始関数のスクリプト | //+------------------------------------------------------------------+ void OnStart() { string arr_text[]; //rezult配列 //--- Print("format: ",str_format); m_handel=FileOpen(file_name,FILE_READ|FILE_ANSI|FILE_TXT); if(m_handel==INVALID_HANDLE) { Print("Operation FileOpen failed, error ",GetLastError()); return; } Regex *rgx=new Regex(str_format); while(!FileIsEnding(m_handel)) { string str=FileReadString(m_handel); if(str=="[Common]") { m_found_Common=true; int size=ArraySize(arr_text); ArrayResize(arr_text,size+1,10); arr_text[size]=str; continue; // goto while... } if(m_found_Common) { MatchCollection *matches=rgx.Matches(str); int count=matches.Count(); if(count>0) { if(count>1) { Print("Alarm!matches.Count()==",count); return; } delete matches; break; // goto FileClose... } else { delete matches; //見つからなかった場合 } int size=ArraySize(arr_text); ArrayResize(arr_text,size+1,10); arr_text[size]=str; } } FileClose(m_handel); delete rgx; Regex::ClearCache(); //---テスト int size=ArraySize(arr_text); for(int i=0;i<size;i++) { Print(arr_text[i]); } } //+------------------------------------------------------------------+
スクリプトの実行結果:
2016.10.05 06:58:09.276 Receiving lines v.2 (EURUSD,M1) format: (\[)(.*?)(\]) 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) [Common] 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) Login=1783501 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) ProxyEnable=0 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) ProxyType=0 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) ProxyAddress= 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) ProxyAuth= 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) CertInstall=0 2016.10.05 06:58:09.277 Receiving lines v.2 (EURUSD,M1) NewsEnable=0
スクリプトが正確に"test_original.ini"ファイルからパラメータの"[共通]"ブロックを発見します。SearchBlock() 関数で"Receiving lines v.2.mq5"スクリプトのほとんど変更されていないアルゴリズムを使用します。"[共通]"ブロックが連続している場合、SearchBlock() 関数は配列のサービス [arr_common] にこのブロックを書き込みます。
2.3. 4つのファイルを作成する: myconfiguration1.ini、myconfiguration2.ini、myconfiguration3.ini、myconfiguration4.ini
次のコードの連続呼び出しによって 4 つのファイルが作成されます (ファイルを開くときに使用するフラグに注意してください)。
//+------------------------------------------------------------------+ //|Open new File | //+------------------------------------------------------------------+ bool IniFileOpen(const string name_file,int &handle) { handle=FileOpen(name_file,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON); if(handle==INVALID_HANDLE) { Print("Operation FileOpen file ",name_file," failed, error ",GetLastError()); return(false); } //--- return(true); }
2.4. ini ファイルの編集 (個々 の [テスター] セクションおよび [共通] セクションをコピー)
パラメータの"[共通]"ブロックは配列のサービス [arr_common] に書かれています。この配列は、すべての 4 つのファイルに書き込まれます:
//---ブロック"[共通]"の記録 int arr_common_size=ArraySize(arr_common); for(int i=0;i<arr_common_size;i++) { FileWrite(handle1,arr_common[i]); FileWrite(handle2,arr_common[i]); FileWrite(handle3,arr_common[i]); FileWrite(handle4,arr_common[i]); } //---ブロック"[テスター]"記録 string expert_short_name="D0E820_test"; WriteBlockTester(handle1,expert_short_name,ExtTerminal1Symbol,ExtTerminal1Timeframes,ExtDeposit, ExtLeverage,ExtTerminaTick,ExtFromDate,ExtToDate,expert_short_name,3000); WriteBlockTester(handle2,expert_short_name,ExtTerminal2Symbol,ExtTerminal2Timeframes,ExtDeposit, ExtLeverage,ExtTerminaTick,ExtFromDate,ExtToDate,expert_short_name,3001); WriteBlockTester(handle3,expert_short_name,ExtTerminal3Symbol,ExtTerminal3Timeframes,ExtDeposit, ExtLeverage,ExtTerminaTick,ExtFromDate,ExtToDate,expert_short_name,3002); WriteBlockTester(handle4,expert_short_name,ExtTerminal4Symbol,ExtTerminal4Timeframes,ExtDeposit, ExtLeverage,ExtTerminaTick,ExtFromDate,ExtToDate,expert_short_name,3003); //---ファイルを閉じる FileClose(handle1); FileClose(handle2); FileClose(handle3); FileClose(handle4);
パラメータの [テスター] ブロックを形成し、: 各ターミナルの固有パラメータ (シンボルおよび時間枠) と (開始と終了の日付、デポジット、レバレッジをテスト) に共通のパラメータを用意します。
作成されたファイル myconfiguration1.ini、myconfiguration2.ini、myconfiguration3.ini、および myconfiguration4.ini は、共通のデータ フォルダー (TERMINAL_COMMONDATA_PATH\Files\) に保存されます。ファイルのハンドルを閉じる必要があります。
3. 解析および選択したEAの mq5 ファイルを編集
解決すべき問題。
- (戦略テスターから分析チャートの実行を参照) ファイル インクルードのグラフィカルな解析の呼び出しを追加する;
- グラフィカルな解析コールと OnTester() 関数のEAに統合。
なぜシークレットナンバー3でしょうか。#1と#2は以前のトレーダーのライフハック: 4つのバックテストが 1 つよりも優れているで紹介されています。
次のシナリオを検討してみましょう: ターミナルがコマンド ・ ラインから起動され、同時に設定の ini ファイルが指定されています。Ini ファイルには、ターミナルの開始でテスターでEAの名前を指定します。この場合、 まだコンパイルされていないEAの名前を指定して覚えておいてください。
#3
EAの名前を拡張子なしに書き込まれます。この記事の中でどのように見えるか:
NewsEnable=0 [Tester] Expert=D0E820_test Symbol=GBPAUD
起動時、ターミナルコンパイル の最初の検索をします(この記事の中で、ターミナルが検索されます Expert=D0E820_test.ex5)。ターミナルは、コンパイルされたファイルを見つけることができない場合にのみ、ini ファイルで指定されたEAのコンパイルが開始されます。
このため、EAを編集する前に、そのスレーブターミナルのフォルダーを通過し、'D0E820_test.ex5' のファイルを削除する必要があります。DeleteFileW Win API 関数を使用してファイルを削除します。
return(INIT_FAILED);
//---すべてのファイルを削除: expert_short_name +".ex5"
ResetLastError();
string common_data_path=TerminalInfoString(TERMINAL_COMMONDATA_PATH); //共通のデータ フォルダーへのパス
//---
string edited_expert=common_data_path+"\\Files\\"+expert_short_name+".mq5";
//--- delete expert_short_name+".ex5" files
string compiled_expert=expert_short_name+".ex5";
DeleteFileW(slaveTerminalDataPath1+"\\MQL5\\Experts\\"+compiled_expert);
DeleteFileW(slaveTerminalDataPath2+"\\MQL5\\Experts\\"+compiled_expert);
DeleteFileW(slaveTerminalDataPath3+"\\MQL5\\Experts\\"+compiled_expert);
DeleteFileW(slaveTerminalDataPath4+"\\MQL5\\Experts\\"+compiled_expert);
//---expert_short_name +".set"ファイルを削除
*.set ファイルを削除する必要があります。その理由は、EAのエントリーパラメータを編集すると、以前の実行中に使用されるパラメータが開始されるからです。よって *.set ファイルを削除します。
string set_files=expert_short_name+".set";
DeleteFileW(slaveTerminalDataPath1+"\\Tester\\"+set_files);
DeleteFileW(slaveTerminalDataPath2+"\\Tester\\"+set_files);
DeleteFileW(slaveTerminalDataPath3+"\\Tester\\"+set_files);
DeleteFileW(slaveTerminalDataPath4+"\\Tester\\"+set_files);
//---expert_short_name +".htm"ファイル (レポート) を削除
また、スレーブターミナルのフォルダーからテスター レポート ファイルを削除します。
//---expert_short_name +".htm"ファイル (レポート) を削除
string file_report=expert_short_name+".htm";
DeleteFileW(slaveTerminalDataPath1+"\\"+file_report);
DeleteFileW(slaveTerminalDataPath2+"\\"+file_report);
DeleteFileW(slaveTerminalDataPath3+"\\"+file_report);
DeleteFileW(slaveTerminalDataPath4+"\\"+file_report);
//--- сopying an expert in the TERMINAL_COMMONDATA_PATH\Files folder
if(!CopyFileW(expert_full_name,edited_expert,false))
なぜレポートのファイルを削除する必要があるのでしょうか。レポートを削除すると、その後検査結果の複数のシンボルの比較ページを作成するためにファイルを解析できるすべてのスレーブターミナルで、新しいレポート ファイルを作成するときの瞬間をスポットすることができます。
コンパイル済みのファイルを削除した後に、mql5 をツールを使用するファイルと更なるTERMINAL_COMMONDATA_PATH フォルダーに選択したのEA ファイルをコピーできます。
//--- сopying an expert in the TERMINAL_COMMONDATA_PATH\Files folder
if(!CopyFileW(expert_full_name,edited_expert,false))
{
PrintFormat("Failed CopyFileW expert_full_name with error: %x",kernel32::GetLastError());
return(INIT_FAILED);
}
//---EAファイルの解析
複数の tests.mq5::ParsingEA() を比較します。
一般的に、EAファイルに"#include <DistributionOfProfits.mqh>"が既に含まれているかどうかを決定する必要があります。ない場合、EAにこの行を追加する必要があります。しかし、さまざまな相違点があります。
Variants | Good/bad |
---|---|
"#include <DistributionOfProfits.mqh>" | 良いバリアント (理想的な) |
"#include<DistributionOfProfits.mqh>" | 良 (タブ、スペースではなく、このバリアント"#include"が続きます) |
"#include <DistributionOfProfits.mqh>" | 良 (このバリアントは、スペースの代わりに"#include"の前にタブを使用) |
"//#include <DistributionOfProfits.mqh>" | 悪いバリアント (シンプルコメント) |
また、"#include"が続く、スペースではなくタブ文字または複数のスペースでバリエーションが可能です。よって次の正規表現は、検索に対して作成されています。
"(\\s+?#include|^#include)(.*?)(<DistributionOfProfits.mqh)"
(\\s+?#include|^#include) である: (1 つ以上のスペース、greedyではない、そして"#include") または ("#include"で始まる)。NumberRegulars()関数を使用して検索を実行します。新しい変数の導入:"name_Object_CDistributionOfProfits"、 CDistributionOfProfits オブジェクト名が保存されます。複雑な検索を実行する必要がある場合、後で役に立ちます。
//+------------------------------------------------------------------+ //| Insert #include <DistributionOfProfits.mqh> | //|トレードのグラフィカルな分析を挿入 | //+------------------------------------------------------------------+ boolParsingEA() { //---#include <DistributionOfProfits.mqh> を見つける int number=0; string name_Object_CDistributionOfProfits="ExtDistribution"; //CDistributionOfProfits オブジェクト名 string expressions="(\\s+?#include|^#include)(.*?)(<DistributionOfProfits.mqh)"; if(!NumberRegulars(expert_short_name+".mq5",expressions,number)) return(false); if(number==0) //正規表現が見つからない { //---#include <DistributionOfProfits.mqh> を追加 string array[]; ArrayResize(array,2); array[0]="#include <DistributionOfProfits.mqh>"; array[1]="CDistributionOfProfits "+name_Object_CDistributionOfProfits+";"; if(!InsertLine(expert_short_name+".mq5",0,array)) return(false); Print("Line \"#include\" is insert");
文字列が見つからない場合、EA ( InsertLine() 関数) に挿入する必要があります。動作原理は次のように: 一時的な配列にEA行を読み込みます。行番号には、設定する"ポジション"が一致すると、コードの適切な部分は (コード部分は"テキスト"配列から取得) 配列に挿入されます。読み取りが完了すると、エキスパート アドバイザー ファイルは削除され、同じ名前を持つ新しいファイルが作成されます。一時的な配列からの情報は、ファイルに書き込まれます。
//+------------------------------------------------------------------+ //|ファイルに行を挿入する | //+------------------------------------------------------------------+ bool InsertLine(const string name_file,const uint position,string &array_text[]) { int handle; int size_arr=ArraySize(array_text); //--- handle=FileOpen(name_file,FILE_READ|FILE_ANSI|FILE_TXT|FILE_COMMON); if(handle==INVALID_HANDLE) { Print("Operation FileOpen file ",name_file," failed, error ",GetLastError()); return(false); } int line=0; string arr_temp[]; ArrayResize(arr_temp,0,1000); while(!FileIsEnding(handle)) { string str_text=FileReadString(handle,-1); if(line==position) { for(int i=0;i<size_arr;i++) { int size=ArraySize(arr_temp); ArrayResize(arr_temp,size+1,1000); arr_temp[size]=array_text[i]; } } int size=ArraySize(arr_temp); ArrayResize(arr_temp,size+1,1000); arr_temp[size]=str_text; line++; } FileClose(handle); FileDelete(name_file,FILE_COMMON); //--- handle=FileOpen(name_file,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON); if(handle==INVALID_HANDLE) { Print("Operation FileOpen file ",name_file," failed, error ",GetLastError()); return(false); } int size=ArraySize(arr_temp); for(int i=0;i<size;i++) { FileWrite(handle,arr_temp[i]); } FileClose(handle); //--- return(true); }
このタスク以来、"OnTester"という言葉は、多くの異なるバリエーションのプログラム コードで発生する可能性が非常に難しくなります。たとえば、シンプルなケースでない"OnTester"を使用する場合です。古典的なバージョンは次のとおりです。
double OnTester() {
これは非常にむずかしい。開発者が異なり、時々、次のプログラミング スタイルを満たすことができます。
double OnTester() {
おそらく最も困難なケースの一つ:
/*
//+-------------------------------+
//| |
//+-------------------------------+
double OnTester()
{
...
}
...
*/
よって、OnTetster 関数の宣言がコードに含まれているかどうかを調べるには、次の正規表現を使いましょう。
"(\\s+?double|^double)(.+?)(OnTester\\(\\))(.*)" | |
"(\\s+?double" | \\sスペースに少なくとも 1 回発生するスペース\\s+ \\s+? 、少なくとも 1 回発生する、最短一致演算子、 \\s+?double少なくとも 1 回発生する空間領域、演算子、および"double"。 |
"|" | | or |
"^double)" | doubleの単語で始まる |
"(.+?)" | .改行文字または Unicode 文字列の他の区切り文字を除く任意のシンボル. +改行文字または Unicode 文字列、発生する1 つまたは複数回の他の区切り文字を除く任意のシンボル. +? 改行文字または Unicode 文字列、発生する 1 つまたは複数回、最短の他の区切りシンボルを除くすべてのシンボル |
"(OnTester\\(\\))" | OnTester\\(\\) OnTester() |
"(.*)" | .改行文字または Unicode 文字列の他の区切り文字を除く任意のシンボル. *改行文字や Unicode 文字列発生ゼロより多くその他の区切りシンボルを除くすべてのシンボル |
最もシンプルな場合、正規表現は、検索後にゼロを返す、 OnTester() 関数の呼び出しを挿入します。
//--- expressions="(\\s+?double|^double)(.+?)(OnTester\\(\\))(.*)"; if(!NumberRegulars(expert_short_name+".mq5",expressions,number)) return(false); if(number==0) //正規表現が見つからない { //---関数 OnTester を追加 if(!InsertLine(expert_short_name+".mq5",2, "double OnTester()"+ " {"+ " double ret=0.0;"+ "ExtDistribution.AnalysisTradingHistory(0);"+ "ExtDistribution.ShowDistributionOfProfits();"+ "return(ret);"+ " }")) return(false); Print("Line \"OnTester\" is insert"); }
よって、"#include <DistributionOfProfits.mqh>"も"OnTester()"コードが必要でした、ソース コードは (たとえば、macd が Sample.mq5 を選択して) 次のようになります。
#include<DistributionOfProfits.mqh> CDistributionOfProfits ExtDistribution; double OnTester() { double ret=0.0; ExtDistribution.AnalysisTradingHistory(0); ExtDistribution.ShowDistributionOfProfits(); return(ret); } //+------------------------------------------------------------------+ //| MACD Sample.mq5 | //| Copyright 2009-2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2009-2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "5.50"
コードは審美的にもかかわらずそのタスクを実行します。3.1 と 3.2 項で取り上げたシンプルなケースのEA コードが、グラフィカルな解析ライブラリ宣言の OnTester() 関数に含まれる場合。次に、 より複雑な場合、コードに最初からグラフィカルな解析ライブラリおよび/または OnTester() 関数の宣言が含まれているかを検討してください。
3.4. 複雑な例: コードに DistributionOfProfits.mqh や OnTester() が既に含まれている
AdvancedSearch() 関数で複雑な検索を実行する。
//+------------------------------------------------------------------+ //| Advanced Search | //| only_ontester=true | //| -検索関数のみ OnTester() | //| only_ontester=false | //| - search #include <DistributionOfProfits.mqh> | //| and function OnTester() | //+------------------------------------------------------------------+ bool AdvancedSearch(const string name_file,const string name_object,const bool only_ontester)
パラメータ:
- name_file -EA ファイルの名前
- name_object -CDistributionOfProfits クラス オブジェクトの名前
- only_ontester -only_ontester = true OnTester() のみ検索します。
初めに、全体のファイルが一時配列に読み込まれる
string arr_temp[];
-操作が容易になります。
サービス コードは順番に呼び出されます。
RemovalMultiLineComments() — 配列からこのコードすべての複数行のコメントを削除
RemovalComments()-単一行コメントは削除されます。
DeleteZeroLine() — すべての長さゼロのラインが、配列から削除されます。
FindInclude() 関数を使用して、only_ontester = = false、 "#include <DistributionOfProfits.mqh>"が行われます。
FindInclude()"#include <DistributionOfProfits.mqh>"を検索し、 (p. 3.1 の"function_position"変数に行の番号を保存します。"#include"を含むコードに"#include <DistributionOfProfits.mqh>"が既に含まれていることを決定する正規表現を使いました)。その後、"CDistributionOfProfits"を検索しようとしました。行が見つかった場合、その行から"CDistributionOfProfits"クラスの変数名を取得します。行が見つからない場合、 "function_position"の横に挿入する必要があります。
only_ontester = = true の場合、 は OnTester() の検索を開始します。一度が見つかったFindFunctionOnTester() 関数を使用して、その行のグラフィカルな解析ライブラリ呼び出し検索します。
4. EAをスレーブターミナルのフォルダーにコピー。
EAが OnInit() にコピーされます。
//---アドバイザー ファイルの解析 if(!ParsingEA()) return(INIT_FAILED); //--- сopying an expert in the terminal folders ResetLastError(); if(!CopyFileW(edited_expert,slaveTerminalDataPath1+"\\MQL5\\Experts\\"+expert_short_name+".mq5",false)) { PrintFormat("Failed CopyFileW #1 with error: %x",kernel32::GetLastError()); return(INIT_FAILED); } if(!CopyFileW(edited_expert,slaveTerminalDataPath2+"\\MQL5\\Experts\\"+expert_short_name+".mq5",false)) { PrintFormat("Failed CopyFileW #2 with error: %x",kernel32::GetLastError()); return(INIT_FAILED); } if(!CopyFileW(edited_expert,slaveTerminalDataPath3+"\\MQL5\\Experts\\"+expert_short_name+".mq5",false)) { PrintFormat("Failed CopyFileW #3 with error: %x",kernel32::GetLastError()); return(INIT_FAILED); } if(!CopyFileW(edited_expert,slaveTerminalDataPath4+"\\MQL5\\Experts\\"+expert_short_name+".mq5",false)) { PrintFormat("Failed CopyFileW #4 with error: %x",kernel32::GetLastError()); return(INIT_FAILED); }
5. スレーブターミナル
スレーブターミナルを起動する前に以下を確認してください: メインターミナルのトレード口座は、すべてのスレーブターミナルで使用される必要があります。または、すべてのスレーブターミナルで dll を許可する必要があります。
Dll を許可しないと、スレーブターミナルは EA を実行できない (EA は積極的に Win API 呼び出しを使用)、およびテスターの"ジャーナル"タブに次のメッセージが出力されます。
2016.10。13 11:28:57コア1 2016.02。03 00:00:00 DLL の読み込みは許可されていません
詳細についてはシステム関数 ShellExecuteW: ShellExecuteWを参照してください。一時ストップは、"LaunchSlaveTerminal"関数によって実行されます。
if(!CopyFileW(edited_expert,slaveTerminalDataPath4+"\\MQL5\\Experts\\"+expert_short_name+".mq5",false)) { PrintFormat("Failed CopyFileW #4 with error: %x",kernel32::GetLastError()); return(INIT_FAILED); } //---スレーブターミナルを起動 Sleep(ExtSleeping); LaunchSlaveTerminal(ExtInstallationPathTerminal_1,common_data_path+"\\Files\\myconfiguration1.ini"); Sleep(ExtSleeping); LaunchSlaveTerminal(ExtInstallationPathTerminal_2,common_data_path+"\\Files\\myconfiguration2.ini"); Sleep(ExtSleeping); LaunchSlaveTerminal(ExtInstallationPathTerminal_3,common_data_path+"\\Files\\myconfiguration3.ini"); Sleep(ExtSleeping); LaunchSlaveTerminal(ExtInstallationPathTerminal_4,common_data_path+"\\Files\\myconfiguration4.ini"); } //--- return(INIT_SUCCEEDED); }
6. 比較レポート
コードにポジションの開始時間との関係を視覚的に解析するためのライブラリの呼び出しを挿入するEAのコードの解析を行うことが目的です。 (このライブラリは記事で記述されていたトレーダーのライフハック: 最適化またはPrintトレード分布です). 挿入するコードを作成してテストした後、次の html ページを自動的に開くスレーブターミナルですべてのEAを有効にします:
以前では、[テスター] ブロック内の構成 ini ファイルに"レポート"パラメータを追加しました。
Expert=D0E820_test
Symbol=GBPAUD
Period=PERIOD_H1
Deposit=100000
Leverage=1:100
Model=0
ExecutionMode=0
FromDate=2016.10.03
ToDate=2016.10.15
ForwardMode=0
Report=D0E820_test
ReplaceReport=1
Port=3000
ShutdownTerminal=0
ターミナルはテスト終了後にレポートを保存するファイル (D0E820_test.htm) の名前です。次のデータを抽出する必要があります。(スレーブターミナル) のシンボル名と期間、EAがテストした上"バックテスト"ブロックとバランス グラフ値。すべてのスレーブターミナルの結果に基づいて次の比較レポートを生成します。
スレーブターミナルは、データ ディレクトリのルート フォルダーに (この場合 htm 形式で)テスト レポートを保存します。EAは、スレーブターミナルを実行し、定期的にディレクトリにレポート ファイルのテストを探します。4つのレポートが見つかったら、一般的な比較レポートを生成します。
まず、レポート ファイルの検索を開始します"find_report"フラグを紹介します。
//---
string arr_path[][2];
bool find_report=false;
//+------------------------------------------------------------------+
//|アプリケーションを起動するコマンドを列挙 |
//+------------------------------------------------------------------+
enum EnSWParam
また、OnTimer() 関数を追加します。
{
//---タイマーを作成
EventSetTimer(9);
ArrayFree(arr_path);
find_report=false; // true - flag allows the search reports
if(!FindDataFolders(arr_path))
return(INIT_SUCCEEDED);
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
if(!find_report)
return;
}
OnTimer() 内のファイルの"Expert_short_name"+".htm"をサーチします。各スレーブターミナルのデータ ディレクトリのルート フォルダーでのみ実行します。この目的には、ListingFilesDirectory.mqh::FindFile() 関数を使用します。
検索を実行して、"サンド ボックス"の外、FindFirstFileW Win API 関数を使用します。前回の記事で FindFirstFileW についての続きを読んでください。:
このコードで結果のファイル名を比較し、指定した名前が一致すると、true が返されます。検索ハンドルはその前に閉じる必要があります: 。
//|Find file |
//+------------------------------------------------------------------+
bool FindFile(const string path,const string name)
{
//---
WIN32_FIND_DATA ffd;
long hFirstFind_0;
ArrayInitialize(ffd.cFileName,0);
ArrayInitialize(ffd.cAlternateFileName,0);
//--- stage Search №0.
string filter_0=path+"\\*.*"; // filter_0==C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\*.*
hFirstFind_0=FindFirstFileW(filter_0,ffd);
//---
string str_handle="";
if(hFirstFind_0==INVALID_HANDLE)
str_handle="INVALID_HANDLE";
else
str_handle=IntegerToString(hFirstFind_0);
//Print("filter_0: \"",filter_0,"\", handle hFirstFind_0: ",str_handle);
//---
if(hFirstFind_0==INVALID_HANDLE)
{
PrintFormat("エラーで失敗 FindFirstFile (hFirstFind_0): %x"、kernel32::GetLastError();
return(false);
}
//---情報をディレクトリにすべてのファイルを一覧表示
bool rezult=0;
do
{
string name_0="";
for(int i=0;i<MAX_PATH;i++)
{
name_0+=ShortToString(ffd.cFileName[i]);
}
if(name_0==name)
{
WinAPI_FindClose(hFirstFind_0);
return(true);
}
ArrayInitialize(ffd.cFileName,0);
ArrayInitialize(ffd.cAlternateFileName,0);
ResetLastError();
rezult=WinAPI_FindNextFile(hFirstFind_0,ffd);
}
while(rezult!=0); //if(hFirstFind_1==INVALID_HANDLE)、 はここに表示されます
if(kernel32::GetLastError()!=ERROR_NO_MORE_FILES)
PrintFormat("Failed FindNextFileW (hFirstFind_0) with error: %x",kernel32::GetLastError());
//else
// Print("filter_0: \"",filter_0,"\", handle hFirstFind_0: ",hFirstFind_0,", NO_MORE_FILES");
WinAPI_FindClose(hFirstFind_0);
//---
return(false);
}
EAは、スレーブターミナルのフォルダーですべての 4 つのレポート ファイルが利用可能なかどうかをチェックします。: すべてのターミナルでのテストを完了していることを示します。
この情報を処理する必要があります。4つのレポートのファイルと 4 つのグラフィカルなファイル (バランス ・ チャート) は、サンド ボックス TERMINAL_COMMONDATA_PATH\Files にコピーされます。
string path=TerminalInfoString(TERMINAL_COMMONDATA_PATH);
if(!CopyFileW(slaveTerminalDataPath1+"\\"+expert_short_name+".htm",path+"\\Files\\"+"report_1"+".htm",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath1+"\\"+expert_short_name+".png",path+"\\Files\\"+"report_1"+".png",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath2+"\\"+expert_short_name+".htm",path+"\\Files\\"+"report_2"+".htm",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath2+"\\"+expert_short_name+".png",path+"\\Files\\"+"report_2"+".png",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath3+"\\"+expert_short_name+".htm",path+"\\Files\\"+"report_3"+".htm",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath3+"\\"+expert_short_name+".png",path+"\\Files\\"+"report_3"+".png",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath4+"\\"+expert_short_name+".htm",path+"\\Files\\"+"report_4"+".htm",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
if(!CopyFileW(slaveTerminalDataPath4+"\\"+expert_short_name+".png",path+"\\Files\\"+"report_4"+".png",false))
{
PrintFormat("Failed with error: %x",kernel32::GetLastError());
return;
}
しかし、結果のレポートファイルに正規表現の使用を大幅に複雑にする、不要な情報が多くあります。よって、'複数の tests.mq5::ParsingReportToArray の Compare 関数、その後で操作が実行されます。
このファイルは正規表現を使用するために便利です"(>) (. *?)(<)"、すなわち間すべてのシンボルを検索するため">"と"<"、このようなシンボルの番号は 0 から始まります。
正規表現の使用の結果は 4 つの配列に追加されます: arr_report_1、arr_report_2、arr_report_3、arr_report_4。配列からの情報は、最終的な比較レポートのコードを生成するために使用されます。最終レポートを作成すると、 WinAPI 関数 'ShellExecuteW' を呼び出し、 (ShellExecuteW についての詳細) ブラウザーを起動します。
4つの異なるシンボルのテストのEAの結果を比較できる、ブラウザーのページが開きます。
結論
この記事では、4 つの異なるシンボルのEAの結果を評価する方法について説明しました。この場合、4 つのシンボルの検査では、同時にテストの結果と集計表が得られます。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2731
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索