ピボット・パターン:『ヘッドアンドショルダー』パターンのテスト

Dmitriy Gizlyk | 28 1月, 2019

目次

概論

『反転パターン:ダブルトップ/ボトムパターンのテスト』の記事では、ダブルトップ反転パターンを使用して取引ストラテジーをレビューし、テストしました。この記事は始めたテーマを引き継ぎ、今回は『ヘッドアンドショルダー』と呼ばれるもう1つの反転パターンを検証したいと思います。


1. パターン形成の理論的側面

『ヘッドアンドショルダー』と『反転ヘッドアンドショルダー』のパターンは、最もよく広く周知され使用されている古典的なグラフィック解析のパターンの1つです。パターンの名前自体が、このグラフィック構造を明確に示しています。『ヘッドアンドショルダー』パターンは、強いトレンドの終わりに形成され、ツールを売るシグナルを出します。パターン自体は、価格チャートの3つの連続したトップで構成されています。同時に、平均極値は、肩の上の頭のように、隣接する2つの極値の点よりも上に上がります。中央のトップは『ヘッド』と呼ばれ、他の2つは『ショルダー』と呼ばれています。パターンのトップ間のくぼみを結ぶ線をネックラインと呼びます。同時に、ネックラインが左に傾いているパターンシグナルはより強いものと考えられています。反転したヘッドアンドショルダーパターンは、ヘッドアンドショルダーパターンのミラーコピーであり、ブルモデルでもあります。

ヘッドアンドショルダーパターン

ほとんどの場合、パターンは、サポート/レジスタンス価格レベルの嘘の突破時に形成されます。トレンドプレイヤーは、価格レベル(左ショルダーのトップ)からのわずかな修正を、現在のトレンドの方向に価格を戻し、左ショルダーのレベルの突破を起こす、ポジションを増やす良い機会として認識しています。現在のサポート/レジスタンスレベルを突破した後、カウンタートレンドのプレイヤーは弱いトレンドを壊し、自分のポジションを広げることで新しい修正につなげます。トレンドの逆転が形成され、価格は再びレベル以下に戻ります(ヘッドパターンが形成されます)。トレンドを再開しようとする再試行はその弱さを見せ、小さな動きが形成されます(右ショルダー)。この時点で、市場の参加者はトレンドの変化に気付きます。トレンドプレイヤーはポジションを終了し、反トレンドプレイヤーはポジションを獲得します。これが強力な動きと新しいトレンドの形成につながります。


2. パターンによる取引戦略

『ダブルトップ/ボトム』パターンと同様に、『ヘッドアンドショルダー』パターンを取引するためのさまざまな戦略があります。そして、多くの点で、前回のパターンの取引戦略に似ています。

2.1. シナリオ 1

最初の戦略はネックラインの突破に基づいています。この場合、分析期間のClose価格が突破された後に注文を行います。このケースではストップロスは、ヘッドパターンの極値レベル、または小さなくぼみで設定されます。またツールのスプレッドを考慮する必要があります。

戦略1

このアプローチの欠点は、急激な価格の動きでバーがネックラインから遠い場所で閉じることがあるという事実です。これにより潜在的な利益が失われ、特定のパターンに対する取引損失のリスクが高まる可能性があります。

このようなシナリオの変形として、ネックラインからのある一定の距離の価格で、出現傾向に向かって価格を突破した後に、取引開始することを考慮することができます。

2.2. シナリオ 2

このパターンの処理の2つ目の方法は、突破後にネックラインへ引き戻す価格のポジションを開くことです。このシナリオでは、ストップロスが最後の極値に設定されているため、ストップロスが短くなり、同じリスクでより大きなトランザクションを開くことができます。一般的に、潜在的利益と潜在的損失の比率が増加します。

シナリオ 2

『ダブルトップ/ボトム』パターンの場合のように、突破後にネックラインに価格が戻ってくる現象は常に現れるわけではありません。この事実は、パターンのかなりの部分の省略につながります。したがって、このシナリオを使用すると、パターンの一部が欠落し、市場から出るまでの時間が長くなります。

両方のシナリオを使用する場合、最小テイクプロフィットは、ヘッドからネックラインまでの価格の動きと等しい、ネックラインからの距離の位置に設定することをお勧めします。

テイクプロフィット


3.ストラテジーテスト用のエキスパートアドバイザーを作成しましょう

『ダブルトップ/ボトム』および『ヘッド・ショルダー』パターンを使用して取引するとき、洞察力のある読者はアプローチの特徴に気づくかもしれません。前の記事のアルゴリズムを新しいアドバイザーを構築するための基礎として使用して、この状況を使用します。

準備作業のほとんどは既に記事内の『1』で行いました。パターンを検索するには、CHS_Patternクラスを作成します。そして、前の記事で開発したものを利用するために、これをCPatternクラスの後継者にします。このようにすることで、親クラスのすべてのメソッドにアクセスできます。ここでは、足りない要素だけを追加し、クラスの初期化、パターン検索、およびエントリポイントのメソッドを書き換えます。

class CHS_Pattern : public CPattern
  {
protected:
   s_Extremum     s_HeadExtremum;         //トップの極値点
   s_Extremum     s_StartRShoulder;       //右肩の開始点

public:
                     CHS_Pattern();
                    ~CHS_Pattern();
// --- クラスを初期化する
   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);
//---
   s_Extremum        HeadExtremum(void)      const {  return s_HeadExtremum;     }
   s_Extremum        StartRShoulder(void)    const {  return s_StartRShoulder;   }
  };

クラス初期化メソッドでは、まず親クラスを初期化してから、追加した構造体を準備します。

bool CHS_Pattern::Create(CTrends *trends,double min_correction,double max_correction)
  {
   if(!CPattern::Create(trends,min_correction,max_correction))
      return false;
//---
   s_HeadExtremum.Clear();
   s_StartRShoulder.Clear();
//---
   return true;
  }

パターン検索法では、まずパターンを決定するのに十分な数の極値をチェックします。

bool CHS_Pattern::Search(datetime start_time)
  {
   if(CheckPointer(C_Trends)==POINTER_INVALID || C_Trends.Total()<6)
      return false;

次に、与えられたパターン検索の開始日に対応する極値の序数を決定します。指定された日付以降に極値が形成されなかった場合は、falseの結果を返してメソッドを終了します。

   int start=C_Trends.ExtremumByTime(start_time);
   if(start<0)
      return false;

次にサイクルの最後の6つの極値に関するデータを読み込み、希望のパターンに準拠しているかどうか価格の動きを確認します。パターンを見つけたら、trueの結果でメソッドを終了します。パターンが見つからない場合は、現在の方向に1つのパターンを移動してサイクルを繰り返します。すべての極値を検索してもパターンが見つからない場合は、falseの結果を返してメソッドを終了します 。

   b_found=false; 
   for(int i=start;i>=0;i--)
     {
      if((i+5)>=C_Trends.Total())
         continue;
      if(!C_Trends.Extremum(s_StartTrend,i+5) || !C_Trends.Extremum(s_StartCorrection,i+4) ||
         !C_Trends.Extremum(s_EndCorrection,i+3)|| !C_Trends.Extremum(s_HeadExtremum,i+2) ||
         !C_Trends.Extremum(s_StartRShoulder,i+1) || !C_Trends.Extremum(s_EndTrend,i))
         continue;
//---
      double trend=MathAbs(s_StartCorrection.Price-s_StartTrend.Price);
      double correction=MathAbs(s_StartCorrection.Price-s_EndCorrection.Price);
      double header=MathAbs(s_HeadExtremum.Price-s_EndCorrection.Price);
      double revers=MathAbs(s_HeadExtremum.Price-s_StartRShoulder.Price);
      double r_shoulder=MathAbs(s_EndTrend.Price-s_StartRShoulder.Price);
      if((correction/trend)<d_MinCorrection || header>(trend-correction)   ||
         (1-fmin(header,revers)/fmax(header,revers))>=d_MaxCorrection      ||
         (1-r_shoulder/revers)<d_MinCorrection || (1-correction/header)<d_MinCorrection)
         continue;
      b_found= true; 
//---
      break;
     }
//---
   return b_found;
  }

次のステップでは、エントリポイントの検索方法を書きます。そしてメソッドの最初で、パターンが分析されたクラスインスタンスで以前に検出されたかどうかをチェックします。パターンが見つからない場合は、falseの結果とともにメソッドを終了します。

bool CHS_Pattern::CheckSignal(int &signal, double &sl, double &tp1, double &tp2)
  {
   if(!b_found)
      return false;

次に、パターンの形成後に何本のバーが形成されたかを確認します。パターンが形成されたばかりの場合は、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;
//---
   signal=0;
   sl=tp1=tp2=-1;
   bool up_trend=C_Trends.IsHigh(s_EndTrend);
   int shift1=iBarShift(symbol,e_ConfirmationTF,s_EndCorrection.TimeStartBar,true);
   int shift2=iBarShift(symbol,e_ConfirmationTF,s_StartRShoulder.TimeStartBar,true);
   if(shift1<=0 || shift2<=0)
      return false;
   double koef=(s_StartRShoulder.Price-s_EndCorrection.Price)/(shift1-shift2);
   bool break_neck=false;

さらにこのサイクルでは、その後の価格調整でネックラインを突破することを目指しています。ネックラインが突破されると、潜在的なテイクプロフィットレベルが決定します。そしてさらに、ネックラインへの価格引き下げを求めて、価格が潜在的なテイクプロフィットのレベルに達しなかったかどうかをモニタリングします。価格がネックラインへのロールバックの前に潜在的なテイクプロフィットの利益レベルに達すると、パターンは無効と見なされ、falseの結果とともにメソッドを終了します 。エントリーポイントが検出されると、ストップロスレベルが決定され、trueの結果とともにメソッドを終了します 。エントリーポイントが分析されたタイムフレームの最後の2つのバーよりも早く形成されていない場合は、有効であると見なされます。それ以外の場合はfalseの結果とともにメソッドを終了します。

   for(int i=0;i<total;i++)
     {
      if(up_trend)
        {
         if((tp1>0 && rates[i].low<=tp1) || rates[i].high>s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close>neck)
               continue;
            break_neck=true;
            tp1=neck-(s_HeadExtremum.Price-neck)*0.9;
            tp2=neck-(neck-s_StartTrend.Price)*0.9;
            tp1=fmax(tp1,tp2);
            continue;
           }
         if(rates[i].high>neck)
           {
            if(sl==-1)
               sl=rates[i].high;
            else
               sl=fmax(sl,rates[i].high);
           }
         if(rates[i].close>neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=-1;
         break;
        }
      else
        {
         if((tp1>0 && rates[i].high>=tp1) || rates[i].low<s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close<neck)
               continue;
            break_neck=true;
            tp1=neck+(neck-s_HeadExtremum.Price)*0.9;
            tp2=neck+(s_StartTrend.Price-neck)*0.9;
            tp1=fmin(tp1,tp2);
            continue;
           }
         if(rates[i].low<neck)
           {
            if(sl==-1)
               sl=rates[i].low;
            else
               sl=fmin(sl,rates[i].low);
           }
         if(rates[i].close<neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=1;
         break;
        }
     }   
//---
   return true;
  }

すべてのメソッドと関数の完全なコードは添付ファイルをご覧ください。

ストラテジーテストのためのアドバイザのコードは、記事『1』から引用され基本的には変更をしていません。パターンを操作するための置換クラスの一部にのみ変更が加えられました。エキスパートアドバイザーの完全なコードは、添付ファイルをご覧ください。

その動作の結果に基づいて、2018年の10ヶ月間のエキスパートアドバイザーの動作がテストされました。テストパラメータは、以下のスクリーンショットに表示されています。

テストパラメータ テストパラメータ

テストの結果によると、アドバイザは分析された時間間隔で利益を生み出す能力を実証しました。57%以上の取引が利益で終了し、利益率は2.17でした。しかし、10か月の間の取引数はわずか14回でした。

テスト結果 テスト結果


4.1つのアドバイザーに2つのパターンを組み合わせましょう

2つの記事で行われた作業の結果として、グラフィックパターンの反転に取り組む2つの有益なアドバイザーを獲得しました。そして、トレードの全体的な効率を上げるために、私は両方の戦略を一つのトレーディングプログラムに結合したいと思いました。前のクラスの後継者がパターンを検索するための新しいクラスを作成したのは、決して無意味ではないことをまず申し上げてきます。このテクニックは、戦略を単一のアドバイザーにまとめることに関する我々の仕事を大いに促進するでしょう。

まず、クラスを特定します。基本のCObjectクラスには仮想Typeメソッドがあります。このメソッドをクラスの説明に追加します。CPatternクラスでは、このメソッドは101の値を返し、CHS_Patternクラスでは102を返します。現時点では2つのパターンしか使用していないため、数値定数に制限することができます。パターン数を増やす場合は、列挙を使用することをお勧めします。これにより、コードの可読性が向上します。

その後、クラスを比較する方法を、それらの型を比較するブロックで補完します。その結果、メソッドコードは次の形式になります。

int CPattern::Compare(const CPattern *node,const int mode=0) const
  {
   if(Type()>node.Type())
      return -1;
   else
      if(Type()<node.Type())
         return 1;
//---
   if(s_StartTrend.TimeStartBar>node.StartTrend().TimeStartBar)
      return -1;
   else
      if(s_StartTrend.TimeStartBar<node.StartTrend().TimeStartBar)
         return 1;
//---
   if(s_StartCorrection.TimeStartBar>node.StartCorrection().TimeStartBar)
      return -1;
   else
      if(s_StartCorrection.TimeStartBar<node.StartCorrection().TimeStartBar)
         return 1;
//---
   if(s_EndCorrection.TimeStartBar>node.EndCorrection().TimeStartBar)
      return -1;
   else
      if(s_EndCorrection.TimeStartBar<node.EndCorrection().TimeStartBar)
         return 1;
//---
   return 0;
  }

これでクラスコードの変更は完了です。アドバイザーのコードの仕上げについて見ていきましょう。ここでも、いくつかの追加があります。まず、受け取ったパラメータに応じて、対応するクラスの新しいインスタンスを作成する関数を作成します。この関数のコードはとても単純で、switchを使うことだけで構成されていて、これは必要なクラスを作成するメソッドを呼び出します。

CPattern *NewClass(int type)
  {
   switch(type)
     {
      case 0:
        return new CPattern();
        break;
      case 1:
        return new CHS_Pattern();
        break;
     }
//---
   return NULL;
  }

OnTick関数に以下の変更を加えます。この場合、変更は新しいパターンの検索単位にのみ影響します。すでに見つかったパターンの開始位置のポイントを見つけるためのブロックは、クラス継承による変更を必要としません。

ここでは、最初に1つのタイプの、そして次に別のタイプのと、履歴の中のパターンを一貫して検索するというサイクルを編成します。これを行うには、クラスの新しいインスタンスを作成する時点で、呼び出しを追加したばかりの関数に置き換えます。パラメータで、現在のサイクルパスの数を渡します。アドバイザーのアルゴリズムそのものは変更されません。

void OnTick()
  {
//---
.........................
.........................
.........................
//---
   for(int pat=0;pat<2;pat++)
     {
      Pattern=NewClass(pat);
      if(CheckPointer(Pattern)==POINTER_INVALID)
         return;
      if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
        {
         delete Pattern;
         continue;
        }
//---
      datetime ss=start_search;
      while(!IsStopped() && Pattern.Search(ss))
        {
         ss=fmax(ss,Pattern.EndTrendTime()+PeriodSeconds(e_TimeFrame));
         bool found=false;
         for(int i=2;i<ar_Objects.Total();i++)
           {
            CPattern *temp=ar_Objects.At(i);
            if(Pattern.Compare(temp,0)==0)
              {
               found=true;
               break;
              }
           }
         if(found)
            continue;
         if(!CheckPattern(Pattern))
            continue;
         if(!ar_Objects.Add(Pattern))
            continue;
         Pattern=NewClass(pat);
         if(CheckPointer(Pattern)==POINTER_INVALID)
            break;
         if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
           {
            delete Pattern;
            break;
           }
        }
      if(CheckPointer(Pattern)!=POINTER_INVALID)
         delete Pattern;
     }
//---
   return;
  }

これでエキスパートアドバイザーのコードの変更は完了です。新しいアドバイザーの完全なコードは、添付ファイルのTwoPatterns.mq5ファイルをご覧ください。

必要な変更を加えた後、同じパラメータでテストを行います。

テストパラメータ テストパラメータ

テスト結果は、戦略が互いにどのように補完するかを示しています。テスト期間中、アドバイザーは128回の取引を行い、そのうちの60%以上は利益をもって終了しました。その結果、アドバイザーの動作の収益率は1.94、回収率は3.85でした。完全なテスト結果は以下のスクリーンショットに表示されています。

テスト結果テスト結果

5. 取引システムのテスト

私たちの取引システムの安定性をテストするために、アドバイザーはテストパラメータを変更することなく6つの主要な商品でテストされました。

通貨ペア利益利益率
EURUSD743.571.94
EURJPY125.131.47
GBPJPY
33.931.04
EURGBP-191.7
0.82
GBPUSD-371.050.60
USDJPY
-657.380.31

表に示した結果からわかるように、アドバイザーはテストした通貨ペアの半分で利益を上げ、残りの通貨ペアでは損失を出しました。この事実は、各通貨ペアの価格チャートが個別であることを再度確認しており、アドバイザーを使用する前に特定の条件に合わせて最適化する必要があります。たとえば、 "Stop Loss Backstep"パラメータを最適化すると、4つの通貨ペアで利益を上げ、2つの通貨ペアで損失を減らすことができます。それは一般的に各通貨ペアで収益性を高めました。結果を以下の表に示します。

通貨ペア利益利益率Stop Loss Backstep
EURUSD1020.281.78350
EURJPY532.541.52400
GBPJPY208.691.17300
EURGBP91.451.05450
GBPUSD-315.870.55100
USDJPY-453.080.33100


6. 不採算通貨ペアの取引シグナルを活用しましょう

前の章で私たちのアドバイザーをテストした結果によると、2つの通貨ペアがダメで、良い結果をもたらすことはできませんでした。それはGBPUSDとUSDJPYでした。それはこれらの通貨ペアのトレンド反転のための考慮されたパターンの不十分な強度を示しているのかもしれません。バランスを下げる方向への頑固な動きは、シグナルが到着したときに取引を逆転させるという考えにつながります。

リバーストランザクションに設定する目標は何か?またストップロスをどこに設定するか?これらの質問に答えるために、以前のテストの結果として得られたものを検討します。テストによると、始値の取引がストップロスで決済されるのが、価格が先取り利益の水準に達するよりも多いことがわかりました。したがって、取引を拡大すると、ストップロスとテイクプロフィット1を交換することができます。

不採算取引のチャートを詳細に検証したところ、偽のシグナルの価格帯でチャネルが形成されることが多いことがわかりました。そして古典的なグラフィカル分析では、チャンネルはトレンドの継続の形です。したがって、前の動きと釣り合った価格の動きが期待できます。私たちのパターン検索クラスのCheckSignalメソッドのコードを参照すると、先の動きの大きさはテイクプロフィット2によって固定されていることがわかります。そして私達が必要とするのは別の方向を向いた、全く同じ現在の価格からの距離でテイクプロフィット2を設定することです。

この方法では、パターン検索クラスのコードを変更せずに、トランザクションをデプロイしてアドバイザのコードだけを変更することができます。

この機能を実装するために、リバーストレーディング機能を有効/無効にするフラグとして機能するもう1つのパラメータReverseTradeをアドバイザーに追加します。

input bool  ReverseTrade   =  true; //Reverse Deals

ただし、パラメータを1つ追加しても、取引を開始するロジックは変わりません。CheckPattern関数を変更しましょう。ローカル変数を宣言するブロックにtemp変数を追加します。これは、ストップロスとテイクプロフィット1の交換時に一時的な保管場所として使用されます。

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;
   double temp=0;
//---

次にswitch本体内の、ReverseTradeフラグのステータスのチェックを追加します。フラグがfalseの状態に設定されている場合、私たちは古いロジックを使います。リバーストレーディングを使用する場合、ストップロスとテイクプロフィット1の値を変更します。次に、テイクプロフィット値を通貨ペアのポイントで再計算し、取得した値をCLimitTakeProfitクラスに渡します。すべての反復が正常に完了した後、受信シグナルとは反対の取引を開始します。

//---
   switch(signal)
     {
      case 1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           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;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           temp=tp1;
           tp1=sl-i_SL*_Point;
           sl=temp;
           tp1=(price-tp1)/_Point;
           tp2=(tp2-price)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Sell(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
      case -1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           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;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           temp=tp1;
           tp1=sl+i_SL*_Point;
           sl=temp;
           tp1=(tp1-price)/_Point;
           tp2=(price-tp2)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1 ? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Buy(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
     }
//---
   return true;
  }

これでアドバイザーのコードが完成しました。すべてのクラスと関数の完全なコードは添付ファイルをご覧ください。

リバーストレーディングの機能をテストしましょう。同じ時間間隔と時間枠を使用してGBPUSDの通貨ペアで最初のテストを実施します。アドバイザーのパラメータは、以下のスクリーンショットに表示されています。

GBPUSD反転テストGBPUSD反転テスト

テスト結果は、このソリューションの成功を示しました。テストした時間間隔で、リバーストレーディング機能を有効にしたエキスパートアドバイザーは利益を示しました。127件の取引のうち95件(75.59%)が黒字となりました。利益率は2.35でした。完全なテスト結果は以下のスクリーンショットに表示されています。

GBPUSD反転テスト結果GBPUSD反転テスト結果

得られた結果をまとめるために、USDJPYの通貨ペアで同様のテストを行います。テストパラメータはスクリーンショットに表示されています。

USDJPY反転テストUSDJPY反転テスト

反転機能の2回目のテストも成功しました。108件の取引のうち、66件(61.11%)が黒字で終了し、利益率は2.45でした。完全なテスト結果はスクリーンショットに表示されています。

USDJPY反転テスト結果USDJPY反転テスト結果

テストによると、ポジションの逆転により、損失を生み出す戦略も有益になる可能性があることを示しました。しかし、ある通貨ペアに対して常に収益性の高い戦略が、別の通貨ペアにも利益をもたらすわけではないことを忘れないでください。


まとめ

この記事で実行された作業は、グラフィカル分析の古典的パターンの使用に基づく戦略の実行可能性を示しています。その結果、私たちはさまざまな通貨ペアで長期間にわたって利益を生み出すことができるアドバイザーを獲得しました。ただし、取引戦略を使用するときは、特定の条件で収益性の高い戦略が他の条件では不利になることが多いため、通貨ペアの詳細を考慮する必要があります。時々取り引きを逆転させることは状況を正すのに有効です。しかし、ここであなたは取引条件の特徴を考慮に入れるべきです。

当然のことながら、これらは有益な取引システムを構築するための最初のステップにすぎません。そして、他のパラメータによるアドバイザーの最適化、およびアドバイザーの機能の追加(ストップロスの損益分岐への変換、トレーリングストップなど)も可能です。もちろん、明らかに収益のない通貨ペアでのアドバイザーの使用はお勧めしません。

私の経験が読者にとって取引戦略を構築するのに役立つことを願っています。

もちろん、この記事で紹介されているアドバイザは戦略のデモンストレーションとしての役割を果たすだけであり、実際の市場で使用するためにはさらに仕上げる必要があります。

リンク

  1. ピボット・パターン:『ダブルトップ・ダブルボトム』パターンのテスト
  2. アドバイザーの元のコードを変更することなく、リミットオーダーの形でテイクプロフィットを実現する

記事で使用されているプログラム:

# 名前 タイプ 説明
1 ZigZag.mqh クラスライブラリ ジグザグインジケータのクラス
2 Trends.mqh クラスライブラリ トレンド検索クラス
3 Pattern.mqh クラスライブラリ ダブルトップ/ボトムパターンを使用するクラス
4 HS_Pattern クラスライブラリ ヘッドアンドショルダーのパターンを使用するクラス
5 LimitTakeProfit.mqh クラスライブラリ テイクプロフィットオーダーを置き換えるクラス
6 Header.mqh ライブラリ アドバイザーのヘッダーファイル
7 Head-Shoulders.mq5 エキスパートアドバイザ ヘッドアンドショルダーの戦略に基づくアドバイザー
8 TwoPatterns.mq5 エキスパートアドバイザ 2つのパターンの開発をするアドバイザー