ピボット・パターン:『ダブルトップ・ダブルボトム』パターンのテスト
目次
概論
『どれくらいトレンドが続くか?』の記事内で検証した分析は、価格変動の約60%がトレンドにあることを示しています。そして、トレンドの始まりにポジションを持ち始めることで、最大の結果を得る可能性ができます。トレンド逆転ポイントを検索すると、多数の反転パターンが形成されています。最も有名で頻繁に使用されるパターンの1つはダブルトップ・ダブルボトムです。
1. パターン形成の理論的側面
価格チャート上では、『ダブルトップ・ダブルボトム』のパターンが頻繁に検出されます。その形成の性質は取引レベルの理論に密接に関連しています。このパターンは、前回の動きに応じて価格の動きがレジスタンスやサポートのレベルにあるトレンドやトレンドの終わりに形成され、再テストがレベルを突破することなく修正されると、再びロールバックされます。
この時点で、カウンタートレンドトレーダーは、レベルからのリバウンドを取引し、価格を修正方向に押し上げます。是正措置の動きに伴って、トレンドトレーダーは市場を脱出し始めます。利益を確定するか、またはレジスタンス/サポートレベルを打開するために実行中の取引を終わらせます。そのような行動は、動きをさらに強化し、新しいトレンドの出現をもたらします。
チャート上のパターンを検索するとき、トップ/ボトムの正確な一致を探すべきではありません。トップ/ボトムレベル偏差は正常なものとみなされます。大切なのは、トップが同じレジスタス/サポートレベル内にあることです。パターンの信頼性は、その形成が生じるゾーン内のレベルの強度に比例します。
2. パターンによる取引戦略
パターンが広く普及した為、その分さまざまな取引戦略があります。インターネット上では、このパターンを解明するために少なくとも3つの異なるエントリーポイントがあります。
2.1. シナリオ 1
最初のエントリーポイントは、ネックラインの突破に基づいています。ストップロスはトップ/ボトムのラインの後ろに設定されます。同時に、『ネックライン突破』を決定するための様々なアプローチが存在します。ここでは、ネックライン下のバークローズ、またはネックライン突破の距離の確定のために使用することができます。どちらのアプローチも長所と短所があります。鋭い動きで、ローソクを閉じることはネックラインから十分な距離を置いて起こる場合があり、この場合パターンの使用が効果のないものになります。
このアプローチの短所としては、比較的高いレベルのストップロスがあり、使用される戦略の利益/リスクの比率が低下します。
2.2. シナリオ 2
2番目のエントリーポイントは、サポートからのネックラインがレジスタンスになり、その逆の場合のミラーレベルの理論に基づいています。ここでは、ネックライン突破後に価格がネックラインに向かって下落した時にエントリーします。この方法では、ストップロスは最後の取得極値に設定され、これによりストップロスレベルが大幅に下がります。残念ながら、価格は必ずしもネックライン突破後にテストするとは限らないことに注意してください。この事実は、エントリの数を大幅に減らします。
2.3. シナリオ 3
3番目のエントリーポイントはトレンド理論に基づいています。動きの始まりのポイントからネックラインの極値までのトレンドラインの突破によって決定します。ストップロスは、第1の場合と同様に、トップ/ボトムのラインの後ろに設定されます。早期エントリーは、最初のエントリーポイントに比べて、低いストップロスレベルを与えます。また、第2のシナリオと比べて、より多くのシグナルがあります。同時に、このようなエントリーポイントは、トレンドの継続の数字である極値線とネックまたはペナントパターンとの間にチャネルを形成することが可能であるため、より多くの誤シグナルを出します。
3つの戦略はすべて、極値からネックラインまでの距離に等しいレベルでのクローズを推奨しています。
また、チャート上のパターンを決定する際には、トップ/ボトムの二重表示が価格の動きからはっきりとわかるかどうかに注意をする必要があります。しばしばパターンを記述するとき、2つのトップ/ボトムの間に少なくとも6つのバーがなければならないなどの制限が加えられます。
さらに、パターンの形成は価格水準の理論に基づいているので、パターンによる取引は、少なくともそれに反するものであってはなりません。従って、意図された目的に基づいて、ネックラインは、最初の動きのフィボナッチレベルの50より低くすべきではありません。さらに、誤シグナルをフィルタリングするために、価格レベルの強さの指標として、第1の取得(ネックラインの形成)の最小レベルを追加することができます。
3.アドバイザーの作成
3.1. 極値検索
取引アドバイザーを作成するには、まずパターン検索ブロックを作成します。チャート上の極値を検索するには、MetaTrader 5のボックス配信によるジグザグ・インジケータを使用します。[1]記事で説明されている技術に従って、インジケータの計算された部分をクラスに移してみましょう。ご存知のように、このインジケータは、極値のポイントでの価格数値を含む、2つのインジケータバッファを含んでいます。両極端の間で、インジケータバッファに空の値が含まれます。複数の空値を含む2つのインジケータバッファを作成しないために、これらは極値に関する情報を含む構造配列に置き換えられました。極値に関する情報を格納する構造は以下の通りです。
struct s_Extremum { datetime TimeStartBar; double Price; s_Extremum(void) : TimeStartBar(0), Price(0) { } void Clear(void) { TimeStartBar=0; Price=0; } };
私はジグザグ・インジケータを少なくとも一度使用したことがある人なら、最適なパラメータを検索するときにどれくらいの妥協が必要かを知っていると思います。パラメータの値が小さいと、1つの大きな動きが小さな部分に分割されます。逆に、パラメータの値が大きすぎると、短い動きが省略されます。グラフィックパターンを検索するためのアルゴリズムは、極値検出の質が求められます。『相反するものを共存させる』試みでは、インジケータはパラメータの小さい値を使用し、一方で、片方向の動きと短時間の修正を1つの動きに組み合わせる追加の設定を作成することになりました。
この課題をクリアするために、CTrendsクラスが構築されました。クラスの見出しを以下に示します。初期化中、インジケータクラスのオブジェクトへの参照と、新しい動きがトレンドの継続とみなされる最小の動きのサイズがクラスへ送信されます。
class CTrends : public CObject { private: CZigZag *C_ZigZag; // ZigZagインジケータのオブジェクトへの参照 s_Extremum Trends[]; // 極値配列 int i_total; // 保存された極値の総数 double d_MinCorrection; // トレンド継続の為の動きの最小サイズ public: CTrends(); ~CTrends(); // --- クラス初期化メソッド virtual bool Create(CZigZag *pointer, double min_correction); // --- 極値についての情報を得る virtual bool IsHigh(s_Extremum &pointer) const; virtual bool Extremum(s_Extremum &pointer, const int position=0); virtual int ExtremumByTime(datetime time); // --- 一般情報の取得 virtual int Total(void) { Calculate(); return i_total; } virtual string Symbol(void) const { if(CheckPointer(C_ZigZag)==POINTER_INVALID) return "Not Initilized"; return C_ZigZag.Symbol(); } virtual ENUM_TIMEFRAMES Timeframe(void) const { if(CheckPointer(C_ZigZag)==POINTER_INVALID) return PERIOD_CURRENT; return C_ZigZag.Timeframe(); } protected: virtual bool Calculate(void); virtual bool AddTrendPoint(s_Extremum &pointer); };
クラス内の極値についての情報の取得は、クラス内の以下の方法になります。
- ExtremumByTime ー 指定した時間でデータベースの極値を取得する
- Extremum ー データベース内の指定したポジションに極値を返す
- IsHigh ー 返品true指定された極値が頂点である場合、 false- 中空の場合。
一般情報ブロックには、極値によって保存された合計金額、使用されたシンボルと時間枠を返すメソッドが含まれています。
メインとなるクラスロジックはCalculateメソッドで実装されています。それを詳しく見てみましょう。
このメソッドの冒頭で、インジケータクラスの対象に対する参照の関連性と、インジケータによって検出された極値の存在をチェックします。
bool CTrends::Calculate(void) { if(CheckPointer(C_ZigZag)==POINTER_INVALID) return false; //--- if(C_ZigZag.Total()==0) return true;
次に、未処理の極値の数を決定します。すべての極値が処理されている場合、trueの結果と共にメソッドを終了します。
int start=(i_total<=0 ? C_ZigZag.Total() : C_ZigZag.ExtremumByTime(Trends[i_total-1].TimeStartBar)); switch(start) { case 0: return true; break; case -1: start=(i_total<=1 ? C_ZigZag.Total() : C_ZigZag.ExtremumByTime(Trends[i_total-2].TimeStartBar)); if(start<0 || ArrayResize(Trends,i_total-1)<=0) { ArrayFree(Trends); i_total=0; start=C_ZigZag.Total(); } else i_total=ArraySize(Trends); if(start==0) return true; break; }
その後、インジケータクラスから極値の必要数を求めます。
s_Extremum base[]; if(!C_ZigZag.Extremums(base,0,start)) return false; int total=ArraySize(base); if(total<=0) return true;
これまでにデータベースに極値が1つもない場合は、AddTrendPointメソッドを呼び出すことでデータベースに最も古い極値を追加します。
if(i_total==0) if(!AddTrendPoint(base[total-1])) return false;
次に、ロードされたすべての極値を列挙したサイクルを構成します。最後に保存したものまでの極値はスキップされます。
for(int i=total-1;i>=0;i--) { int trends_pos=i_total-1; if(Trends[trends_pos].TimeStartBar>=base[i].TimeStartBar) continue;
次のステップでは、トップが単方向であるかどうかをチェックします。新しい極値が前の情報を再描画する場合は、情報を更新します。
if(IsHigh(Trends[trends_pos])) { if(IsHigh(base[i])) { if(Trends[trends_pos].Price<base[i].Price) { Trends[trends_pos].Price=base[i].Price; Trends[trends_pos].TimeStartBar=base[i].TimeStartBar; } continue; }
多方向のトップについては、新しい動きが以前のトレンドの続きであるかどうかを確認します。この質問に肯定的な答えがある場合は、極値に関する情報を更新してください。チェック結果がそうでない場合、AddTrendPointメソッドを呼び出して極値に関する情報を追加します。
else { if(trends_pos>1 && Trends[trends_pos-1].Price>base[i].Price && Trends[trends_pos-2].Price>Trends[trends_pos].Price) { double trend=fabs(Trends[trends_pos].Price-Trends[trends_pos-1].Price); double correction=fabs(Trends[trends_pos].Price-base[i].Price); if(fabs(1-correction/trend)>d_MinCorrection) { Trends[trends_pos-1].Price=base[i].Price; Trends[trends_pos-1].TimeStartBar=base[i].TimeStartBar; i_total--; ArrayResize(Trends,i_total); continue; } } AddTrendPoint(base[i]); } }
すべてのクラスの完全なコードとそのメソッドは、添付ファイルにあります。
3.2. パターン検索
価格の極値を決定したら、ポジションを開けるためのポイントを見つけるためのブロックを構築します。この作業を2つのサブステップに分けます。
- ポテンシャルのあるポジションオープンのパターンを検索します。
- ポジションオープンを直接指示します。
この機能はCPtternクラスに割り当てられます。このクラスのタイトルは以下に示されています。
class CPattern : public CObject { private: s_Extremum s_StartTrend; //トレンドの開始点 s_Extremum s_StartCorrection; //修正の開始点 s_Extremum s_EndCorrection; //修正の終了点 s_Extremum s_EndTrend; //トレンドの終了点 double d_MinCorrection; //最小補正 double d_MaxCorrection; //最大補正 //--- bool b_found; //『パターン検出』フラグ //--- CTrends *C_Trends; public: CPattern(); ~CPattern(); // --- クラスを初期化する virtual bool Create(CTrends *trends, double min_correction, double max_correction); // --- パターンとエントリポイントの検索方法 virtual bool Search(datetime start_time); virtual bool CheckSignal(int &signal, double &sl, double &tp1, double &tp2); //--- method of comparing the objects virtual int Compare(const CPattern *node,const int mode=0) const; // --- パターンの極値情報を取得するメソッド s_Extremum StartTrend(void) const { return s_StartTrend; } s_Extremum StartCorrection(void) const { return s_StartCorrection; } s_Extremum EndCorrection(void) const { return s_EndCorrection; } s_Extremum EndTrend(void) const { return s_EndTrend; } virtual datetime EndTrendTime(void) { return s_EndTrend.TimeStartBar; } };
パターンは、隣接する4つの極値によって決定され、その情報は4つの構造体s_StartTrend、s_StartCorrection、s_EndCorrection、およびs_EndTrendに保存されます。パターンを特定するには、d_MinCorrectionおよびd_MaxCorrection変数に格納される最小および最大補正レベルも必要です。最近作成されたクラスCTrendsのインスタンスから極小値が取得されます。
クラスの初期化時に、CTrendsクラスのオブジェクトへのポインタと修正の境界レベルを与えます。メソッドの中で、渡されたポインタの妥当性をチェックし、受け取った情報を保存し、極値の構造をクリアします。
bool CPattern::Create(CTrends *trends,double min_correction,double max_correction) { if(CheckPointer(trends)==POINTER_INVALID) return false; //--- C_Trends=trends; b_found=false; s_StartTrend.Clear(); s_StartCorrection.Clear(); s_EndCorrection.Clear(); s_EndTrend.Clear(); d_MinCorrection=min_correction; d_MaxCorrection=max_correction; //--- return true; }
Search()メソッドでポテンシャルのあるパターンを検索します。このメソッドはパラメータで、パターン検索の開始日を受け取り、検索結果を通知するブール値を返します。この方法のアルゴリズムをより詳細に解説します。
メソッドの冒頭で、CTrendsクラスのオブジェクトへのポインタの関連性と保存された極値の存在をチェックします。否定的な結果の場合は、falseの結果でメソッドを終了します。
bool CPattern::Search(datetime start_time) { if(CheckPointer(C_Trends)==POINTER_INVALID || C_Trends.Total()<4) return false;
次に、入力パラメータで得られた日付に対応する極値を決定します。極値が見つからない場合は、falseの結果とともにメソッドを終了します。
int start=C_Trends.ExtremumByTime(start_time); if(start<0) return false;
次に、指定された日付から最後の日付までのすべての極値を調べるサイクルを構成します。最初に4つの連続した極値を取得します。極値のうちの少なくとも1つを受信できない場合、次の極値に進みます。
b_found=false; for(int i=start;i>=0;i--) { if((i+3)>=C_Trends.Total()) continue; if(!C_Trends.Extremum(s_StartTrend,i+3) || !C_Trends.Extremum(s_StartCorrection,i+2) || !C_Trends.Extremum(s_EndCorrection,i+1) || !C_Trends.Extremum(s_EndTrend,i)) continue;
次の段階で、極値と希望するパターンとの相性をチェックします。極値が希望するパターンに会わない場合は、次の極値に進みます。パターンを見つけるときは、trueのフラグを立て、同じ結果を持つメソッドを終了します。
double trend=s_StartCorrection.Price-s_StartTrend.Price; double correction=s_StartCorrection.Price-s_EndCorrection.Price; double re_trial=s_EndTrend.Price-s_EndCorrection.Price; double koef=correction/trend; if(koef<d_MinCorrection || koef>d_MaxCorrection || (1-fmin(correction,re_trial)/fmax(correction,re_trial))>=d_MaxCorrection) continue; b_found= true; //--- break; } //--- return b_found; }
次のステップでは、パターンを決定した後、エントリポイントを探します。第2のシナリオに従ってエントリポイントを探します。しかし、ネックラインへの価格の反転リスクを最小限に抑えるために、より新しい時間枠でシグナルの確証を探します。
この機能を実装するために、CheckSignal()メソッドを作成します。このメソッドは、シグナルそのものに加えて、ストップロスと利益のトレードレベルを返します。したがって、メソッドのパラメータでは、変数へのポインタを使用します。
メソッドの最初に、以前に見つかったパターンの存在のフラグをチェックし、パターンが見つからない場合は、結果をfalseにしてメソッドを終了します。
bool CPattern::CheckSignal(int &signal, double &sl, double &tp1, double &tp2) { if(!b_found) return false;
次に、パターン形成のローソクを閉じる時間を決定し、パターン形成の開始から現在の瞬間までの時間枠のデータをロードする。
string symbol=C_Trends.Symbol(); if(symbol=="Not Initilized") return false; datetime start_time=s_EndTrend.TimeStartBar+PeriodSeconds(C_Trends.Timeframe()); int shift=iBarShift(symbol,e_ConfirmationTF,start_time); if(shift<0) return false; MqlRates rates[]; int total=CopyRates(symbol,e_ConfirmationTF,0,shift+1,rates); if(total<=0) return false;
その後、次々にバーがネックラインを突破するサイクルと、予想される動きの方向へのネックラインの後ろのローソクの終了と補正を構成します。
ここで私はいくつかの制限事項を追加しました。
- 価格がトップ/ボトムのレベルを超えた場合、パターンは無効と見なされます。
- 価格が予想テイクプロフィットレベルに達した場合、パターンは無効と見なされます。
- シグナル発生の瞬間からポジションオープンまで2つ以上のローソクが形成された場合、取引を開始するシグナルは無視されます。
signal=0; sl=tp1=tp2=-1; bool up_trend=C_Trends.IsHigh(s_EndTrend); double extremum=(up_trend ? fmax(s_StartCorrection.Price,s_EndTrend.Price) : fmin(s_StartCorrection.Price,s_EndTrend.Price)); double exit_level=2*s_EndCorrection.Price - extremum; bool break_neck=false; for(int i=0;i<total;i++) { if(up_trend) { if(rates[i].low<=exit_level || rates[i].high>extremum) return false; if(!break_neck) { if(rates[i].close>s_EndCorrection.Price) continue; break_neck=true; continue; } if(rates[i].high>s_EndCorrection.Price) { if(sl==-1) sl=rates[i].high; else sl=fmax(sl,rates[i].high); } if(rates[i].close<s_EndCorrection.Price || sl==-1) continue; if((total-i)>2) return false;
ポジションを開くためのシグナルの検出後、シグナルのタイプ(『-1』-売り、『1』-買い)および取引レベルを指定します。ストップロスをネックライン突破後の最大補正深度のレベルに設定します。テイクプロフィットのために、2つのレベルを設定しました。
1. ポジションを開く方向に極値線からネックまでの距離の90%に等しいレベル。
2. 以前のトレンドの動きの90%に等しいレベル。
同時に、1回目のテイクプロフィットレベルが、2回目のテイクプロフィットレベルを上回らないという制限を加えます。
signal=-1; double top=fmax(s_StartCorrection.Price,s_EndTrend.Price); tp1=s_EndCorrection.Price-(top-s_EndCorrection.Price)*0.9; tp2=top-(top-s_StartTrend.Price)*0.9; tp1=fmax(tp1,tp2); break; }
すべてのクラスとメソッドの完全なコードは添付ファイルにあります。
3.3. アドバイザーの収集
準備作業が終わったら、すべてのブロックを1つのアドバイザに集めます。以下の3つのブロックに分割する外部変数を宣言します。
- ジグザグインジケータのパラメータ。
- パターンとエントリポイントを検索するためのパラメータ。
- 取引操作実行のためのパラメータ。
sinput string s1 = "---- ZigZag Settings ----"; //--- input int i_Depth = 12; // Depth input int i_Deviation = 100; // Deviation input int i_Backstep = 3; // Backstep input int i_MaxHistory = 1000; // Max history, bars input ENUM_TIMEFRAMES e_TimeFrame = PERIOD_M30; // Work Timeframe sinput string s2 = "---- Pattern Settings ----"; //--- input double d_MinCorrection= 0.118; // Minimal Correction input double d_MaxCorrection= 0.5; // Maximal Correction input ENUM_TIMEFRAMES e_ConfirmationTF= PERIOD_M5; // Timeframe for confirmation sinput string s3 = "---- Trade Settings ----"; //--- input double d_Lot = 0.1; // Trade Lot input ulong l_Slippage = 10; // Slippage input uint i_SL = 350; // Stop Loss Backstep, points
グローバル変数のブロックでは、パターンオブジェクトへのポインタ、取引操作クラスのインスタンス、処理中のクラスインスタンスへのポインタを格納するパターンを検索するクラスのインスタンス、そして次のパターンの検索開始時刻を格納する変数を宣言します。
CArrayObj *ar_Objects;
CTrade *Trade;
CPattern *Pattern;
datetime start_search;
そのポジションに2つのテイクプロフィットを同時に設定する機能を実装するために、我記事[2]の技術を使用しましょう。
OnInit()関数で、必要なすべてのオブジェクトを初期化します。同時に、CZigZagクラスとCTrendsクラスのインスタンスをグローバルに宣言していないため、それらを初期化し、これらのオブジェクトへのポインタを配列に追加するだけです。いずれかの段階で初期化エラーが発生した場合は、結果をINIT_FAILEDにして関数を終了します。
int OnInit() { //--- オブジェクトの配列の初期化 ar_Objects=new CArrayObj(); if(CheckPointer(ar_Objects)==POINTER_INVALID) return INIT_FAILED; // ---ジグザグインジケータクラスの初期化 CZigZag *zig_zag=new CZigZag(); if(CheckPointer(zig_zag)==POINTER_INVALID) return INIT_FAILED; if(!ar_Objects.Add(zig_zag)) { delete zig_zag; return INIT_FAILED; } zig_zag.Create(_Symbol,i_Depth,i_Deviation,i_Backstep,e_TimeFrame); zig_zag.MaxHistory(i_MaxHistory); // ---トレンド動向検索クラスの初期化 CTrends *trends=new CTrends(); if(CheckPointer(trends)==POINTER_INVALID) return INIT_FAILED; if(!ar_Objects.Add(trends)) { delete trends; return INIT_FAILED; } if(!trends.Create(zig_zag,d_MinCorrection)) return INIT_FAILED; // --- 取引操作クラスの初期化 Trade=new CTrade(); if(CheckPointer(Trade)==POINTER_INVALID) return INIT_FAILED; Trade.SetAsyncMode(false); Trade.SetDeviationInPoints(l_Slippage); Trade.SetTypeFillingBySymbol(_Symbol); // --- 補助変数の初期化 start_search=0; CLimitTakeProfit::OnlyOneSymbol(true); //--- return(INIT_SUCCEEDED); }
OnDeinit()関数では、使用するオブジェクトのインスタンスをクリーンアップします。
void OnDeinit(const int reason) { //--- if(CheckPointer(ar_Objects)!=POINTER_INVALID) { for(int i=ar_Objects.Total()-1;i>=0;i--) delete ar_Objects.At(i); delete ar_Objects; } if(CheckPointer(Trade)!=POINTER_INVALID) delete Trade; if(CheckPointer(Pattern)!=POINTER_INVALID) delete Pattern; }
いつものように、主な機能はOnTick関数で実装されています。この関数の機能は、2つのブロックに分けられます。
1. 以前に見つかったパターンでポジションを開くシグナルをチェックします。これはシグナルの確証を探す小さな時間枠で、新しいローソクの各開口部で起動します。
2. 新しいパターンを検索する。これは(インジケータに指定された)時間枠で新しいローソクの発生時に起動します。
関数の冒頭で、エントリポイントが確認された時間枠で新しいバーの開始を確認します。バーが形成されていない場合は、次のティックまで関数を終了します。このアプローチは、エントリポイントを確認するための時間枠が作業時間枠以下の場合にのみ正しく機能することに注意してください。それ以外の場合は、関数を終了する代わりに、パターン検索ブロックに移動する必要があります。
void OnTick() { //--- static datetime Last_CfTF=0; datetime series=(datetime)SeriesInfoInteger(_Symbol,e_ConfirmationTF,SERIES_LASTBAR_DATE); if(Last_CfTF>=series) return; Last_CfTF=series;
新しいバーが発生した場合、以前に保存されたすべてのパターンをチェックしてシグナルがポジションを開くようにサイクルを調整します。ここでは、配列の最初の2つのオブジェクトではシグナルの有無をチェックしないことに注意してください。これらのセルには、極値検索クラスのインスタンスへのポインタが格納されているからです。格納されたポインタが無効であるか、シグナルテスト関数がfalse値を返す場合、ポインタは配列から削除されます。パターンのシグナルのチェックは、下でご紹介するCheckPattern()関数で直接実行されます。
int total=ar_Objects.Total(); for(int i=2;i<total;i++) { if(CheckPointer(ar_Objects.At(i))==POINTER_INVALID) if(ar_Objects.Delete(i)) { i--; total--; continue; } //--- if(!CheckPattern(ar_Objects.At(i))) { if(ar_Objects.Delete(i)) { i--; total--; continue; } } }
以前に見つかったパターンを確認した後、新しいパターンを検索する2番目のブロックに進みます。これを行うには、作業する時間枠に新しいバーがあるかどうかを確認します。新しいバーが形成されなかった場合、新しいティックを待ち、関数を終了します。
static datetime Last_WT=0; series=(datetime)SeriesInfoInteger(_Symbol,e_TimeFrame,SERIES_LASTBAR_DATE); if(Last_WT>=series) return;
新しいバーが表示されたら、パターンの検索の初期日付を決定します(分析履歴の深さのパラメータを考慮して)。次に、CPatternクラスのオブジェクトへのポインタの関連性をチェックし、無効なポインタの場合はクラスの新しいインスタンスを作成します。
start_search=iTime(_Symbol,e_TimeFrame,fmin(i_MaxHistory,Bars(_Symbol,e_TimeFrame))); if(CheckPointer(Pattern)==POINTER_INVALID) { Pattern=new CPattern(); if(CheckPointer(Pattern)==POINTER_INVALID) return; if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection)) { delete Pattern; return; } } Last_WT=series;
次にサイクルで、ポテンシャルのあるパターンを検索するメソッドを呼び出します。検索が成功した場合、新しいパターンの検索の開始日をシフトし、以前に見つかった配列内のパターンの存在を確認します。パターンが既に配列内にある場合は、新しい検索に移動します。
while(!IsStopped() && Pattern.Search(start_search)) { start_search=fmax(start_search,Pattern.EndTrendTime()+PeriodSeconds(e_TimeFrame)); bool found=false; for(int i=2;i<ar_Objects.Total();i++) if(Pattern.Compare(ar_Objects.At(i),0)==0) { found=true; break; } if(found) continue;
新しいパターンが見つかった場合は、CheckPattern()関数を呼び出してシグナルをチェックしてポジションを開きます。その後、必要に応じてパターンを配列に保存し、次の検索のためにクラスの新しいインスタンスを初期化します。サイクルは、次回の検索でSearch()メソッドがfalse値を返すまで続きます。
if(!CheckPattern(Pattern)) continue; if(!ar_Objects.Add(Pattern)) continue; Pattern=new CPattern(); if(CheckPointer(Pattern)==POINTER_INVALID) break; if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection)) { delete Pattern; break; } } //--- return; }
より完全なものにするためにも、アルゴリズム関数のCheckPattern()を検討することを提案したいと思います。パラメータでは、このメソッドはCPaternクラスのインスタンスへのポインタを受け取り、操作の結果の論理値を返します。検証する関数からfalse値を受け取ると、分析されたパターンは、格納されたオブジェクトの配列から取り除かれます。
関数の始めに、CPatternクラスのポジションを開くシグナルを検索するメソッドを呼び出します。チェックがエラーの場合は、falseの結果とともに関数を終了します。
bool CheckPattern(CPattern *pattern) { int signal=0; double sl=-1, tp1=-1, tp2=-1; if(!pattern.CheckSignal(signal,sl,tp1,tp2)) return false;
ポジションを開くシグナルを正常に検出すると、取引レベルを設定し、受信したシグナルに従ってポジションを開くための注文を送信します。
double price=0; double to_close=100; //--- switch(signal) { case 1: price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); CLimitTakeProfit::Clear(); if((tp1-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point) if(CLimitTakeProfit::AddTakeProfit((uint)((tp1-price)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100))) to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100); if(to_close>0 && (tp2-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point) if(!CLimitTakeProfit::AddTakeProfit((uint)((tp2-price)/_Point),to_close)) return false; if(Trade.Buy(d_Lot,_Symbol,price,sl-i_SL*_Point,0,NULL)) return false; break; case -1: price=SymbolInfoDouble(_Symbol,SYMBOL_BID); CLimitTakeProfit::Clear(); if((price-tp1)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point) if(CLimitTakeProfit::AddTakeProfit((uint)((price-tp1)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100))) to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100); if(to_close>0 && (price-tp2)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point) if(!CLimitTakeProfit::AddTakeProfit((uint)((price-tp2)/_Point),to_close)) return false; if(Trade.Sell(d_Lot,_Symbol,price,sl+i_SL*_Point,0,NULL)) return false; break; } //--- return true; }
ポジションが正常に開かれた場合、falseの結果と共に関数を終了することに注意をしてください。これはエラーではなく、使用済みパターンを配列から削除する必要があるためです。このステップは、同じパターン上にポジションを再度開くことを避けるために役立ちます。
すべてのメソッドと関数の完全なコードは添付ファイルにあります。
4.ストラテジーテスト
アドバイザーを作成した後は、過去のデータをチェックしてください。EURUSDのペアについては、2018年の9ヶ月間でテストを行います。M30の時間枠でパターンを検索し、M5の時間枠上のポジションの開始点を探します。
テスト結果には、アドバイザーの利益を生み出す能力が実証されました。テスト期間中、アドバイザーは90の取引を行い、そのうち70が収益を上げました。アドバイザーは2.02の利益率と4.77の回復係数を示し、これは実際の取引でもアドバイザーを使用することができる可能性を示しています。完全なテスト結果を以下に示します。
まとめ
この記事では、『ダブルトップ/ボトム』トレンド反転パターンを扱うエキスパートアドバイザーを作成しました。アドバイザーの履歴データ上でのテストでは、受け入れることができる結果と、アドバイザーの利益を生み出す能力が示されました。これは、 『ダブルトップ/ボトム』パターンを使用して、トレンド逆転の良好なシグナルとしてポジション開始ポイントを検索することができるということを確かめるものとなりました。
リンク
記事で使用されているプログラム:
# | 名前 | タイプ | 説明 |
---|---|---|---|
1 | ZigZag.mqh | クラスライブラリ | ジグザグインジケータのクラス |
2 | Trends.mqh | クラスライブラリ | トレンド検索クラス |
3 | Pattern.mqh | クラスライブラリ | パターンを扱うクラス |
4 | LimitTakeProfit.mqh | クラスライブラリ | テイクプロフィットオーダーを置き換えるクラス |
5 | Header.mqh | ライブラリ | アドバイザーのヘッダーファイル |
6 | DoubleTop.mq5 | エキスパートアドバイザ | 『ダブルトップ/ボトム』ストラテジーに基づくアドバイザー |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/5319
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索