MQL5クックブック - 移動チャンネルのシグナルトレーディング

Denis Kirichenko | 21 9月, 2016


イントロダクション

前の記事MQL5クックブック - 移動チャンネルのプログラミングは、頻繁にチャンネルを移動するプロットのメソッドについて説明しました。«等距離チャンネル»ツールを解決するために、 OOP関数が使用されていました。

この記事では、これらのチャネルを使用することにより、シグナルに焦点を当てます。これらのシグナルに基づいて、取引戦略を作成してみましょう。

モジュールへの呼び出しをして、売買シグナルの発生について公開された複数の記事があります。標準ライブラリうまくいけば、標準クラス
の範囲でユーザーの幅を広げることができます。

この戦略に慣れるため、単純なものから複雑なものまで学ぶことが推奨されます。まず、基本的な戦略を作成し、それを複雑にします。

1. 等距離チャネルインジケータ

移動チャネルの前回の記事では、グラフィカルオブジェクトを作成することにより、チャネル自体をプロットしました。一方で、このアプローチは、プログラマのタスクを容易にするが、一方で、不可能なものもあります。例えば、最適化モードではチャートは存在しません。チャート上の任意のグラフィカルオブジェクトを検出することはできません。テスト中の制限に依存:

テストでのグラフィカルオブジェクト

テスト/最適化のグラフィカル・オブジェクトがプロットされません。テスト/最適化の際に作成されたオブジェクトのプロパティを参照するとき、EAはゼロを受け取ることになります。

この制限は、ビジュアルモードでのテストには適用されません。

そのため、フラクタルと実際のチャネルの両方を反映したインジケーターを作成します。

このインジケータはEquidistantChannelsです。2つのブロックで構成されています。チャネル・バッファ - 最初のフラクタルのバッファ、および秒計算。

Calculateイベントハンドラのコードです。

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---前回のコールでない棒がなかった場合
   if(prev_calculated==0)
     {
      //---バッファをゼロ
      ArrayInitialize(gUpFractalsBuffer,0.);
      ArrayInitialize(gDnFractalsBuffer,0.);
      ArrayInitialize(gUpperBuffer,0.);
      ArrayInitialize(gLowerBuffer,0.);
      ArrayInitialize(gNewChannelBuffer,0.);
     }
//---フラクタルの計算[スタート]
   int startBar,lastBar;
//---
   if(rates_total<gMinRequiredBars)
     {
      Print("Not enough data for calculation");
      return 0;
     }
//---
   if(prev_calculated<gMinRequiredBars)
      startBar=gLeftSide;
   else
      startBar=rates_total-gMinRequiredBars;
//---
   lastBar=rates_total-gRightSide;
   for(int bar_idx=startBar; bar_idx<lastBar && !IsStopped(); bar_idx++)
     {
      //---
      if(isUpFractal(bar_idx,gMaxSide,high))
         gUpFractalsBuffer[bar_idx]=high[bar_idx];
      else
         gUpFractalsBuffer[bar_idx]=0.0;
      //---
      if(isDnFractal(bar_idx,gMaxSide,low))
         gDnFractalsBuffer[bar_idx]=low[bar_idx];
      else
         gDnFractalsBuffer[bar_idx]=0.0;
     }
//---フラクタルの計算[終了]

//---チャネルの境界線用の計算[スタート]
   if(prev_calculated>0)
     {
      //---セットが初期化されていない場合
      if(!gFracSet.IsInit())
         if(!gFracSet.Init(
            InpPrevFracNum,
            InpBarsBeside,
            InpBarsBetween,
            InpRelevantPoint,
            InpLineWidth,
            InpToLog
            ))
           {
            Print("Fractal set initialization error!");
            return 0;
           }
      //---計算
      gFracSet.Calculate(gUpFractalsBuffer,gDnFractalsBuffer,time,
                         gUpperBuffer,gLowerBuffer,
                         gNewChannelBuffer
                         );
     }
//---チャネルの境界線用の計算[終了]

//---次の呼び出しのためのprev_calculatedの戻り値
   return rates_total;
  }

フラクタルバッファ値の計算とブロックが黄色で強調表示され、チャネル・バッファの計算とブロックは - になります。。なお、第2ブロックのハンドラの次の呼び出しでは、アクティブ化されることに気づくでしょう。第2のブロックのこの実装は、フラクタルのバッファを取得することができます。

さて、フラクタルの集合に関する CFractalSetオブジェクト。チャンネル表示メソッドの変更に、 CFractalSetクラスを変更することが必要でした。鍵メソッドは、CFractalSet::Calculateで、インジケータのチャネルバッファを計算します。コードはCFractalPoint.mqhファイルで提供されています。



等距離チャネルからのシグナルのプロバイダ - これで基礎になります。インジケータの動作は、ビデオに表示されます。


2. Basic strategy

OOPで改訂することができる簡単なものから始めましょう。いくつかの基本的な戦略です。

この戦略は、かなり単純な取引ルールです。相場のエントリは、チャネルの境界によって行われます。価格は下に触れると買い、上に触れたときに売り。図1は、下に触れて、買ったことを示しています。トレードのレベル(ストップロス、テイクプロフィット)は、固定サイズで自動的に配置しました。ポジションが開かれている場合、エントリシグナルは無視されます。

図1の入力シグナル

Fig.1Entry signal


また、標準ライブラリは、かなり進化しています。すでに使用することができ、多くの既製のクラスがあります。まず、シグナルクラス CExpertSignalに«接続»してみましょう。ドキュメントによれば、基底クラスの取引シグナルを作成するため
です

このクラスは、かなり正確です。これは CTradeSignalではありません。CSignalでもありません。EAコードで使用するように設計されたシグナル CExpertSignalです。

その内容にこだわる必要はありません。この記事では、«MQL5ウィザード:取引シグナルのモジュールを作成するメソッド»の詳細な説明があります。


2.1 CSignalEquidChannel シグナルクラス

導出されたシグナルのクラスは、次のとおりです。

//+------------------------------------------------------------------+
//| Class CSignalEquidChannel                                        |
//|目的:等距離売買シグナルのクラス|
//|チャンネル。 |
//|CExpertSignalクラスから派生。 |
//+------------------------------------------------------------------+
class CSignalEquidChannel : public CExpertSignal
  {
protected:
   CiCustom          m_equi_chs;          // インジケータオブジェクト EquidistantChannels 
   //---調整可能なパラメータ
   int               m_prev_frac_num;     // 前のフラクタル
   bool              m_to_plot_fracs;     //表示フラクタル?
   int               m_bars_beside;       //フラクタルの左/右
   int               m_bars_between;      // 中間バー//
   ENUM_RELEVANT_EXTREMUM m_relevant_pnt; //関連するポイント
   int               m_line_width;        //線幅
   bool              m_to_log;            //ログを保存//?
   double            m_pnt_in;            //内部、ピップ
   double            m_pnt_out;           //外部ピップ
   bool              m_on_start;          //起動時にシグナルフラグ
   //--- 計算後
   double            m_base_low_price;    // ベースの低価格
   double            m_base_high_price;   //高値ベース
   double            m_upper_zone[2];     //アッパーゾーン:[0]-internal、[1]-external
   double            m_lower_zone[2];     //下のゾーン
   datetime          m_last_ch_time;      //最後のチャンネルの発生時刻
   //---相場モデルの「重み」(0-100)
   int               m_pattern_0;         //"チャネルの下に触れる - 、買い - 上に触れる:売り」

   //--- === Methods === --- 
public:
   //---コンストラクタ/デストラクタ
   void              CSignalEquidChannel(void);
   void             ~CSignalEquidChannel(void){};
   //---調整可能なパラメータを設定するメソッド
   void              PrevFracNum(int _prev_frac_num)   {m_prev_frac_num=_prev_frac_num;}
   void              ToPlotFracs(bool _to_plot)        {m_to_plot_fracs=_to_plot;}
   void              BarsBeside(int _bars_beside)      {m_bars_beside=_bars_beside;}
   void              BarsBetween(int _bars_between)    {m_bars_between=_bars_between;}
   void              RelevantPoint(ENUM_RELEVANT_EXTREMUM _pnt) {m_relevant_pnt=_pnt;}
   void              LineWidth(int _line_wid)          {m_line_width=_line_wid;}
   void              ToLog(bool _to_log)               {m_to_log=_to_log;}
   void              PointsOutside(double _out_pnt)    {m_pnt_out=_out_pnt;}
   void              PointsInside(double _in_pnt)      {m_pnt_in=_in_pnt;}
   void              SignalOnStart(bool _on_start)     {m_on_start=_on_start;}
   //---相場モデルの「重み」を調整するメソッド
   void              Pattern_0(int _val) {m_pattern_0=_val;}
   //---設定の検証メソッド
   virtual bool      ValidationSettings(void);
   //---インジケータと時系列を作成するメソッド
   virtual bool      InitIndicators(CIndicators *indicators);
   //---メソッド相場モデルが生成される場合
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);
   virtual double    Direction(void);
   //---
protected:
   //---インジケーターの初期化のメソッド
   bool              InitCustomIndicator(CIndicators *indicators);
   // - チャネルの上の値を取得
   double            Upper(int ind) {return(m_equi_chs.GetData(2,ind));}
   // - チャネルの下の値を取得
   double            Lower(int ind) {return(m_equi_chs.GetData(3,ind));}
   // - チャンネル発生のフラグを取得します
   double            NewChannel(int ind) {return(m_equi_chs.GetData(4,ind));}
  };
//+------------------------------------------------------------------+

いくつかのニュアンスに注意してください。

このクラスの主シグナルは、等間隔チャネルのセットアップがあります。そしてそれは、現在のバージョンでは唯一のものです。今のところ、他のものが存在しません。このクラスclassが含まれて,カスタム型の技術的なインジケーターを操作するため - CiCustom

基本モデルは、シグナルモデルとして使用されます。:「チャネルの下に触れる - 買い、上 - 売り」。ピンポイントの精度で触れることがあるので、最も可能性の高いイベントは、調整可能な境界線を持つ特定のバッファを使用します。外部パラメータm_pnt_outは、チャネルを越えて行くことを許されており、内部パラメータm_pnt_inが離れてから許可されているボーダーです。ロジックは非常に簡単です。価格が非常に近い場合、チャネルの境界線に触れたか、超えていることを前提としています。図2は、概略的にバッファを示します。価格を入力すると、境界線との価格は、モデルをトリガします。

図2は、基本的なシグナルモデルのトリガ

図2基本的なシグナルモデルのトリガ


m_upper_zone[2]パラメータ配列は、上部バッファーの境界を概説し、m_lower_zone[2]は下を描きます。

例では$1,11552のレベルがチャンネル(赤線)の上側の境界として関数します。$1,11452は、バッファの下を担当し、$1,11702は上です。したがって、外部許容誤差の大きさは150ポイントであり、内部は100ポイントです。価格は青い曲線で表示されます。

m_on_startパラメータは、チャート上で実行するとき、既に描画されていた第1チャンネルの兆候を無視することができます。フラグがリセットされると、ロボットは次のチャンネルで動作し、現在の売買シグナルを処理しません。

m_base_low_price m_base_high_priceパラメータの実際のバーの最高値と最安値の値を格納します。取引が唯一の新しいバーの外観で許可された場合、または以前のバー上で実行される場合は、ゼロバーがあると考えられています。

さて、メソッドについていくつか言及します。メソッドの約半分が仮想であるため、開発者は、十分に広い自由度を提供することに留意すべきです。これは下位クラスの動作は、必要に応じて実施できることを意味します。

定量的に潜在的な取引方向をDirection()メソッドで始めましょう:

//+------------------------------------------------------------------+
//||「加重」方向を決定
//+------------------------------------------------------------------+
double CSignalEquidChannel::Direction(void)
  {
   double result=0.;
//---新しいチャネルの外観
   datetime last_bar_time=this.Time(0);
   bool is_new_channel=(this.NewChannel(0)>0.);
//---第1チャンネルのシグナルを無視している場合
   if(!m_on_start)
      //---最初のチャネルは、通常、初期化時に表示された場合
      if(m_prev_frac_num==3)
        {
         static datetime last_ch_time=0;
         //---新しいチャネルがあらわれた場合
         if(is_new_channel)
           {
            last_ch_time=last_bar_time;
            //---最初がある場合
            if(m_last_ch_time==0)
               //---最初のチャネルが登場しているバーの時間を保存
               m_last_ch_time=last_ch_time;
           }
         //---時間が一致した場合
         if(m_last_ch_time==last_ch_time)
            return 0.;
         else
         //---フラグをクリア
            m_on_start=true;
        }
//---実際のバーのインデックス
   int actual_bar_idx=this.StartIndex();
//---境界線を設定
   double upper_vals[2],lower_vals[2]; // [0]-bar preceding the actual, [1]-actual bar
   ArrayInitialize(upper_vals,0.);
   ArrayInitialize(lower_vals,0.);
   for(int idx=ArraySize(upper_vals)-1,jdx=0;idx>=0;idx--,jdx++)
     {
      upper_vals[jdx]=this.Upper(actual_bar_idx+idx);
      lower_vals[jdx]=this.Lower(actual_bar_idx+idx);
      if((upper_vals[jdx]==0.) || (lower_vals[jdx]==0.))
         return 0.;
     }
//---価格を得ます
   double curr_high_pr,curr_low_pr;
   curr_high_pr=this.High(actual_bar_idx);
   curr_low_pr=this.Low(actual_bar_idx);
//---価格が得られた場合
   if(curr_high_pr!=EMPTY_VALUE)
      if(curr_low_pr!=EMPTY_VALUE)
        {
         //---価格を保存
         m_base_low_price=curr_low_pr;
         m_base_high_price=curr_high_pr;
         //---価格を定義します。
         //---アッパーゾーン:[0]-internal、[1]-external 
         this.m_upper_zone[0]=upper_vals[1]-m_pnt_in;
         this.m_upper_zone[1]=upper_vals[1]+m_pnt_out;
         //---低いゾーン:[0]-internal、[1]-external 
         this.m_lower_zone[0]=lower_vals[1]+m_pnt_in;
         this.m_lower_zone[1]=lower_vals[1]-m_pnt_out;
         //---正規化
         for(int jdx=0;jdx<ArraySize(m_lower_zone);jdx++)
           {
            this.m_lower_zone[jdx]=m_symbol.NormalizePrice(m_lower_zone[jdx]);
            this.m_upper_zone[jdx]=m_symbol.NormalizePrice(m_upper_zone[jdx]);
           }
         //---ゾーンが収束するかどうかを確認
         if(this.m_upper_zone[0]<=this.m_lower_zone[0])
            return 0.;
         //--- 結果
         result=m_weight*(this.LongCondition()-this.ShortCondition());
        }
//---
   return result;
  }
//+------------------------------------------------------------------+

チャート上の最初のチャンネルを無視する必要があるかどうかをチェックします。

第2のブロックは、現在の価格を取得し、バッファゾーンを決定します。これは、ゾーンの収束のチェックです。チャネルが狭すぎるか、バッファーゾーンが広すぎる場合は、価格は両ゾーンに入る可能性があります。したがって、このような状況を処理する必要があります。

ターゲットラインは、で強調表示されます。ここでは、取引方向の定量的な推定値を取得します。

LongCondition()メソッドを考えてみましょう。

//+------------------------------------------------------------------+
//|買いの条件を確認|
//+------------------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- 安値をセット
   if(m_base_low_price>0.)
      //---安値が下のレベルであれば
      if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
        {
         if(IS_PATTERN_USAGE(0))
            result=m_pattern_0;
        }
//---
   return result;
  }
//+------------------------------------------------------------------+

価格が下のバッファゾーンに入った場合、確認してください。それがなかった場合は、相場モデルを活性化するための許可をチェックしてください。タイプの構造についての詳細は、「IS_PATTERN_USAGE(k)」の記事に記載されています«トレーディングシグナルジェネレータベースのカスタムインジケーター»

ShortCondition()メソッドは、上記と同様に動作します。焦点は、上部地帯にあります。

//+------------------------------------------------------------------+
//|売りの条件を確認してください|
//+------------------------------------------------------------------+
int CSignalEquidistantChannel::ShortCondition(void)
  {
   int result=0;
//---高値が設定されている場合
   if(m_base_high_price>0.)
      //---高値が上のレベルであれば
      if((m_base_high_price>=m_upper_zone[0]) && (m_base_high_price<=m_upper_zone[1]))
        {
         if(IS_PATTERN_USAGE(0))
            result=m_pattern_0;       
        }
//---
   return result;
  }
//+------------------------------------------------------------------+

InitCustomIndicator()メソッドを使用して、カスタムインジケータを初期化します。

//+------------------------------------------------------------------+
//|カスタムインジケーターの初期化|
//+------------------------------------------------------------------+
bool CSignalEquidChannel::InitCustomIndicator(CIndicators *indicators)
  {
//---コレクションにオブジェクトを追加
   if(!indicators.Add(GetPointer(m_equi_chs)))
     {
      PrintFormat(__FUNCTION__+": error adding object");
      return false;
     }
//---インジケータ・パラメータを指定します
   MqlParam parameters[8];
   parameters[0].type=TYPE_STRING;
   parameters[0].string_value="EquidistantChannels.ex5";
   parameters[1].type=TYPE_INT;
   parameters[1].integer_value=m_prev_frac_num;   // 1) 前のフラクタル
   parameters[2].type=TYPE_BOOL;
   parameters[2].integer_value=m_to_plot_fracs;   // 2) フラクタルの表示?
   parameters[3].type=TYPE_INT;
   parameters[3].integer_value=m_bars_beside;     // 3) フラクタルの足
   parameters[4].type=TYPE_INT;
   parameters[4].integer_value=m_bars_between;    // 4) 中間の足
   parameters[5].type=TYPE_INT;
   parameters[5].integer_value=m_relevant_pnt;    // 5) れればんとポイント
   parameters[6].type=TYPE_INT;
   parameters[6].integer_value=m_line_width;    // 6) ライン幅
   parameters[7].type=TYPE_BOOL;
   parameters[7].integer_value=m_to_log;          // 7) ログの保存?

//---オブジェクトの初期化
   if(!m_equi_chs.Create(m_symbol.Name(),_Period,IND_CUSTOM,8,parameters))
     {
      PrintFormat(__FUNCTION__+": error initializing object");
      return false;
     }
//---バッファの数
   if(!m_equi_chs.NumBuffers(5))
      return false;
//--- ok
   return true;
  }
//+------------------------------------------------------------------+

パラメータ配列の最初の値が文字列として、インジケーター名でなければなりません。

クラスには、仮想の ValidationSettings()メソッドが含まれています。チェックチャネルインジケータのパラメータが正しく設定されている場合、同様のメソッドを呼び出します。カスタムインジケータに対応するバッファの値を取得するサービス・メソッドもあります。

導出されたシグナルのクラスに関連するすべてのものがあります。

2.2 CEquidChannelExpert トレード戦略クラス

基本的な考え方は CExpert標準クラスから派生クラスを記述します。Processing()メソッド - 実際には、主ハンドラの動作のみを変更する必要があるため、可能な限りコンパクトになります。任意の戦略を付与し、仮想です。

//+------------------------------------------------------------------+
//| Class CEquidChannelExpert.                                       |
//|目的:等距離チャネルに基づいたトレードEAのクラス。 |
//| CExperクラスから派生。|
//+------------------------------------------------------------------+
class CEquidChannelExpert : public CExpert
  {
   //---===データメンバー===--- 
private:


   //--- === Methods === --- 
public:
   //---コンストラクタ/デストラクタ
   void              CEquidChannelExpert(void){};
   void             ~CEquidChannelExpert(void){};

protected:
   virtual bool      Processing(void);
  };
//+------------------------------------------------------------------+

メソッド自体は、次のとおりです。

//+------------------------------------------------------------------+
//|メインモジュール|
//+------------------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//---方向の計算
   m_signal.SetDirection();
//---オープンポジションのチェック
   if(!this.SelectPosition())
     {
      //---ポジションオープニングモジュール
      if(this.CheckOpen())
         return true;
     }
//---何のトレードの操作がない場合
   return false;
  }

非常に簡単です。まず、シグナルオブジェクトは、開いたポジションの存在がチェックされていることをした後、取引方向を推定します。ポジションが存在しない場合、開く機会を探します。ポジションがある場合、そのままです。

基本戦略のコードは BaseChannelsTrader.mq5ファイルに実装されています。




基本戦略の操作例は、ビデオで提示されています。

2013-2015の基本戦略の図3の結果。

図32013年から2015年の基本戦略の結果。

EURUSDのシンボルの時間単位の時間枠での戦略テスターで行いました。基本的な戦略は、「原則を見た」バランス・チャートに気づくことができます:不採算取引が続きました。テストで使用したカスタムパラメータの値がbase_signal.setファイルで提供されています。また、チャネルパラメータが含まれている値が戦略のすべてのバージョンで変更されていません。

「全ティックテストモード」を使用します。

戦略の取引実績を改善するための2つのメソッドがあります。最初は、利益を最大にするパラメータ値の組み合わせを選択し、最適化します。2つ目は、EAのパフォーマンスに影響を与える要因を見つけることです。第一は、取引戦略のロジックを変更に関連付けられていない場合、もう一つは行うことはできません。

次のセクションでは、基本的な戦略が編集され、パフォーマンスの要因が求められるでしょう。

3. パフォーマンス要因

処分についてのいくつかの可能性。1つのプロジェクトフォルダにユニーク戦略のすべてのファイルを配置すると便利かもしれません。基本戦略のこの実装は、ベースサブフォルダ(図4)にポジションしています。

チャネル戦略のプロジェクトフォルダ 図4

チャネル戦略のプロジェクトフォルダ 図4

それぞれの新たな要因は、EAコードを構成するソースファイルへの変更を行うための新たな段階があることを前提としています。

3.1トレーリングを使用しました

開始する前に、戦略にトレーリング関数を追加することが提案されています。オープンポジションを維持することを可能にする CTrailingFixedPipsクラスのオブジェクトとします。ストップロスの価格とテイクプロフィットの両方を追います。テイクプロフィットをトレーリングで無効にするには、対応するパラメータにゼロ値を設定します(InpProfitLevelPips)。<BR3/>

コード内で次のように変更します。

EAのChannelsTrader1.mq5ファイルにカスタムパラメータのグループを追加します。

//---
sinput string Info_trailing="+===-- Trailing --====+"; // +===-- Trailing --====+
input int InpStopLevelPips=30;          // ストップロスのレベル
input int InpProfitLevelPips=50;        // テイクプロフィットのレベル

その上のオブジェクト CTrailingFixedPipsタイプは、後続のパラメータを設定します。

//---トレーリングのオブジェクト
   CTrailingFixedPips *trailing=new CTrailingFixedPips;
   if(trailing==NULL)
     {
      //--- エラー
      printf(__FUNCTION__+": error creating trailing");
      myChannelExpert.Deinit();
      return(INIT_FAILED);
     }
//---トレーリングのオブジェクトを追加します
   if(!myChannelExpert.InitTrailing(trailing))
     {
      //--- エラー
      PrintFormat(__FUNCTION__+": error initializing trailing");
      myChannelExpert.Deinit();
      return INIT_FAILED;
     }
//---トレーリングのパラメータ
   trailing.StopLevel(InpStopLevelPips);
   trailing.ProfitLevel(InpProfitLevelPips);

トレーリングが使用されるので、距離チャンネルExpert1.mqhのCEquidChannelExpert::Processing()メソッドを変更する必要があります。

//+------------------------------------------------------------------+
//|メインモジュール|
//+------------------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//---方向の計算
   m_signal.SetDirection();
//---ポジションが存在しない場合
   if(!this.SelectPosition())
     {
      //---ポジションオープニングモジュール
      if(this.CheckOpen())
         return true;
     }
//---ポジションが存在する場合
   else
     {
      //---ポジションの変更が可能である場合
      if(this.CheckTrailingStop())
         return true;
     }
//---何のトレードの操作がない場合
   return false;
  }

これで大丈夫です。トレーリングが追加されました。戦略のファイルは別々のChannelsTrader1サブフォルダに配置されています。

有効性に影響を持っている場合、確認してみましょう。

最適化モードで複数回実行は、基本的な戦略と同じパラメータ値を使用して、戦略テスターで行われています。ストップロスとテイクプロフィットのパラメータが調整されました:

 Variable StartStep
Stop
Level for StopLoss, pips
0
10
100
Level for TakeProfit, pips
0
10
150

最適化結果は、ReportOptimizer-signal1.xmlファイルに記載されています。最高のパターンが図5で提示されています。ストップロス=0、およびTakeProfit=150。

図5、2013年から2015年のトレーリングを使用した戦略の結果。

図5 トレーリングを使用したトレード結果2013-2015.

最後の数字は、図3に似ています。この値の範囲内の使用は、結果を改善しなかったと言えます。

3.2チャネルタイプ

チャネルタイプは、パフォーマンス結果に影響を与えるという仮定があります。一般的な考えはこうです:下チャネルで売り、上で買いをお勧めします。チャネル(傾斜なし)が平坦である場合、両方の境界に基づいて取引することが可能です。

ENUM_CHANNEL_TYPEは、チャネルタイプを定義します。

//+------------------------------------------------------------------+
//|チャネルタイプ|
//+------------------------------------------------------------------+
enum ENUM_CHANNEL_TYPE
  {
   CHANNEL_TYPE_ASCENDING=0,  //昇順
   CHANNEL_TYPE_DESCENDING=1, //降順
   CHANNEL_TYPE_FLAT=2,       // フラット
  };
//+------------------------------------------------------------------+

チャネル型を検索するための許容範囲パラメータを定義します。 EAのChannelsTrader2.mq5の初期化ブロック。

//---フィルタパラメータ
   filter0.PointsInside(_Point*InpPipsInside);
   filter0.PointsOutside(_Point*InpPipsOutside);
   filter0.TypeTolerance(_Point*InpTypePips);
   filter0.PrevFracNum(InpPrevFracNum);
   ...

このパラメータは、ポイントで価格変化の速度を制御します。7ピップに等しいと仮定します。チャンネルが「成長」する場合、6ピップごとのバーで、昇順とみなすのに十分ではありません。シンプルに(傾斜しない)平坦を考えます。

Direction()メソッドにチャネルタイプの識別を追加します。

//---チャネルが新しい場合
   if(is_new_channel)
     {
      m_ch_type=CHANNEL_TYPE_FLAT;                // フラット(傾いていない)チャネル
      //---タイプの許容値が設定されている場合
      if(m_ch_type_tol!=EMPTY_VALUE)
        {
         //---チャネルタイプ
         //---変化のスピード
         double pr_speed_pnt=m_symbol.NormalizePrice(upper_vals[1]-upper_vals[0]);
         //---速度が十分であれば
         if(MathAbs(pr_speed_pnt)>m_ch_type_tol)
           {
            if(pr_speed_pnt>0.)
               m_ch_type=CHANNEL_TYPE_ASCENDING;  //チャネルを昇順
            else
               m_ch_type=CHANNEL_TYPE_DESCENDING; // チャネル降順
           }
        }
     }

最初に、チャネルがフラットであると考えられます。 toleranceパラメータに設定されていなかったチャネルタイプを識別するために、速度変化を決定しません。

 買いの条件は、チャネルが下降していることのチェックが含まれます。

//+------------------------------------------------------------------+
//|買いの条件を確認|
//+------------------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- 安値をセット
   if(m_base_low_price>0.)
      //---チャネルが下降していない場合
      if(m_ch_type!=CHANNEL_TYPE_DESCENDING)
         //---安値が下のレベルであれば
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
//---
   return result;
  }
//+------------------------------------------------------------------+

チャネルが昇順されていないかどうかを確認するため、売りの条件が行われます。

EquidistantChannelExpert2.mqhファイルはトレーリングが除外されているので、 CEquidChannelExpert::Processing()メソッドは基本バージョンと同じになります。

この因子の有効性を確認してください。一つのパラメータのみ最適化されます。

 Variable StartStep
Stop
Tolerance for type, pips
0
5
150

最適化結果は、ReportOptimizer-signal2.xmlファイルに記載されています。最高のは、図6に提示されています。

図6、2013年から2015年のチャネルタイプを使用した戦略の結果。

図6 チャネルタイプを使用したテスト結果 for 2013-2015.

戦略テストの結果がわずかに優れていることに気づくかと思います。パラメータの指定されたベース値で、チャンネル型のようなフィルタは、最終結果に影響を与える、ということが判明しました。 

3.3チャネル幅

チャネル幅が戦略の種類に影響を与えると思われます。チャネルが狭い場合、その境界線が切断されたとき、その後、ブレイクアウトの方向に向かってではなく、それに対する取引とすることが可能です。ブレイクアウト戦略になります。チャンネルは広い場合、その境界に基づいて取引することが可能です。これはリバウンド戦略です。現在の戦略は何であるか - チャネルの境界に基づいています。

明らかに、チャネルが狭いか広いかどうかを決定するための基準がここで必要とされます。極端にならないように、どちらも考慮して、その間に何かを追加することが提案されています。その結果、2つの基準が必要とされます。

  1. 狭いチャネルの十分な幅。
  2. 広いチャネルの十分な幅。

チャネルがどちらもない場合、相場参入を控えるのが賢明かもしれません。

図7 チャンネル幅

図7 チャンネル幅

チャネル幅を決定する際、幾何学的な問題があることに留意すべきです。軸が異なる値で測定されています。したがって、AB及びCDのセグメントの長さを測定します。しかし、CEセグメント(図7)の計算に問題があります。

最も簡単な手法は、標準化のために選択されています。式は次のとおりです。

length of CE ≃ length of CD / (1.0 + channel speed)

チャネル幅は、ENUM_CHANNEL_WIDTH_TYPEの列挙を使用して測定されます。

//+------------------------------------------------------------------+
//|チャネル幅|
//+------------------------------------------------------------------+
enum ENUM_CHANNEL_WIDTH_TYPE
  {
   CHANNEL_WIDTH_NARROW=0,   //狭い
   CHANNEL_WIDTH_MID=1,      // 平均
   CHANNEL_WIDTH_BROAD=2,    //ワイド
  };

ChannelsTrader3.mq5のソースファイルへの"チャンネル"カスタムパラメータのグループにチャネル幅の基準を追加します。

//---
sinput string Info_channels="+===-- Channels --====+"; // +===-- Channels --====+
input int InpPipsInside=100;            //内部、ピップ
input int InpPipsOutside=150;           //、ピップ
input int InpNarrowPips=250;            //狭チャネル、ピップ
input int InpBroadPips=1200;            //ワイドチャネル、ピップ
...

狭いチャネルの基準で広いチャネルよりも大きな値を有する場合、初期エラーが起こります。

//---フィルタパラメータ
   filter0.PointsInside(_Point*InpPipsInside);
   filter0.PointsOutside(_Point*InpPipsOutside);
   if(InpNarrowPips>=InpBroadPips)
     {
      PrintFormat(__FUNCTION__+": error specifying narrow and broad values");
      return INIT_FAILED;
     }
   filter0.NarrowTolerance(_Point*InpNarrowPips);
   filter0.BroadTolerance(_Point*InpBroadPips);

チャネル幅を決定する間、Direction()メソッドの本体に示されています。

// - - チャネル幅 
   m_ch_width=CHANNEL_WIDTH_MID;               // 平均//
   double ch_width_pnt=((upper_vals[1]-lower_vals[1])/(1.0+pr_speed_pnt));
//---狭チャネル基準が指定されている場合
   if(m_ch_narrow_tol!=EMPTY_VALUE)
      if(ch_width_pnt<=m_ch_narrow_tol)
         m_ch_width=CHANNEL_WIDTH_NARROW;      //狭い
//---広いチャネル基準が指定されている場合
   if(m_ch_narrow_tol!=EMPTY_VALUE)
      if(ch_width_pnt>=m_ch_broad_tol)
         m_ch_width=CHANNEL_WIDTH_BROAD;       //ワイド

最初に、チャネルが平均であると考えられます。狭いか広いかをチェックします。

エントリー方向を決定するためのメソッドを変更することも必要です。買いするための条件は、次のようになります:

//+------------------------------------------------------------------+
//|買いの条件を確認|
//+------------------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
//--- チャネルが狭い場合- 上部の境界線のブレイクアウトで取引
   if(m_ch_width==CHANNEL_WIDTH_NARROW)
     {
      //---高値が設定されている場合
      if(m_base_high_price>0.)
         //---高値が上のレベルであれば
         if(m_base_high_price>=m_upper_zone[1])
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
     }
//---チャネルが広い場合 - 下の境界線でリバウンド取引
   else if(m_ch_width==CHANNEL_WIDTH_BROAD)
     {
      //--- 安値をセット
      if(m_base_low_price>0.)
         //---安値が下のレベルであれば
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
               result=m_pattern_0;
           }
     }
//---
   return result;
  }
//+------------------------------------------------------------------+

このメソッドは、2つのブロックで構成されています。最初は、狭いチャネル内にブレイクアウト取引のチェックをします。現在の変形例では、ブレイクアウトは上部バッファーゾーンの先頭に達する価格であると考えられていることに注意してください。2番目のブロックは、価格がすでにリバウンド戦略の下部バッファゾーンに入ったか確認します。

売りをチェックするメソッド - ShortCondition()は - 同じように作成されます。

CEquidChannelExpert::Processing()メソッドは変わりません。

最適化する2つのパラメータがあります。

 Variable StartStep
Stop
Narrow channel, pips
100
20
250
Wide channel, pips
350
50
1250

最適化の結果は、ReportOptimizer-signal3.xmlファイルに記載されています。最高は、図8に示されています。

図8、2013年から2015年のチャネル幅を考慮した戦略の結果。

図8 チャネル幅を考慮したトレード結果 2013-2015.

これは最も影響のある要因でしょう。資産曲線は、より顕著な方向を有します。

3.4ボーダーラインストップロスとテイクプロフィットを取ります

テイクプロフィットを取る場合は、現在の戦略の条件にこれらのレベルを調整する能力があるはずです。一定の角度でチャット上のダイナミクスを通してメソッドを作るチャネルがあると、テイクプロフィットは、チャネル境界線と一緒に移動する必要があります。

モデルは便宜のために追加されました。次のようになります。

//---相場モデルの「重み」(0-100)
   int               m_pattern_0;         //チャネル境界線からのリバウンド」モデル
   int               m_pattern_1;         // "ブレイクアウトチャネルの境界線の「モデル
   int               m_pattern_2;         //"新しいチャネル」モデル

以前のバージョンでは、チャネルのいずれかの境界線に触れて価格を決定していました。さて、リバウンドとブレイクアウトモデルを区別します。新しいチャネルモデル - 第3のモデルがあります。新しいチャネルがあり、過去のチャネルで開かれたポジションがある場合、例に必要とされます。モデルがトリガされた場合、ポジションが閉じられます。

買いの条件は、次のようになります:

//+------------------------------------------------------------------+
//|買いの条件を確認|
//+------------------------------------------------------------------+
int CSignalEquidChannel::LongCondition(void)
  {
   int result=0;
   bool is_position=PositionSelect(m_symbol.Name());
//--- チャネルが狭い場合- 上部の境界線のブレイクアウトで取引
   if(m_ch_width_type==CHANNEL_WIDTH_NARROW)
     {
      //---高値が設定されている場合
      if(m_base_high_price>0.)
         //---高値が上のレベルであれば
         if(m_base_high_price>=m_upper_zone[1])
           {
            if(IS_PATTERN_USAGE(1))
              {
               result=m_pattern_1;
               //---ポジションが存在しない場合
               if(!is_position)
                  //---ジャーナル
                  if(m_to_log)
                    {
                     Print("\nTriggered the \"Breakout of channel border\" model for buying.");
                     PrintFormat("High price: %0."+IntegerToString(m_symbol.Digits())+"f",m_base_high_price);
                     PrintFormat("Trigger price: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[1]);
                    }
              }
           }
     }
//---チャネルが広いか平均値である場合 - 下の境界線からのリバウンド取引
   else
     {
      //--- 安値をセット
      if(m_base_low_price>0.)
         //---安値が下のレベルであれば
         if((m_base_low_price<=m_lower_zone[0]) && (m_base_low_price>=m_lower_zone[1]))
           {
            if(IS_PATTERN_USAGE(0))
              {
               result=m_pattern_0;
               //---ポジションが存在しない場合
               if(!is_position)
                  //---ジャーナル
                  if(m_to_log)
                    {
                     Print("\nTriggered the \"Rebound of channel border\" model for buying.");
                     PrintFormat("Low price: %0."+IntegerToString(m_symbol.Digits())+"f",m_base_low_price);
                     PrintFormat("Zone up: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[0]);
                     PrintFormat("Zone down: %0."+IntegerToString(m_symbol.Digits())+"f",m_upper_zone[1]);
                    }
              }
           }
     }
//---
   return result;
  }
//+------------------------------------------------------------------+

また、売りの条件のチェックをします。

//+------------------------------------------------------------------+
//|買いポジションを閉じるための条件を確認してください|
//+------------------------------------------------------------------+
bool CSignalEquidChannel::CheckCloseLong(double &price) const
  {
   bool to_close_long=true;
   int result=0;
   if(IS_PATTERN_USAGE(2))
      result=m_pattern_2;
   if(result>=m_threshold_close)
     {
      if(m_is_new_channel)
         //---買いが決済される場合
         if(to_close_long)
           {
            price=NormalizeDouble(m_symbol.Bid(),m_symbol.Digits());
            //---ジャーナル
            if(m_to_log)
              {
               Print("\nTriggered the \"New channel\" model for closing buy.");
               PrintFormat("Close price: %0."+IntegerToString(m_symbol.Digits())+"f",price);
              }
           }
     }
//---
   return to_close_long;
  }
//+------------------------------------------------------------------+

ショートポジションの場合、決済の条件は同じになります。

さて、トレーリングについていくつか言及します。 CTrailingEquidChannelクラスは CExpertTrailingクラスのために書かれています。

//+------------------------------------------------------------------+
//| Class CTrailingEquidChannel.                                     |
//| Purpose: Class of trailing stops based on Equidistant Channel.   |
//|              Derives from class CExpertTrailing.                 |
//+------------------------------------------------------------------+
class CTrailingEquidChannel : public CExpertTrailing
  {
protected:
   double            m_sl_distance;       //損失をストップする距離
   double            m_tp_distance;       //利益を取るための距離
   double            m_upper_val;         // 上のボーダー
   double            m_lower_val;         //下のボーダー
   ENUM_CHANNEL_WIDTH_TYPE m_ch_wid_type; // 幅によるチャネル型
   //---
public:
   void              CTrailingEquidChannel(void);
   void             ~CTrailingEquidChannel(void){};
   //--- 保護されたデータの初期化メソッド
   void              SetTradeLevels(double _sl_distance,double _tp_distance);
   //---
   virtual bool      CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp);
   virtual bool      CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp);
   //---
   bool              RefreshData(const CSignalEquidChannel *_ptr_ch_signal);
  };
//+------------------------------------------------------------------+

チャンネルシグナルから情報を取得するためのメソッドは、で強調表示されます。

OOPの基本原理 - 再定義されたショートとロング・ポジションのトレーリングをチェックするためのメソッド。

トレーリングのクラスが実際のチャネルの時間及び価格目標を受信できるようにするためには、 CSignalEquidChannelシグナルクラスとの結合を作成する必要がありました。 CEquidChannelExpertクラス内の定数で実装されました。このアプローチは、シグナル自体のパラメータを変更する危険性なしに、シグナルからすべての必要な情報を得ることができます。

//+------------------------------------------------------------------+
//| Class CEquidChannelExpert.                                       |
//|目的:等距離チャネルに基づいたトレードEAのクラス。 |
//| CExperクラスから派生。|
//+------------------------------------------------------------------+
class CEquidChannelExpert : public CExpert
  {
   //---===データメンバー===--- 
private:
   const CSignalEquidChannel *m_ptr_ch_signal;

   //--- === Methods === --- 
public:
   //---コンストラクタ/デストラクタ
   void              CEquidChannelExpert(void);
   void             ~CEquidChannelExpert(void);
   //---チャネルのシグナルオブジェクトへのポインタ
   void              EquidChannelSignal(const CSignalEquidChannel *_ptr_ch_signal){m_ptr_ch_signal=_ptr_ch_signal;};
   const CSignalEquidChannel *EquidChannelSignal(void) const {return m_ptr_ch_signal;};

protected:
   virtual bool      Processing(void);
   //---決済ポジション確認
   virtual bool      CheckClose(void);
   virtual bool      CheckCloseLong(void);
   virtual bool      CheckCloseShort(void);
   //---トレーリングストップチェック
   virtual bool      CheckTrailingStop(void);
   virtual bool      CheckTrailingStopLong(void);
   virtual bool      CheckTrailingStopShort(void);
  };
//+------------------------------------------------------------------+

決済およびトレーリングのメソッドはまた、EAのクラスで再定義されています。

EquidistantChannelExpert4.mqhファイル内のCEquidChannelExpert::Processing()は次のようになります。

//+------------------------------------------------------------------+
//|メインモジュール|
//+------------------------------------------------------------------+
bool CEquidChannelExpert::Processing(void)
  {
//---方向の計算
   m_signal.SetDirection();
//---ポジションが存在しない場合
   if(!this.SelectPosition())
     {
      //---ポジションオープニングモジュール
      if(this.CheckOpen())
         return true;
     }
//---ポジションが存在する場合
   else
     {
      if(!this.CheckClose())
        {
         //---ポジションの変更が可能である場合
         if(this.CheckTrailingStop())
            return true;
         //---
         return false;
        }
      else
        {
         return true;
        }
     }
//---何のトレードの操作がない場合
   return false;
  }
//+------------------------------------------------------------------+

これらのパラメータは、最適化されます。
 Variable StartStep
Stop
Stop loss, points
25
5
75
Take profit, points50
5
200

最適化結果は、ReportOptimizer-signal4.xmlファイルに記載されています。最良の結果は、図9に示されています。

図9、2013年から2015年の境界線のレベルを考慮した戦略の結果。

図9 境界線のレベルを考慮した戦略の結果2013-2015.


ボーダーラインの価格水準 - - パフォーマンスは向上しませんでした。

結論

この記事では、移動チャネルに基づいてシグナルを送信するためのクラスを実装するプロセスを提示しました。シグナルバージョンで、テスト結果と取引戦略を行いました。

等距離チャンネルの設定値は、全体で使用されています。したがって、別の要因が有効であったかどうかの結論は、指定の値について真です。

それでも、パフォーマンス結果を改善するための他のメソッドがあります。この記事では、そのような可能性を見つけることに注力しました。