インジケーターへのエントリの解決
Dmitriy Gizlyk | 25 12月, 2017
イントロダクション
勝てるトレーダーを見て、同じような戦略に従おうとしていますか。 もしくは、自分のトレード履歴を見て、負けトレードを減らす方法を考えていますか。 少なくともどちらかの質問にはポジティブな返答であることを期待しています。 この記事では、トレード履歴からインジケーターに落とし込み、どのインジケーターが役に立つかを判断する方法を考察します。
1. 問題の定義
以前の記事では、カルマンのフィルタベースのEAの構築について説明しました。 テストにおいて利益を示したが、同時に2つの戦略上のネックがありました。つまり、遅い決済とレンジ相場における負けトレードです。
したがって、今回の目的は、この戦略下で負けトレードの数を減らすことです。 そのためには、ポジションの建玉の瞬間にインジケーターの値を保存します。 その後、分析を行い、トレード結果とインジケーターの値を比較します。 後は、トレードのパフォーマンスを向上させるために役立つインジケーターの間で選択することです。
まずは行動計画を立てましょう。
- テスト期間を決定 テストしてレポートを保存
- テストレポートを解析し、トレードの配列を作成(操作結果を含む)。
- 使用するインジケーターの一覧とデータ保存形式を決定 さらにアプリケーションのクラスを準備
- 結果出力用のレポートフォームを準備します。
- 分析 EA を構築します。
- ストラテジーテスターで対象EA を起動し、レポートを分析します。
- EA に必要なインジケーターを追加します。
- 更新された EA をテストし、結果を比較します。
2. 分析された EA の最初のテスト
上記の記事では、EA はテストの1ヶ月以内に150のトレードを行った。 これは、統計解析には不十分です。 テスト期間を8倍に増やします。 最適化なしで、3120足 (約3ヶ月) の自己回帰関数の構築期間を設定し、テストを開始します。
テスト結果では明らかに損失になる資産チャートになりました。 一般的に、勝ちトレードは 34% 未満になりました。 ただし、平均利益は 45% 平均損失を上回っています。 これは、テスト期間中において利益を得るには不足しています。
この価格表は、明確に定義されたトレンドがない場合(レンジ)、EAが損失になるポジションを開き、その後決済することを示しています。 今回のタスクは、そのようなトレードの数を減らす、可能であれば、完全に除外することです。
まず、テストレポートを保存して、さらに処理を行う必要があります。 ただし、セキュリティ上の理由から、ファイルを使用した MQL5 タスクでは厳密に制御されているという意味合いがあります。 MQL5 ツールを使用して操作を実行するためのファイルが "サンドボックス" ファイルにあることを確認してください。 よって、レポートはそこに保存する必要があります。 しかし、ストラテジーテスターでプログラムを起動するので、各エージェントがその "サンドボックス" で動作することを考慮する必要があります。 したがって、任意のエージェントでテスト中にプログラムがレポートにアクセスできるようにするため、共有ターミナルフォルダに保存します。
クライアントターミナルの共有フォルダへのパスを確認するには、メタエディタ の "ファイル" メニューを開き、[共通データフォルダを開く] を選択します。
開いたウィンドウで、「ファイル」フォルダを入力します。
次に、"Ctrl + C" を押して、完全なパスラインを exchange バッファにコピーします。
"サンドボックス" へのパスが知られており、テストレポートを保存することができます。 そのためには、「ストラテジーテスター」で「結果」を選択し、そのスペースでマウスの右ボタンをクリックします。 表示されるメニューで、"Report"-> "HTML (Internet Explorer)" を選択します。
操作を実行すると、ファイル保存用のシステムウィンドウが開きます。 まず、ファイル名の入力フィールドに "サンドボックス" へのパスを入れて、"保存" を押してください。 この操作により、ファイル保存用のフォルダが変更されます。
次の手順では、テストレポートを保存する名前を指定し、ファイルを保存します。
「サンドボックス」でレポートを保存した後、次のフェーズに進み、分析にトレード配列を作成します。
3. トレード配列を作成する
3.1. 解析の一般的な概念
前のセクションでは、EA のテストレポートを保存しました。 これを処理するための便利なトレードの配列を形成するつもりです。 ブラウザではトレードの一覧が表示されますが、MQL5 プログラムは html ファイルから直接データ配列をダウンロードすることはできません。 したがって、レポートの解析を実装する必要があります。
実質的には、html ファイルは、その書式とデザインを記述するタグで区切られたテキストです。 レポートをテキストエディタで開いたことで、レポート内のすべてのデータが2つのデータテーブルに分割されることを意味する2つのタグ "<table>" を見つけることができます。 トレードに関する情報は2テーブルにあります。 その始まりには、オーダーに関する情報とし、トレードに関する情報があります。
表の行は、"<tr> </tr>" というタグでマークアップされています。 行では、"<td> </td>" というタグによってセルに分割されます。
3.2. トレードの情報を保存するためのクラス
レポートでデータの表示形式を決定しました。 では、配列内のデータ保存形式に進みましょう。 分析された EA が1つのシンボルでのみ動作する限り、シンボル名は保存されません。 それもかかわらず、インジケーターの初期化に必要となります。 最後に、トレードの記録的構造は次のようになります。
- position open time;
- position open volume;
- trade direction;
- position close volume;
- commission amount;
- swap amount;
- profit amount.
このタスクの主な側面を決定しました。 コードを書き始めましょう。 まず、トレードクラスの CDeal を確認します。
class CDeal : public CObject { private: datetime OpenTime; //オープンポジションの時間 double OpenedVolume; //開いたポジションのボリューム ENUM_POSITION_TYPE Direct; //オープンポジションのタイプ double ClosedVolume; //クローズドボリューム double Comission; //手数料 double Swap; //ポジションの入れ替え double Profit; //ポジションの利益 public: CDeal(); ~CDeal(); };
新しいオープントレードを記録するときにクラスを初期化します。ポジションの開始時, トレードのボリュームと方向はすでに知っているものとします。 したがって、初期化関数のパラメータに値とコミッションが伝達されます。(可能な場合) 初期化時に他のパラメータをゼロにします。 その結果、クラス初期化関数は次のようになります。
CDeal::CDeal(ENUM_POSITION_TYPE type,datetime time,double volume,double comission=0.0) : ClosedVolume(0), Swap(0), Profit(0) { OpenTime = time; OpenedVolume = volume; Direct = type; Comission = comission; }
それ以上のタスクでは、すでに保存されたトレードの状態を確認する必要があります。 これを行うためには、トレードがすでに閉じているかどうかをチェックする IsClosed 関数を作成します。 トレードの開始および決済のボリュームで比較されます。 等しい場合は、トレードが決済され、この関数は "true" の値を返すことを意味します。 トレードが閉じられていない場合、関数は "false" を返し、相場に残っているボリュームが返されます。
bool CDeal::IsClosed(double &opened_volume) { opened_volume=OpenedVolume-ClosedVolume; return (opened_volume<=0); }
トレードの状態だけを確認する必要がある場合には、ボリュームを見つけるために必要はありません。同じ名前でもう一つの関数を作ります。
boolCDeal:: IsClosed (void) { double opened_volume; return IsClosed(opened_volume); }
正しくトレードを閉じるために、そのタイプを知っている必要があります。 "Type" メソッドは、"private" 変数 "Direct" の値を返します。 この関数はかなり短いため、クラス本体で再記述することができます。
ENUM_POSITION_TYPE Type(void) { return Direct; }
ステータスがチェックされた後、トレードを閉じる必要があります。 それを行うために "Close" 関数を作成します。 次のパラメータが渡されます。すはわち、クロージングボリューム、トレード利益、コミッションと累積スワップ。 この関数は、渡されたボリュームがないトレード量を超えた場合に "false" を返します。 それ以外の場合は、渡されたパラメータが対応するクラス変数に保存され、この関数は "true" を返します。
bool CDeal::Close(double volume,double profit,double comission=0.0,double swap=0.0) { if((OpenedVolume-ClosedVolume)<volume) return false; ClosedVolume += volume; Profit += profit; Comission += comission; Swap += swap; return true; }
トレードを分析するときにリクエスト時にトレード利益を返す関数が必要になります。 その関数GetProfitを呼び出しましょう。
double CDeal::GetProfit(void) { return (Comission+Swap+Profit); }
また、インジケーターのステータスに関するデータをタイムリーに受けとるためには、トレード時間を知る必要があります。 このために、"GetTime" 関数を作成します。
datetime GetTime(void) { return OpenTime; }
3.3. レポート解析のクラス
各トレードに関する情報を保存するためのクラスを作成したら、すぐに次に移りましょう。 そのための"CParsing" クラスを作成します。 クラスの決定:
- クラス CArrayObj のオブジェクト-トレード配列を格納。
- クラス CFileTxt のオブジェクト-レポートファイルを操作
- 文字列型の変数-シンボル名を格納
初期化と初期化関数は別として、次の2つの関数がクラスに追加されます。
- ReadFile — immediately for parsing;
- GetSymbol —リクエスト時にシンボル名を返します。
class CParsing { private: CArrayObj *car_Deals; //情報の配列 CFileTxt *c_File; //ファイルを解析 string s_Symbol; //取引のシンボル public: CParsing(CArrayObj *&array); ~CParsing(); bool ReadFile(string file_name); string GetSymbol(void) { return s_Symbol; } };
このクラスの関数の主な目的は、さらなる処理のトレード配列を作成することです。 作成された配列がメインプログラムで動作できるようにする必要があることを意味します。 この目的のため、トレード・配列・ストレージ用の CArrayObj クラスのオブジェクトはメイン・プログラムで宣言され、リンクは初期化時にクラスに渡されます。 このため、初期化関数は次のようになります。
CParsing::CParsing(CArrayObj *&array) : s_Symbol(NULL) { if(CheckPointer(array)==POINTER_INVALID) { array=new CArrayObj(); } car_Deals=array; }
CFileTxt クラスオブジェクトの削除は初期化関数に書き込まれます。 ファイルの決済は CFile の親クラスの初期化関数で指定されますが、ここでは提供しません。
CParsing::~CParsing() { if(CheckPointer(c_File)!=POINTER_INVALID) delete c_File; }
解析に進んでみましょう。 パラメータの ReadFile 解析関数を呼び出すときに、レポートファイルの名前を指定します。 この関数で最初に行ったことは、渡されたパラメータが空でないかどうかをチェックすることです。 同様に、トレードに関する情報を保存するための配列の可用性を確認します。 少なくとも1つの条件が満たされていない場合は、関数の実行を決済し、"false" を返します。
bool CParsing::ReadFile(string file_name) { //--- if(file_name==NULL || file_name=="" || CheckPointer(car_Deals)==POINTER_INVALID) return false;
次に、CFileTxt クラスオブジェクトを初期化し、関数パラメータで渡されたファイルを開きます。 エラーが発生した場合は、"false" の結果となり関数を終了します。
if(CheckPointer(c_File)==POINTER_INVALID) { c_File=new CFileTxt(); if(CheckPointer(c_File)==POINTER_INVALID) return false; } //--- if(c_File.Open(file_name,FILE_READ|FILE_COMMON)<=0) return false;
ファイルを開いた後、その内容をすべて "string" 型変数に読み込みます。 ファイルが空の場合は、"false" の結果で関数を終了します。
string html_report=NULL; while(!c_File.IsEnding()) html_report+=c_File.ReadString(); c_File.Close(); if(html_report==NULL || html_report=="") return false;
次の段階では、レポートテキストでは発生しない文字を検索し、区切りシンボルとして使用することもできます。 この文字が使用できない場合は、"false" の結果で関数から終了します。
string delimiter = NULL; ushort separate = 0; for(uchar tr=1;tr<255;tr++) { string temp = CharToString(tr); if(StringFind(html_report,temp,0)>0) continue; delimiter = temp; separate = tr; break; } if(delimiter==NULL) return false;
上記のように html ファイル構造テーブルでは、"</table>" によって閉じられています。区切りシンボルでこのタグを置き換え、その上の行で完全なレポートを分割してみましょう。 このようなやり方で、必要なテーブルを分離します。
if(StringReplace(html_report,"</table>",delimiter)<=0) return false; //--- s_Symbol=NULL; car_Deals.Clear(); //--- string html_tables[]; int size=StringSplit(html_report,separate,html_tables); if(size<=1) return false;
"</tr>" でこの手順を繰り返し、テーブルを分解する。
if(StringReplace(html_tables[size-2],"</tr>",delimiter)<=0) return false; size=StringSplit(html_tables[size-2],separate,html_tables); if(size<=1) return false;
次に、受信した文字列配列をサイクルで処理してみましょう。 まず、オーダーに関する情報を含むすべての文字列を渡します。 一方、レポート内のオーダーとトレードを分割するテキスト "Deals" で行によって整列されます。
bool found_start=false; double opened_volume=0; for(int i=0;i<size;i++) { //--- if(!found_start) { if(StringFind(html_tables[i],"Deals",0)>=0) found_start=true; continue; }
その後、各行をセルに分割し、情報をそれぞれの形式に変換します。
string columns[]; int temp=StringFind(html_tables[i],"<td>",0); if(temp<0) continue; if(temp>0) html_tables[i]=StringSubstr(html_tables[i],temp); StringReplace(html_tables[i],"<td>",""); StringReplace(html_tables[i],"</td>",delimiter); temp=StringSplit(html_tables[i],separate,columns); if(temp<13) continue; //--- ENUM_POSITION_TYPE e_direction = (ENUM_POSITION_TYPE)(columns[3]=="buy" ? POSITION_TYPE_BUY : columns[3]=="sell" ? POSITION_TYPE_SELL : -1); if(e_direction==-1) continue; //--- datetime dt_time = StringToTime(columns[0]); StringReplace(columns[5]," ",""); double d_volume = StringToDouble(columns[5]); StringReplace(columns[8]," ",""); double d_comission = StringToDouble(columns[8]); StringReplace(columns[9]," ",""); double d_swap = StringToDouble(columns[9]); StringReplace(columns[10]," ",""); double d_profit = StringToDouble(columns[10]); if(s_Symbol==NULL || s_Symbol=="") { s_Symbol=columns[2]; StringTrimLeft(s_Symbol); StringTrimRight(s_Symbol); }
次の段階で、トレードがポジションクローズの段階であるかどうかを確認します。 結果が正の場合は、FIFO のメソッドに基づいてポジションを決済します。
if(opened_volume>0 && StringFind(columns[4],"out",0)>=0) { int total=car_Deals.Total(); double total_volume=MathMin(opened_volume,d_volume); for(int d=0;(d<total && e_direction!=(-1) && total_volume>0);d++) { CDeal *deal=car_Deals.At(d); if(CheckPointer(deal)==POINTER_INVALID) continue; //--- if(deal.Type()==e_direction) continue; //--- double deal_unclosed=0; if(deal.IsClosed(deal_unclosed)) continue; double close_volume = MathMin(deal_unclosed,total_volume); double close_comission = d_comission/d_volume*close_volume; double close_swap = d_swap/total_volume*close_volume; double close_profit = d_profit/total_volume*close_volume; if(deal.Close(close_volume,close_profit,close_comission,close_swap)) { opened_volume -= close_volume; d_volume -= close_volume; total_volume -= close_volume; d_comission -= close_comission; d_swap -= close_swap; d_profit -= close_profit; } } }
次に、ポジションの開始操作が行われたかどうかを確認します。 必要に応じて、新たなトレードを行います。
if(d_volume>0 && StringFind(columns[4],"in",0)>=0) { CDeal *deal = new CDeal(e_direction,dt_time,d_volume,d_comission); if(CheckPointer(deal)==POINTER_INVALID) return false; if(!car_Deals.Add(deal)) return false; opened_volume += d_volume; } }
少なくとも1つのトレードが保存された場合、関数は最後に "true"、それ以外の場合は "false" を返します。
return (car_Deals.Total()>0); }
次のタスクフェーズに進みます。
4. インディケータを使用するためのクラスの準備
すでに前に言ったように、このタスクの一つは、明確に定義されたトレンドのない状態で損失のトレードをオフに選別することです。 このウェブサイト (たとえば、記事 [3] および [4]) を含む、トレンドの定義の問題が定期的に発生します。 トレンドの定義において極端な表現をするつもりはありません。 トレードシステムの分析と最適化に使用する、トレードとインジケーターの値の比較法を提案します。 よって、標準のターミナル配布パックですでに利用可能な最も広範なインジケーターを考慮してみましょう。
4.1. ATR インジケーターインクード用クラス
オシレータータイプインジケーター「真の平均値」を最初に考慮します。 周知のように、トレンド相場のボラティリティが大きくなります。 これはオシレーターの値の成長についてのシグナルになります。 どの値を保存する必要があるでしょうか? これまでのところ、EAの分析は、最後の閉じたロウソクの上にインジケーター値を保存する必要がある規定のロウソクの始値だけでなく、この値の比率を設定します。 最初の値は、現在のボラティリティが表示され、2つ目はボラティリティの変化のダイナミクスを示します。
考慮するインジケーターは、1つのバッファインジケーターのクラスの代表的なものです。 したがって、この種のインジケーターで動作するように単一のクラスを作ることは理にかなっています。
インジケーター値の保存のアプローチは、トレードの保存になります。まず、1つのトレードのインジケーター値を格納するためのクラスを作成し、外部のリクエストとデータ保存のインジケーターと即時タスクの上位レベルのクラスを作成します。
最初のクラスを「CValue」と呼んでみましょう。 インジケーターの値 (値)、インジケーターの2つの最後の値の比率 (動) と値が保存されていたオーダーチケットの数 (Deal_Ticket) に関する情報を格納するための3つのプライベート変数が含まれます。 分析時のオーダーとのインジケーター値の比較には、チケット番号が必要となります。 保存に必要なすべての値は、初期化時にクラスインスタンスに渡されます。 必要な情報を取得するためには、GetTicket、GetDinamic、ぞれの変数の値を返す関数を作成します。 さらに、インジケーターの値とそのダイナミクスを同時に返す関数 GetValues を作成します。
class CValue : public CObject { private: double Value; //インジケーターの値 double Dinamic; //インジケーターの・値 long Deal_Ticket; //取引のチケット public: CValue(double value, double dinamic, long ticket); ~CValue(void); //--- long GetTicket(void) { return Deal_Ticket; } double GetValue(void) { return Value; } double GetDinamic(void) { return Dinamic; } void GetValues(double &value, double &dinamic); };
次に、上位レベルのクラスをデータ配列 COneBufferArray に格納するようにします。 "private" ブロックには、保存されたデータの配列とインジケーターハンドルが含まれます。 すべての1つのバッファのインジケーターで動作するように普遍的なクラスを作成することにしました。 しかし、異なるインジケーターの呼び出しには、パラメータのさまざまなセットが付属しています。 したがって、私の意見では、最もシンプルには、メインプログラムのインジケーターを初期化し、クラスを初期化して、必要なインジケーターのハンドルを渡すことだと思います。 後続のインジケーターの識別については、レポート内の "s_Name" 変数を使いましょう。
class COneBufferArray : CObject { private: CArrayObj *IndicatorValues; //インジケーターの値の配列 int i_handle; //インジケーターのハンドル string s_Name; string GetIndicatorName(int handle); public: COneBufferArray(int handle); ~COneBufferArray(); //--- bool SaveNewValues(long ticket); //--- double GetValue(long ticket); double GetDinamic(long ticket); bool GetValues(long ticket, double &value, double &dinamic); int GetIndyHandle(void) { return i_handle; } string GetName(void) { return (s_Name!= NULL ? s_Name:); } };
外部リクエストによってデータを保存するには、パラメータオーダーチケットを1つだけ含む SaveNewValues 関数を作成します。 この関数の先頭にデータストレージとインジケーターハンドルの配列の状態を確認します。 エラー関数の場合は、"false" の値を返します。
bool COneBufferArray::SaveNewValues(long ticket) { if(CheckPointer(IndicatorValues)==POINTER_INVALID) return false; if(i_handle==INVALID_HANDLE) return false;
その後、インディケーターのデータを受け取ります。 インジケーターの値がダウンロードできない場合は、false が返されます。
double ind_buffer[]; if(CopyBuffer(i_handle,0,1,2,ind_buffer)<2) return false;
次のペースで "CValue" クラスのインスタンスを作成し、必要な値を渡します。 クラスインスタンスの作成時にエラーが発生した場合、この関数は false を返します。
CValue *object=new CValue(ind_buffer[1], (ind_buffer[0]!=0 ? ind_buffer[1]/ind_buffer[0] : 1), ticket); if(CheckPointer(object)==POINTER_INVALID) return false;
クラスがインジケータ名を取得していない場合、GetIndicatorName (関数コードは添付ファイルで提供される) で、チャートからを取得します。
if(s_Name==NULL) s_Name=GetIndicatorName(i_handle);
結論として、新しく作成されたデータクラスのインスタンスを配列に追加し、返された操作の結果を関数から終了します。
return IndicatorValues.Add(object);
}
リクエスト時に配列からデータを返すためには、GetDinamic と GetValues を作成し、オーダーチケットで必要な値を返します。
完全なクラスコードは添付ファイルに用意されています。
インジケーター CCI、ボリューム、フォース、チャイキンオシレーターと標準偏差によってデータを収集するためにこのクラスを適用しました。
4.2. MACD のインジケーターのクラス
コレクションにもう1つの標準インジケータ-MACD を追加してみましょう。 周知のように、これはトレンドの力と方向を決定するために使用されます。
以前のインジケーターとは対照的に、MACD は2インジケータバッファ (メインとシグナル) があります。 したがって、同様に2つの行についての情報を保存します。 データストレージのクラスコードの上に表示されるアルゴリズムを使用すると、次のようになります。
class CMACDValue : public CObject { private: double Main_Value; //メインラインの値 double Main_Dinamic; //メインラインのダイナミクス double Signal_Value; //シグナル線の値 double Signal_Dinamic; //シグナルライムの値 long Deal_Ticket; //チケット public: CMACDValue(double main_value, double main_dinamic, double signal_value, double signal_dinamic, long ticket); ~CMACDValue(void); //--- long GetTicket(void) { return Deal_Ticket; } double GetMainValue(void) { return Main_Value; } double GetMainDinamic(void) { return Main_Dinamic; } double GetSignalValue(void) { return Signal_Value; } double GetSignalDinamic(void) { return Signal_Dinamic; } void GetValues(double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic); };
それぞれの変更は、データ配列を使用するためのクラスでも行われます。 セクション4.1 で説明されているユニバーサルクラスとは対照的に、このクラスは特定のインジケーターで動作するため、クラスの初期化時には、渡されるインジケーターハンドルではなく、初期化に必要なパラメータを指定します。 インジケーターの初期化は、クラス内で直ちに実行されます。
class CMACD { private: CArrayObj *IndicatorValues; //インジケーターの値の配列 int i_handle; //インジケーターのハンドル public: CMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price); ~CMACD(); //--- bool SaveNewValues(long ticket); //--- double GetMainValue(long ticket); double GetMainDinamic(long ticket); double GetSignalValue(long ticket); double GetSignalDinamic(long ticket); bool GetValues(long ticket, double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic); };
この関数の全体のロジックは同じままで、変更はインジケーターバッファと保存された変数の量のみを考慮します。
bool CMACD::SaveNewValues(long ticket) { if(CheckPointer(IndicatorValues)==POINTER_INVALID) return false; if(i_handle==INVALID_HANDLE) return false; double main[], signal[]; if(!CopyBuffer(i_handle,0,1,2,main)<2 || !CopyBuffer(i_handle,1,1,2,signal)<2) return false; CMACDValue *object=new CMACDValue(main[1], (main[0]!=0 ? main[1]/main[0] : 1), signal[1], (signal[0]!=0 ? signal[1]/signal[0] : 1), ticket); if(CheckPointer(object)==POINTER_INVALID) return false; return IndicatorValues.Add(object); }
スケーリングの同様のロジックは、インジケーターバッファの任意の量に適用できます。 選択したインジケーターバッファのみを保存する場合は、しれぞれのクラスの SaveNewValues 関数で記述するだけで十分です。 しかし、今の段階ではまだ利益のあるトレードと特定のインジケーターのバッファの値との間の相互関係があるかどうかはわからないが、存在する場合、これを行うことをお勧めしません。
マテリアルを統合するためには、3データバッファとインジケーターデータを保存する別の例をレンダリングすることができます。
4.3. ADX インジケーターを含めるためのクラス
ADX インジケーターは、トレンドの力と方向を決定するために広く使用されています。 "マネーボックス" に追加されます。
このインジケーターには3つのインジケーターバッファがあり、上記の推奨スケーリングメソッドに従って、保存された変数の数を増やします。 したがって、データストレージクラスは次のようになります。
class CADXValue : public CObject { private: double ADX_Value; //ADX 値 double ADX_Dinamic; //ADX の・値 double PDI_Value; //+ DI 値 double PDI_Dinamic; //+ DIの値 double NDI_Value; //-DIの値 double NDI_Dinamic; //-DIのダイナミクス long Deal_Ticket; //取引のチケット public: CADXValue(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, long ticket); ~CADXValue(void); //--- long GetTicket(void) { return Deal_Ticket; } double GetADXValue(void) { return ADX_Value; } double GetADXDinamic(void) { return ADX_Dinamic; } double GetPDIValue(void) { return PDI_Value; } double GetPDIDinamic(void) { return PDI_Dinamic; } double GetNDIValue(void) { return NDI_Value; } double GetNDIDinamic(void) { return NDI_Dinamic; } void GetValues(double &adx_value, double &adx_dinamic, double &pdi_value, double &pdi_dinamic, double &ndi_value, double &ndi_dinamic); };
格納されるデータの増加がある場合、配列を使用するクラスの変更を伴うことになります。
class CADX { private: CArrayObj *IndicatorValues; //インジケーターの値の配列 int i_handle; //インジケーターのハンドル public: CADX(string symbol, ENUM_TIMEFRAMES timeframe, uint period); ~CADX(); //--- bool SaveNewValues(long ticket); //--- double GetADXValue(long ticket); double GetADXDinamic(long ticket); double GetPDIValue(long ticket); double GetPDIDinamic(long ticket); double GetNDIValue(long ticket); double GetNDIDinamic(long ticket); bool GetValues(long ticket,double &adx_value,double &adx_dinamic,double &pdi_value,double &pdi_dinamic,double &ndi_value,double &ndi_dinamic); }; bool CADX::SaveNewValues(long ticket) { if(CheckPointer(IndicatorValues)==POINTER_INVALID) return false; if(i_handle==INVALID_HANDLE) return false; double adx[], pdi[], ndi[]; if(!CopyBuffer(i_handle,0,1,2,adx)<2 || !CopyBuffer(i_handle,1,1,2,pdi)<2 || !CopyBuffer(i_handle,1,1,2,ndi)<2) return false; CADXValue *object=new CADXValue(adx[1], (adx[0]!=0 ? adx [1]/adx [0]: 1)、pdi [1]、(pdi [0]! =0 ? pdi [1]/pdi [0]: 1), .ndi [1], (.ndi [0]! =0 ? ndi[1]/ndi[0] : 1), ticket); if(CheckPointer(object)==POINTER_INVALID) return false; return IndicatorValues.Add(object); }
インジケーターとタスクのクラスを構築する原則を理解しているものと思います。 したがって、記事をいたずらに長くしないようにするため、次のインジケーターのコードを記述しません。 同様に、分析の "マネーボックス" に BW MFI とアリゲーターを追加しました。 添付ファイルで完全なコードを得ることができます。
5. 結果出力用のレポートフォームの準備
トレードの瞬間に関係するインジケーターから情報を取得した後、得られたデータの分析について考える必要があります。 私の意見では、最も明確にぞれのインジケーターの値にトレード利益の依存関係のチャートを構築することです。 記事 [2] のVictor によって提案された技術に応じてチャートを構築します。
これまでのことから、インジケーターの値に利益の依存関係を検索するトレードの最適化を実装します。 トレードを繰り返す場合は、トレード数とインジケーター値の間の依存関係を検索する必要があります。
まず、各インジケーターの情報を準備するクラスを作成します。
5.1. ユニバーサルクラスの1つのバッファインジケータ
1つのバッファインジケータを使用するクラスが最初に作成されます。 どの情報を分析することができるでしょうか。 インジケーターバッファの値とその変化のダイナミクスを保存したことに注意してください。 したがって、分析することができます。
- ポジション開始時のインジケーター値に対する実行操作からの利益の依存性
- インジケーターラインのトレンドが利益に与える影響
- 同様に、インジケーター値の複雑な影響と実行操作の結果にそのダイナミクス。
チャート描画の場合は、クラス CStaticOneBuffer を作成します。 このクラスには、保存されたデータ配列 DataArray への参照、プリセットステップ d_Step による値のインジケーター値の配列、およびロングおよびショートポジションの合計利益の2つの配列が含まれます。 注意: 総利益計算の配列は2次元になります。 最初の測定値の大きさは、Value 配列のサイズに対応します。 2番目の測定は3つの要素が含まれます。1つ目-落下インジケータダイナミクス、2番目-水平方向のインジケータの動き、3番目-成長運動。
クラスの初期化時には、パラメータでデータ配列への参照と、インジケーター値のステップサイズを設定します。
class CStaticOneBuffer : CObject { private: COneBufferArray *DataArray; double d_Step; //値配列のステップ double Value[]; //値の配列 double Long_Profit[][3]; //買いトレードの利益の配列、direct -> DOWN-0, EQUAL-1, UP-22 double Short_Profit[][3]; //売りトレード利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 bool AdValues(double value, double dinamic, double profit, ENUM_POSITION_TYPE type); int GetIndex(double value); bool Sort(void); public: CStaticOneBuffer(COneBufferArray *data, double step); ~CStaticOneBuffer(); bool Ad(long ticket, double profit, ENUM_POSITION_TYPE type); string HTML_header(void); string HTML_body(void); };
初期化関数では、渡された値を保存し、使用されている配列をゼロにします。
CStaticOneBuffer::CStaticOneBuffer(COneBufferArray *data,double step) { DataArray = data; d_Step = step; ArrayFree(Value); ArrayFree(Long_Profit); ArrayFree(Short_Profit); }
統計情報を収集するためには、トレードについての情報を渡すAd関数を作成します。 それぞれのインディケータパラメータは関数の内部にあり、データは必要な配列要素に保存されます。
bool CStaticOneBuffer::Ad(long ticket,double profit,ENUM_POSITION_TYPE type) { if(CheckPointer(DataArray)==POINTER_INVALID) return false; double value, dinamic; if(!DataArray.GetValues(ticket,value,dinamic)) return false; value = NormalizeDouble(value/d_Step,0)*d_Step; return AdValues(value,dinamic,profit,type); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CStaticOneBuffer::AdValues(double value,double dinamic,double profit,ENUM_POSITION_TYPE type) { int index=GetIndex(value); if(index<0) return false; switch(type) { case POSITION_TYPE_BUY: if(dinamic<1) Long_Profit[index,0]+=profit; else if(dinamic==1) Long_Profit[index,1]+=profit; else Long_Profit[index,2]+=profit; break; case POSITION_TYPE_SELL: if(dinamic<1) Short_Profit[index,0]+=profit; else if(dinamic==1) Short_Profit[index,1]+=profit; else Short_Profit[index,2]+=profit; break; } return true; }
チャートの視覚化で、HTML ページのヘッダーと本文のコードの断片が生成される HTML_header と HTML_body を作成します。 HTML ページコードの構築の原則は記事 [2] で詳しく説明されていますが、焦点は当てません。 完全なコードは添付ファイルに用意されています。
5.2. ビルウィリアムズ MFI のインジケーターのデータを表示するためのクラス
次にビルウィリアムズ MFI のインジケーターを検討してみましょう。 チャートに表示するメソッドでは、1つのバッファのインジケーターに似ていますが、BW MFIは、値を持つカラーパレットのバッファもあります。 同時に、2つのバッファインジケータとは対照的に、カラーバッファの変化のダイナミクスに興味がありません。 したがって、1つのバッファのインジケーターの上に提案されたチャートには、インジケーターの色だけでなく、現在のインジケータの色に応じて、値とインジケーターのダイナミクスの複雑な影響のチャートに利益依存のチャートが追加されます。
統計データを収集し、分析チャートを作成するために、クラス CStaticBWMFI を作成します。 クラス構造は、上で考えられるものと類推されます。 変更の利益計算の配列は、今では3つの次元があります。 第3次元は使用される色の数に従って4つの要素を得た。
class CStaticBWMFI : CObject { private: CBWMFI *DataArray; double d_Step; //値配列のステップ double Value[]; //値の配列 double Long_Profit[][3][4]; //買いトレードの利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 double Short_Profit[][3][4]; //売りトレード利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 bool AdValues(double value, double _color, double dinamic, double profit, ENUM_POSITION_TYPE type); int GetIndex(double value); bool Sort(void); public: CStaticBWMFI(CBWMFI *data, double step); ~CStaticBWMFI(); bool Ad(long ticket, double profit, ENUM_POSITION_TYPE type); string HTML_header(void); string HTML_body(void); };
完全なコードは添付ファイルに用意されています。
5.3. MACD インジケーターのデータを表示するためのクラス
さらに、MACD のインジケーターを考えてみましょう。 ご存知のように、2つのバッファ: ヒストグラムとシグナル線があります。 このインジケーターシグナルの解釈のルールの下で、ヒストグラムの価値および動きの方向はシグナル線のポジションと同様、重要です(上かヒストグラムの下)。 包括的な分析に、チャートの数を作成します。
- ヒストグラムとその方向の値に対するトレード利益率の依存性。
- シグナル線とその方向の値に対するトレード利益率の依存性
- ヒストグラムに対するシグナル線ポジションに対する利益の依存性
- ヒストグラムの値の共同効果に対する利益の依存性, ヒストグラムに対する方向とシグナル線のポジション.
class CStaticMACD : CObject { private: CMACD *DataArray; double d_Step; //値配列のステップ double Value[]; //値の配列 double SignalValue[]; //値の配列 double Long_Profit[][3][3]; //買いトレードの利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 double Short_Profit[][3][3]; //売りトレード利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 double Signal_Long_Profit[][3]; //買いトレードの利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 double Signal_Short_Profit[][3]; //売りトレード利益の配列、direct -> DOWN-0, EQUAL-1, UP-2 bool AdValues(double main_value, double main_dinamic, double signal_value, double signal_dinamic, double profit, ENUM_POSITION_TYPE type); int GetIndex(double value); int GetSignalIndex(double value); bool Sort(void); public: CStaticMACD(CMACD *data, double step); ~CStaticMACD(); bool Ad(long ticket, double profit, ENUM_POSITION_TYPE type); string HTML_header(void); string HTML_body(void); };
ご覧の通り、クラスの構造、名前と関数の指定は同じままでした。 変更については、添付ファイルで得ることができる関数の内容のみを懸念しています。
5.4. ADX インジケーターのデータを表示するためのクラス
次の1つを検討する CStaticADX クラスです。 ADX インジケーターの値によって統計情報を収集します。 インジケーターシグナルの解釈のルール: ライン + di は正の力、-di-負の力、および ADX-はミドルの力を示しています。 ルールから、依存関係のチャートを作成します。
- +DI値に対する利益の依存性、ADXに対する方向と位置
- 利益の-DI値の依存性、その方向性と ADX に対する位置。
統計情報のクラスのデータをもう少し収集することにしました。 必要な結果:
- インジケーター値;
- 線の方向;
- 反対の移動ラインに関する位置;
- 反対の移動ラインの方向;
- ADX 行に対する位置
- ADXの方向。
class CProfitData { public: double Value; double LongProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/; double ShortProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/; CProfitData(void) ArrayInitialize(LongProfit,0);ArrayInitialize(ShortProfit,0); } ~CProfitData(void) {}; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CStaticADX : CObject { private: CADX *DataArray; double d_Step; //値配列のステップ CProfitData *PDI[][3]; //値の配列 + DI CProfitData *NDI[][3]; //値の配列-DI bool AdValues(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, double profit, ENUM_POSITION_TYPE type); int GetPDIIndex(double value); int GetNDIIndex(double value); bool Sort(void); public: CStaticADX(CADX *data, double step); ~CStaticADX(); bool Ad(long ticket, double profit, ENUM_POSITION_TYPE type); string HTML_header(void); string HTML_body(void); };
他の点では前のクラスからのアプローチとして原理原則は維持されました。 完全なコードは添付ファイルに用意されています。
5.5. アリゲーターインジケーターのデータを表示するためのクラス
このブロックの最後にアリゲーターのインジケーターのデータを収集するクラスを作成してみましょう. このインジケータのシグナルは、異なる期間の3つの移動平均に基づいています。 したがって、インジケーターを解釈するときにインジケーターラインの特定の値は重要ではありません。 はるかに重要なのは、線の方向とポジションです。
インジケーターシグナルをより具体的にするために、ラインのポジションによってトレンドを決定しましょう。 LIPのラインが、TEETHよりも高い場合、TEETHはJAWよりも高く、トレンドとして買いを検討してください。 LIPがTEETHよりも低い場合は、TEETHがJAWよりも低いので-売りを検討してください。 ラインの順番がない場合にはフラットだとみなします。
それぞれ、チャートはトレンド方向のシグナルとインジケータラインのダイナミクスから構築されます。
上記の指定されたエントリーデータに続いて CStaticAlligator クラスを作成します。 クラスの原則は、以前のクラスから取得されます。
class CStaticAlligator : CObject { private: CAlligator *DataArray; double Long_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/; //長期トレード利益の配列 double Short_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/; //Array of short feals profit bool AdValues(double jaw_value, double jaw_dinamic, double teeth_value, double teeth_dinamic, double lips_value, double lips_dinamic, double profit, ENUM_POSITION_TYPE type); public: CStaticAlligator(CAlligator *data); ~CStaticAlligator(); bool Ad(long ticket, double profit, ENUM_POSITION_TYPE type); string HTML_header(void); string HTML_body(void); };
完全なコードは添付ファイルに用意されています。
6. 情報の収集と分析の EA の構築
すべての準備タスクが完了したら、分析データの情報収集と出力のストラテジーテスターでEA を作成してみましょう。 まず第一に、EA のインプットパラメータでは、分析のテストレポートファイルの名前を指定し、使用される時間枠と使用されるインジケーターのすべての必要なパラメータを用意します。
input string FileName = "Kalman_test.html" ; input ENUM_TIMEFRAMES Timefarame = PERIOD_CURRENT ; input string s1 = "ADX" ; //--- input uint ADX_Period = 14 ; input string s2 = "Alligator" ; //--- input uint JAW_Period = 13 ; input uint JAW_Shift = 8 ; input uint TEETH_Period = 8 ; input uint TEETH_Shift = 5 ; input uint LIPS_Period = 5 ; input uint LIPS_Shift = 3 ; input ENUM_MA_METHOD Alligator_Method = MODE_SMMA ; input ENUM_APPLIED_PRICE Alligator_Price = PRICE_MEDIAN ; input string s3 = "ATR" ; //--- input uint ATR_Period = 14 ; input string s4 = "BW MFI" ; //--- input ENUM_APPLIED_VOLUME BWMFI_Volume = VOLUME_TICK ; input string s5 = "CCI" ; //--- input uint CCI_Period = 14 ; input ENUM_APPLIED_PRICE CCI_Price = PRICE_TYPICAL ; input string s6 = "Chaikin" ; //--- input uint Ch_Fast_Period = 3 ; input uint Ch_Slow_Period = 14 ; input ENUM_MA_METHOD Ch_Method = MODE_EMA ; input ENUM_APPLIED_VOLUME Ch_Volume = VOLUME_TICK ; input string s7 = "Force Index" ; //--- input uint Force_Period = 14 ; input ENUM_MA_METHOD Force_Method = MODE_SMA ; input ENUM_APPLIED_VOLUME Force_Volume = VOLUME_TICK ; input string s8 = "MACD" ; //--- input uint MACD_Fast = 12 ; input uint MACD_Slow = 26 ; input uint MACD_Signal = 9 ; input ENUM_APPLIED_PRICE MACD_Price = PRICE_CLOSE ; input string s9 = "Standart Deviation" ; //--- input uint StdDev_Period = 14 ; input uint StdDev_Shift = 0 ; input ENUM_MA_METHOD StdDev_Method = MODE_SMA ; input ENUM_APPLIED_PRICE StdDev_Price = PRICE_CLOSE ; input string s10 = "Volumes" ; //--- input ENUM_APPLIED_VOLUME Applied_Volume = VOLUME_TICK ;
次に、上記で説明したすべてのクラスのインスタンスを宣言します。
CArrayObj *Deals;
CADX *ADX;
CAlligator *Alligator;
COneBufferArray *ATR;
CBWMFI *BWMFI;
COneBufferArray *CCI;
COneBufferArray *Chaikin;
COneBufferArray *Force;
CMACD *MACD;
COneBufferArray *StdDev;
COneBufferArray *Volume;
CStaticOneBuffer *IndicatorsStatic[];
CStaticBWMFI *BWMFI_Stat;
CStaticMACD *MACD_Stat;
CStaticADX *ADX_Stat;
CStaticAlligator *Alligator_Stat;
6.1. EA 初期化関数
これまでのところ、 EA はストラテジーテスターのデータ分析に指定されているように、まず開始されている環境を確認してください。 テスターで始める場合は、その初期化を中止する必要があります。
int() { //--- if(!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION)) return INIT_FAILED;
次に、レポートファイルのテストからデータの解析を行います。 レポートからのデータ読み出し後、解析クラスインスタンスは必要ではなくなるので、メモリから削除します。
CParsing *Parsing = new CParsing(Deals); if(CheckPointer(Parsing)==POINTER_INVALID) return INIT_FAILED; if(!Parsing.ReadFile(FileName) || CheckPointer(Deals)==POINTER_INVALID || Deals.Total()<=0) { delete Parsing; return INIT_FAILED; } delete Parsing;
その後、インジケータークラスの初期化を実行します。
//--- ADX = new CADX(_Symbol,Timefarame,ADX_Period); if(CheckPointer(ADX)==POINTER_INVALID) return INIT_FAILED; //--- Alligator = new CAlligator(_Symbol,Timefarame,JAW_Period,JAW_Shift,TEETH_Period,TEETH_Shift,LIPS_Period,LIPS_Shift,Alligator_Method,Alligator_Price); if(CheckPointer(Alligator)==POINTER_INVALID) return INIT_FAILED; //--- int handle=iATR(_Symbol,Timefarame,ATR_Period); if(handle>0) { ATR = new COneBufferArray(handle); if(CheckPointer(ATR)==POINTER_INVALID) return INIT_FAILED; } //--- BWMFI = new CBWMFI(_Symbol,Timefarame,BWMFI_Volume); if(CheckPointer(BWMFI)==POINTER_INVALID) return INIT_FAILED; //--- handle=iCCI(_Symbol,Timefarame,CCI_Period,CCI_Price); if(handle>0) { CCI = new COneBufferArray(handle); if(CheckPointer(CCI)==POINTER_INVALID) return INIT_FAILED; } //--- handle=iChaikin(_Symbol,Timefarame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume); if(handle>0) { Chaikin = new COneBufferArray(handle); if(CheckPointer(Chaikin)==POINTER_INVALID) return INIT_FAILED; } //--- handle=iForce(_Symbol,Timefarame,Force_Period,Force_Method,Force_Volume); if(handle>0) { Force = new COneBufferArray(handle); if(CheckPointer(Force)==POINTER_INVALID) return INIT_FAILED; } //--- MACD = new CMACD(_Symbol,Timefarame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price); if(CheckPointer(MACD)==POINTER_INVALID) return INIT_FAILED; //--- handle=iStdDev(_Symbol,Timefarame,StdDev_Period,StdDev_Shift,StdDev_Method,StdDev_Price); if(handle>0) { StdDev = new COneBufferArray(handle); if(CheckPointer(StdDev)==POINTER_INVALID) return INIT_FAILED; } //--- handle=iVolumes(_Symbol,Timefarame,Applied_Volume); if(handle>0) { Volume = new COneBufferArray(handle); if(CheckPointer(Volume)==POINTER_INVALID) return INIT_FAILED; }
関数を終了するには、関数を0に設定してから閉じます。
cur_ticket = 0; //--- return(INIT_SUCCEEDED); }
6.2. 統計データの収集
インジケーター状態に関するデータの収集は、OnTick 関数で実行されます。 この関数の先頭に、すべてのオーダーの情報が収集されるかどうかを確認します。 yes の場合は、関数を決済します。
void OnTick() { if(cur_ticket>=Deals.Total()) return;
次のステップで分析されるトレードの性能の時間は処理したときと比べられます。 トレードの時間でない場合は、関数を終了します。
CDeal *object = Deals.At(cur_ticket); if(object.GetTime()>TimeCurrent()) return;
前のチェックアップをする場合は、インジケータークラスのインスタンスの状態を確認し、各インジケータークラスに必要な情報を呼び出し SaveNewValues 関数を保存します。
if(CheckPointer(ADX)!=POINTER_INVALID) ADX.SaveNewValues(cur_ticket); //--- if(CheckPointer(Alligator)!=POINTER_INVALID) Alligator.SaveNewValues(cur_ticket); //--- if(CheckPointer(ATR)!=POINTER_INVALID) ATR.SaveNewValues(cur_ticket); //--- if(CheckPointer(BWMFI)!=POINTER_INVALID) BWMFI.SaveNewValues(cur_ticket); //--- if(CheckPointer(CCI)!=POINTER_INVALID) CCI.SaveNewValues(cur_ticket); //--- if(CheckPointer(Chaikin)!=POINTER_INVALID) Chaikin.SaveNewValues(cur_ticket); //--- if(CheckPointer(Force)!=POINTER_INVALID) Force.SaveNewValues(cur_ticket); //--- if(CheckPointer(MACD)!=POINTER_INVALID) MACD.SaveNewValues(cur_ticket); //--- if(CheckPointer(StdDev)!=POINTER_INVALID) StdDev.SaveNewValues(cur_ticket); //--- if(CheckPointer(Volume)!=POINTER_INVALID) Volume.SaveNewValues(cur_ticket);
この関数の終わりには、処理された命令のカウンタを増加し、終了します。
cur_ticket++;
return;
}
6.3. 分析のチャート出力
データ分析およびレポート出力は、OnTester 関数で実装されます。 この関数を起動すると、分析トレードの数量を確認します。
double OnTester() { double ret=0.0; int total=Deals.Total();
分析を実施する必要がある場合は、統計クラスの初期化を実行します。
その後の処理を容易にするために、1バッファインジケーターの統計クラスを配列に収集します。 したがって、初期化カウントと並行して1つのバッファインジケータが使用されます。
int total_indy=0; if(total>0) { if(CheckPointer(ADX)!=POINTER_INVALID) ADX_Stat=new CStaticADX(ADX,1); //--- if(CheckPointer(Alligator)!=POINTER_INVALID) Alligator_Stat=new CStaticAlligator(Alligator); //--- if(CheckPointer(ATR)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(ATR,_Point*10); if(CheckPointer(indy)!=POINTER_INVALID) { if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } } //--- if(CheckPointer(BWMFI)!=POINTER_INVALID) BWMFI_Stat=new CStaticBWMFI(BWMFI,_Point*100); //--- if(CheckPointer(CCI)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(CCI,10); if(CheckPointer(indy)!=POINTER_INVALID) if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } //--- if(CheckPointer(Chaikin)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(Chaikin,100); if(CheckPointer(indy)!=POINTER_INVALID) if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } //--- if(CheckPointer(Force)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(Force,0.1); if(CheckPointer(indy)!=POINTER_INVALID) if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } //--- if(CheckPointer(MACD)!=POINTER_INVALID) MACD_Stat=new CStaticMACD(MACD,_Point*10); //--- if(CheckPointer(StdDev)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(StdDev,_Point*10); if(CheckPointer(indy)!=POINTER_INVALID) if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } //--- if(CheckPointer(Volume)!=POINTER_INVALID) { CStaticOneBuffer *indy=new CStaticOneBuffer(Volume,100); if(CheckPointer(indy)!=POINTER_INVALID) if(ArrayResize(IndicatorsStatic,total_indy+1)>0) { IndicatorsStatic[total_indy]=indy; total_indy++; } } }
さらに、インジケーターデータをそれぞれのトレードと比較し、グラフィックレポートの出力に必要な指示によって情報をグループ化します。 各統計クラスの呼び出しのAd関数で、そのパラメータのトレードについての情報を渡しました。
for(int i=0;i<total;i++) { CDeal *deal = Deals.At(i); ENUM_POSITION_TYPE type = deal.Type(); double d_profit = deal.GetProfit(); for(int ind=0;ind<total_indy;ind++) IndicatorsStatic[ind].Ad(i,d_profit,type); if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID) BWMFI_Stat.Ad(i,d_profit,type); if(CheckPointer(MACD_Stat)!=POINTER_INVALID) MACD_Stat.Ad(i,d_profit,type); if(CheckPointer(ADX_Stat)!=POINTER_INVALID) ADX_Stat.Ad(i,d_profit,type); if(CheckPointer(Alligator_Stat)!=POINTER_INVALID) Alligator_Stat.Ad(i,d_profit,type); }
データのグループ化後、レポートファイルレポートを作成します。 Report.htmlをターミナルの共有フォルダに保存します。
if(total_indy>0 || CheckPointer(BWMFI_Stat)!=POINTER_INVALID || CheckPointer(MACD_Stat)!=POINTER_INVALID || CheckPointer(ADX_Stat)!=POINTER_INVALID || CheckPointer(Alligator_Stat)!=POINTER_INVALID ) { int handle=FileOpen("Report.html",FILE_WRITE|FILE_TXT|FILE_COMMON); if(handle<0) return ret;
ファイルの先頭に html レポートのヘッダーを書く。
FileWrite(handle,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"); FileWrite(handle,"<html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"); FileWrite(handle,"<title>Deals to Indicators</title> <!-- - -->"); FileWrite(handle,"<script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js\" type=\"text/javascript\"></script>"); FileWrite(handle,"<script src=\"https://code.highcharts.com/highcharts.js\" type=\"text/javascript\"></script>"); FileWrite(handle,"<!-- - --> <script type=\"text/javascript\">$(document).ready(function(){");
次に、すべての統計クラスの HTML_header 関数を1つずつ呼び出して、チャート描画用のファイルデータを入力します。
for(int ind=0;ind<total_indy;ind++) FileWrite(handle,IndicatorsStatic[ind].HTML_header()); if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID) FileWrite(handle,BWMFI_Stat.HTML_header()); if(CheckPointer(MACD_Stat)!=POINTER_INVALID) FileWrite(handle,MACD_Stat.HTML_header()); if(CheckPointer(ADX_Stat)!=POINTER_INVALID) FileWrite(handle,ADX_Stat.HTML_header()); if(CheckPointer(Alligator_Stat)!=POINTER_INVALID) FileWrite(handle,Alligator_Stat.HTML_header());
その後、各統計クラスの HTML_body 関数を1つずつ呼び出して、レポート出力用のテンプレートを作成します。 注: この関数の呼び出しによって、統計クラスでタスクを終了し、メモリをクリアするため削除します。
FileWrite(handle,"});</script> <!-- - --> </head> <body>"); for(int ind=0;ind<total_indy;ind++) { FileWrite(handle,IndicatorsStatic[ind].HTML_body()); delete IndicatorsStatic[ind]; } if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID) { FileWrite(handle,BWMFI_Stat.HTML_body()); delete BWMFI_Stat; } if(CheckPointer(MACD_Stat)!=POINTER_INVALID) { FileWrite(handle,MACD_Stat.HTML_body()); delete MACD_Stat; } if(CheckPointer(ADX_Stat)!=POINTER_INVALID) { FileWrite(handle,ADX_Stat.HTML_body()); delete ADX_Stat; } if(CheckPointer(Alligator_Stat)!=POINTER_INVALID) { FileWrite(handle,Alligator_Stat.HTML_body()); delete Alligator_Stat; }
最後に、終了タグの書き込みを完了し、ファイルを閉じて、配列をクリアし、関数から終了します。
FileWrite(handle,"</body> </html>"); FileFlush(handle); FileClose(handle); } //--- ArrayFree(IndicatorsStatic); //--- return(ret); }
OnDeinit 関数の残りのクラスを削除することを忘れないでください。
7. 情報分析
今回のタスクは論理的な帰結に近づいています。 今での結果を見てみる必要があります。 ストラテジーテスターにこれを行うため、記事の2番目のセクションで使ったEAをテストし、新たに作成された分析EA のテストを開始し、すべての設定を繰り返します。
テストが完了したら、共有ターミナルフォルダを開き、レポートを見つけます。 ブラウザで開きます。 さらに、レポートから例を提供します。
7.1. ATR
ATR インジケーターにおける利益依存度チャートを分析するとき、潜在的に収益性の高い領域を見ることができず、トレードのフィルターの可能性はありません。
7.2. CCI
CCI インジケーターにおける利益依存のチャートは、200より高いインジケーター値での売買によって利益を吸収することができます。 しかし、売ることによって収益性の高い領域は利用できません。
7.3. Chaikin
ATR と同じようにチャイキンオシレーターは、インジケーター値とトレードから利益の間の相互関係を明らかにしませんでした。
7.4. フォースインジケーター
フォースインジケーターの分析チャートは、依存関係も明らかにしませんでした。
7.5. 標準偏差
StdDev インジケーターの値に依存関係の分析は、買いオーダーの問題領域を明らかにすることができますが、売りトレードをフィルタリングする可能性はありません。
7.6. ボリュームインジケーター
同様にボリュームインジケータのデータの分析で依存を検出できませんでした。
7.7. ビルウィリアムズ MFI
BW MFI は、カラー0でのみ開いている場合は、買いトレードのフィルタリングで利益を出すことができます。 しかし、売りトレードの依存の検出は失敗しました。
7.8. MACD
MACD インジケーターのシグナルは、収益性の高い買いトレードをフィルタリングすることができます。 これは、シグナルラインがヒストグラムの上にあるとき買いトレードすれば可能です。 しかし、この分析は売りトレードには見られません。 同時に、このインジケーターは、ヒストグラムの下または同等のシグナルラインの成長している位置での売りトレードを除外して、損失を減らすことができます。
7.9. ADX
ADX インジケーターシグナルの分析は、トレードをフィルタリングすることはできません。
7.10. アリゲーター
私の見解では、アリゲーターに対するフィルタリングの使用は、最も有望です。 アリゲーターにおけるトレードを行うためのパターンは、線の方向と位置の組み合わせにあります。 したがって、収益性の高い買いトレードが実行される場合があります。:
- インジケータラインの位置は、売りトレンドとLIPやJAWのラインが上向きになります。
- インジケーターラインの位置は、買いトレンドとLIPとTEETHの線が上向きに指示されていることを示しています。
- トレンドは不定であり、TEETHおよびJAWのラインは下方に示されます。
売りトレードでは、ミラーシグナルが使用されます。
8. 初期EAの修正
EAのトレードを分析する上で非常に広範なタスクを行っています。 さて、戦略のパフォーマンスにどのように影響するかを見てみましょう。 このため、記事 [1] のトレードシグナルモジュールには、上記の指定された分析に従って、フィルタールール付きのインジケーターを追加します。 モジュールに MACD とアリゲーターを追加することを推奨します。
順番にインジケーターフィルタを追加し、各フィルタの追加後にインジケーターにトレードを解決するための手順を循環的に実行することをお勧めします。 これは全体の戦略に各フィルタの影響をより明確にし、その複雑な影響を評価するのに役立ちます。 最初の段階の分析では、任意のインジケーターの値に利益依存を検出することはできませんが、以降の反復でそのような依存関係が表示されないことを意味しません。 これは今回はしませんが、今後役に立つかもしれません。
最初に、モジュールにインジケーターパラメータを追加します。
//| Parameter=JAW_Period,uint,13,JAW Period | //| Parameter=JAW_Shift,uint,8,JAW Shift | //| Parameter=TEETH_Period,uint,8,TEETH Period | //| Parameter=TEETH_Shift,uint,5,TEETH Shift | //| Parameter=LIPS_Period,uint,5,LIPS Period | //| Parameter=LIPS_Shift,uint,3,LIPS_Shift | //| Parameter=Alligator_Method,ENUM_MA_METHOD,MODE_SMMA,Method | //| Parameter=Alligator_Price,ENUM_APPLIED_PRICE,PRICE_MEDIAN,Alligator Price | //| Parameter=MACD_Fast,uint,12,MACD Fast | //| Parameter=MACD_Slow,uint,26,MACD Slow | //| Parameter=MACD_Signal,uint,9,MACD Signal | //| Parameter=MACD_Price,ENUM_APPLIED_PRICE,PRICE_CLOSE,MACD Price |
パラメータをプライベートブロックに格納するための変数を追加しますが、保存する関数は public に追加します。
uint ci_MACD_Fast; uint ci_MACD_Slow; uint ci_MACD_Signal; ENUM_APPLIED_PRICE ce_MACD_Price; uint ci_JAW_Period; uint ci_JAW_Shift; uint ci_TEETH_Period; uint ci_TEETH_Shift; uint ci_LIPS_Period; uint ci_LIPS_Shift; ENUM_MA_METHOD ce_Alligator_Method; ENUM_APPLIED_PRICE ce_Alligator_Price; void JAW_Period(uint value) { ci_JAW_Period = value; } void JAW_Shift(uint value) { ci_JAW_Shift = value; } void TEETH_Period(uint value) { ci_TEETH_Period= value; } void TEETH_Shift(uint value) { ci_TEETH_Shift = value; } void LIPS_Period(uint value) { ci_LIPS_Period = value; } void LIPS_Shift(uint value) { ci_LIPS_Shift = value; } void Alligator_Method(ENUM_MA_METHOD value) { ce_Alligator_Method = value; } void Alligator_Price(ENUM_APPLIED_PRICE value) { ce_Alligator_Price= value; } void MACD_Fast(uint value) { ci_MACD_Fast = value; } void MACD_Slow(uint value) { ci_MACD_Slow = value; } void MACD_Signal(uint value) { ci_MACD_Signal = value; } void MACD_Price(ENUM_APPLIED_PRICE value) { ce_MACD_Price = value; }
また、必要なデータの受信の初期化のインジケーターと関数を持つタスク用のクラスを追加する必要があります。 MACDに標準的なクラスを使用しました。 これまでのところは、アリゲーターの標準的なクラスが存在せず、移動平均の3つのクラスに置き換え、名前に応じてインジケータラインの名前に割り当てます。
protected: CiMACD m_MACD; //オブジェクトオシレーター CiMA m_JAW; CiMA m_TEETH; CiMA m_LIPS; //---インジケーターの初期化メソッド bool InitMACD(CIndicators *indicators); bool InitAlligator(CIndicators *indicators); //データ取得メソッド double Main(int ind) { return(m_MACD.Main(ind)); } double Signal(int ind) { return(m_MACD.Signal(ind)); } double DiffMain(int ind) { return(Main(ind+1)!=0 ? Main(ind)-Main(ind+1) : 0); } int AlligatorTrend(int ind); double DiffJaw(int ind) { return(m_JAW.Main(ind+1)!=0 ? m_JAW.Main(ind)/m_JAW.Main(ind+1) : 1); } double DiffTeeth(int ind) { return(m_TEETH.Main(ind+1)!=0 ? m_TEETH.Main(ind)/m_TEETH.Main(ind+1) : 1); } double DiffLips(int ind) { return(m_LIPS.Main(ind+1)!=0 ? m_LIPS.Main(ind)/m_LIPS.Main(ind+1) : 1); }
次のステップで、InitIndicators に変更を加えて、EA ライブラリにインジケータを追加します。
bool CSignalKalman::InitIndicators(CIndicators *indicators) { //---インジケーターの初期化と追加フィルタの時系列 if(!CExpertSignal::InitIndicators(indicators)) return(false); //--- initialize close serias if(CheckPointer(m_close)==POINTER_INVALID) { if(!InitClose(indicators)) return false; } //--- create and initialize MACD oscilator if(!InitMACD(indicators)) return(false); //--- create and initialize Alligator if(!InitAlligator(indicators)) return(false); //--- create and initialize Kalman Filter if(CheckPointer(Kalman)==POINTER_INVALID) Kalman=new CKalman(ci_HistoryBars,ci_ShiftPeriod,m_symbol.Name(),ce_Timeframe); //---ok return(true); }
その後、関数に追加します。 同時に、インジケーターがフィルタとして機能することを覚えておいてください。 したがって、メインシグナルを受信した後にのみ、インジケータに接続可能です。
int CSignalKalman::LongCondition(void) { if(!CalculateIndicators()) return 0; int result=0; //--- if(cd_correction>cd_forecast) { if(Signal(1)>Main(1)) result=80; else { switch(AlligatorTrend(1)) { case 1: if(DiffLips(1)>1 && DiffTeeth(1)>1 && DiffJaw(1)<=1) result=80; break; case -1: if(DiffLips(1)>1 || DiffJaw(1)>1) result=80; break; case 0: if(DiffJaw(1)<1) { if(DiffLips(1)>1) result=80; else if(DiffTeeth(1)<1) result=80; } break; } } } return result; }
類似の変更は ShortCondition 関数に加えられます。 トレード決定モジュールの完全なコードは添付ファイルで提供されます。
9. 変更した後の EA のテスト
トレーディング決定モジュールに変更を加えた後、新しいEAを作成します (トレーディングシグナルモジュールを使用したEAの作成についての詳細は、記事 [5]) に記載されています。 新たに作成した EA を、第2節の初期テストと同様のパラメータでテストしてみましょう。
テスト結果が示すように、EAのパラメータを変更することなく、フィルタの使用は0.75 から1.12 に利益率を増加させることができました。 すなわち、利益を得るために管理元の EA の損失パラメータです。 初めに、意図的に元のEAの非最適化パラメータを取ったことを思い出させてください。
結論
この記事では、トレード履歴から標準的なインジケーターにフィルタシステムを導入する技術について実証しました。 テストすることによって、このシステムは、EAの運用収益性の具体的な結果を示すことができました。 提案システムは、既存のトレーディングシステムの最適化ではなく、新しいシステムを作成しようとするときに適用することができます。
References
記事で使用されるプログラム:
# |
Name |
Type |
Description |
---|---|---|---|
1 | Kalman.mqh | クラスライブラリ | カルマンフィルタクラス |
2 | SignalKalman.mqh | クラスライブラリ | カルマンフィルタによるトレーディングシグナルモジュール |
3 | SignalKalman+Filters.mqh | クラスライブラリ | インジケーターフィルタ追加後のカルマンフィルタによるトレーディングシグナルモジュール |
4 | Kalman_expert.mq5 | EA | カルマンフィルタ適用の戦略に関するEA |
5 | Kalman+Filters.mq5 | EA | カルマンフィルタ適用による戦略EAの修正 |
6 | Deals_to_Indicators.mq5 | EA | トレード履歴をインジケーターに導入するEA |
7 | Deal.mqh | クラスライブラリ | トレードに関する情報を保存するためのクラス |
8 | Parsing.mqh | クラスライブラリ | テストレポートからのトレード履歴解析のクラス |
9 | Value.mqh | クラスライブラリ | インジケーターバッファの状態でデータを保存するためのクラス |
10 | OneBufferArray.mqh | クラスライブラリ | 1バッファインジケーターのデータ履歴を保存するクラス |
11 | StaticOneBuffer.mqh | クラスライブラリ | 1バッファインジケーター統計の収集と解析のクラス |
12 | ADXValue.mqh | クラスライブラリ | ADX インジケーターの状態でデータを保存するためのクラス |
13 | ADX.mqh | クラスライブラリ | ADX インジケーターのデータ履歴を保存するためのクラス |
14 | StaticADX.mqh | クラスライブラリ | ADX インジケーター統計の収集と分析のクラス |
15 | AlligatorValue.mqh | クラスライブラリ | アリゲーターのインジケーターの状態でデータを保存するためのクラス |
16 | Alligator.mqh | クラスライブラリ | アリゲーターのインジケーターのデータ履歴を保存するためのクラス |
17 | StaticAlligator.mqh | クラスライブラリ | アリゲーターのインジケーター統計の収集と分析のクラス |
18 | BWMFIValue.mqh | クラスライブラリ | BW MFI インジケータ状態でデータを保存するためのクラス |
19 | BWMFI.mqh | クラスライブラリ | BW MFI のデータ履歴を保存するためのクラス |
20 | StaticBWMFI.mqh | クラスライブラリ | BW MFI インジケーター統計の収集と分析のクラス |
21 | MACDValue.mqh | クラスライブラリ | MACD インジケーターのデータを保存するためのクラス |
22 | MACD.mqh | クラスライブラリ | MACD インジケーターのデータ履歴を保存するためのクラス |
23 | StaticMACD.mqh | クラスライブラリ | MACD インジケーター統計の収集と分析のクラス |
24 | Reports.zip | Archive | アーカイブには、ストラテジーテスターと分析レポートのEAのテスト結果があります。 |