クロスプラットフォームEA: シグナル

Enrico Lambino | 19 6月, 2017


目次

イントロダクション

前の記事クロスプラットフォームEA: オーダーマネージャでは、COrderManager クラスを示し、取引の開始と終了のプロセスを自動化するために何ができるか説明しました。 この記事では、シグナル生成のプロセスを自動化するために、ほぼ同じ原理を使用します。 これらは、CSignal クラスとそのコンテナ、CSignals クラスを通じて実現できます。 今回は、クラスオブジェクトの実装について詳しく説明します。

目的

この記事に記載されている CSignal および CSignal クラスには、次の目的があります。

  • MQL4 と MQL5 の互換性。
  • トレードシグナルの評価に関連するプロセスの大部分を自動化
  • シンプルな実装

トレードシグナル

CSignal とそのコンテナ及び CSignalsは、相場の現在の状態を与えられた全体的なシグナルを評価します。 トレードシグナルは2つの主要なグループに分けられます。エントリーシグナルと決済シグナルです。 エントリーをEAで実行する場合、すべてのエントリ条件が整っている必要があります。 一方、決済の場合、各シグナルは単独で独立しており、最終結果に影響を与えることができます。 決済も累積的に評価されます。例えば、シグナル1がすべての売りポジションを決済し、シグナル2が買いポジションを閉じる場合、最終的な結果はすべてのポジションの決済になります。

シグナルタイプ

EAには、シグナルオブジェクトがどのように使用されるか (エントリーまたは決済) に基づいて解釈される4種類のシグナルタイプがあります。 次の表は、シグナルの種類、その値、およびターゲットの使用状況に応じてどのように解釈されるかを示しています。

 シグナルタイプ
 Value  Entry Exit
CMD_VOID -1
Invalidates all other signals
Exit all trades
CMD_NEUTRAL 0
Ignored
Ignored
CMD_LONG 1
Go long
Exit Short Trades
CMD_SHORT 2
Go short
Exit Long Trades

シグナルタイプ CMD_LONG と CMD_SHORT は自明なので、他の2つのシグナルタイプに焦点を当てます。

CMD_VOID は-1 の整数値を持ち、不一致のシグナルを指します。 エントリーシグナルの場合、この出力を与えるシグナルは、他のすべてのエントリーシグナルの出力を無効にします。 このシグナルの出力は必須であり、このシグナルによって与えられたトレード条件は、実際の出力に関係なく、他のすべてのシグナルのノントレード条件になり、他のすべてのシグナルが同じ方向に同意するかどうかを意味します。 例として、次の3つのシグナルでエントリーする場合を考えてみます。

Signal 1: CMD_VOID

Signal 2: CMD_LONG

Signal 3: CMD_SHORT

Final: CMD_VOID

この場合、シグナル1が最終的に他の2つのシグナルを無効にし、最終的な結果が CMD_VOID になることがわかります。 しかし、シグナル2とシグナル3は、同じ方向のエントリーに対して同意していないことに注意してください。したがって、この場合のシグナル1の値は、ノーエントリーの状況になります。

次に示すように、少し変更されたケースを考えてみましょう。

Signal 1: CMD_VOID

Signal 2: CMD_LONG

Signal 3: CMD_LONG

Final: CMD_VOID

この例では、シグナル2とシグナル3は同じ方向 (long) に一致しますが、シグナル1は無効です。 したがって、全体的なシグナルとしては、トレードの状況ではありません。 シグナル1と2が一致している場合でも、 void シグナルを与えたときに、より多くの重みが与えられます。

一方、すべての3つのシグナルは、買いポジションを決済することに同意する一方、シグナル1は、買いポジションと売りポジションの両方を閉じるためにメッセージを送信します。 すべての決済シグナルが累積的に評価されるため、最終的な結果はすべてのトレードを閉じることになります。

CMD_NEUTRAL は0の整数値を持ち、シグナルの拒否を示すために使用されます。 選挙におけるで「禁欲」とほぼ同等です。 ニュートラルの出力を与えるシグナルは、シグナルの最終的な結果に影響を与える特権を持たず、他のシグナルに決定を委ねます。 しかし、そのシグナルがニュートラルなスタンスを与えた場合、EAはノーエントリー、ノーイグジットの状況になります。

ここでは、このセクションの最初の例を少し変更して、CMD_NEUTRAL を使用した例を示します。

Signal 1: CMD_NEUTRAL

Signal 2: CMD_LONG

Signal 3: CMD_SHORT

FINAL: CMD_VOID

3番目の例では、シグナル1は中立のポジションを示します。 この場合、最終的なシグナル出力に対してシグナル2とシグナル3のみが考慮されます。 そして、シグナル2とシグナル3は同じ方向には同意しないので、最終的な結果は、ノートレードの状況 (CMD_NEUTRAL) になります。

残りのシグナルが同じ方向に一致する場合、ケースは異なります。 4番目の例 (下図) では、最初のシグナルはニュートラルで、残りのシグナルは同じ方向に一致します。 この場合、シグナル1は無視され、シグナル2とシグナル3が評価され、最終的な結果として買いシグナルが与えられます。

Signal 1: CMD_NEUTRAL

Signal 2: CMD_LONG

Signal 3: CMD_LONG

FINAL: CMD_LONG

シグナルの順序は問題ではなく、次のシグナルのセットに注意してください。

Signal 1: CMD_NEUTRAL

Signal 2: CMD_LONG

Signal 3: CMD_LONG

Signal 4: CMD_NEUTRAL

Signal 5: CMD_LONG

Signal 6: CMD_NEUTRAL

CMD_NEUTRAL ではなく、CMD_LONG の全体的なシグナルになります。

決済シグナルとして使用すると、CMD_NEUTRAL の出力を持つシグナルは、最終的な決済シグナルの最終結果に影響を与えません。 その時点では、最終的なシグナルを決定する限り、決済シグナルがまったく存在しなかったかのようになります。

ここでは、CMD_NEUTRAL の割り当てられた値が0であり、このカスタム列挙型を ENUM_ORDER_TYPE の代用として使用していることにも注目してください。 このカスタム列挙体を使用すると、利点があります。 まず、シグナルがどのように解釈されるかをより適切にカスタマイズできます。 もう一つの利点は、初期化されていない変数からのトレードの偶発的な実行を防ぐことができます。 たとえば、ORDER_TYPE_BUY の整数値は0です。 直接 int 変数のトレード要求を処理するメソッドに直接渡す EAを持っている場合、その後、購入注文のエントリになります。 その一方で、カスタムの列挙では、このような事故が発生することはありません。

CExpertSignal との比較

CExpertSignalは、次のように全体的な方向を評価します。

  1. 独自の方向を計算し、m_direction という名前の変数に保存します。
  2. 各フィルタ
    1. その方向を取得
    2.  m_direction (特定のフィルタが反転している場合は減算) に方向を追加します。
  3. m_direction の最終値が閾値を超えると、トレードシグナルを与えます

この方法を使用すると、m_direction の値がよりポジティブであることが推測でき、より多くのシグナルが価格が上昇する可能性が高いと評価されます (閾値を超える可能性が高まります)。 同様に、m_direction の値がマイナスになるほど、シグナルは価格が下がることを予測するトレンドがあります。 閾値は常に正であるため、短いシグナルをチェックするときに m_direction の絶対値が使用されます。

この記事に示されているシグナルオブジェクトは、 CExpertSignalの簡略化されたバージョンと見なすことができます。 ただし、算術演算を使用してシグナルとそのフィルタを一括して評価するのではなく、それぞれのシグナルが個別に評価されます。 これは、汎用性の低いアプローチですが、トレーダーやプログラマは、個々のシグナルが最終的な結果に影響を与えることができる範囲でより多くの制御を与えることができます。

フェーズ

OnInit と OnDeinit

各シグナルの初期化フェーズでは、使用するインジケーターの作成と初期化、およびクラスオブジェクトで見つかったさまざまなメソッドによって必要とされる追加のクラスメンバ (存在する場合) を処理することがしばしばあります。 終了関数では、インディケータインスタンスを削除する必要があります。

OnTick

  1. 予備フェーズ (計算)-このフェーズでは、計算に必要な値 (シグナルチェック) が更新されます。
  2. メインフェーズ、またはシグナルチェック-メインフェーズでは、シグナルの実際の出力が決定されます。 このメソッドの本体は、コードの読みやすさを向上させるために、1行保持する必要があります (実際にどのようなシグナルか一目で参照できます)。
  3. 更新フェーズ-一部のシグナルでは、実際のシグナルチェックが実行された後にのみ更新できる特定のメンバが存在することがあります。 この例としては、以前のBid価格の値を追跡し、現在のBid価格やその他の値 (チャートオブジェクトやインジケーター出力など) と比較する場合が考えられます。 予備段階で前のBid価格を格納する変数を更新すると、シグナルチェック時にその値が常に現在のBid価格と等しくなるため、意味がありません。

mql4 にはこの関数はありませんが、MQL5 にはティック配列があります。 それにもかかわらず、MQL4 はここで制限要因であるため、実装を分割する場合は、クロスプラットフォームの互換性を確保するために MQL4 標準に準拠するように行われる必要があります。

実装

CSignal クラス

トレードシグナルをチェックする前に、計算に必要なデータを最初に更新することが慣例となります。 CSignal クラスの Refresh メソッドによって更新し、インジケーター (時系列データ) が最新の値に更新されます。 次のコードスニペットは、CSignal の refresh メソッドを示しています。

bool CSignalBase::Refresh(void)
  {
   for(int i=0;i<m_indicators.Total();i++)
     {
      CSeries *indicator=m_indicators.At(i);
      if(indicator!=NULL)
         indicator.Refresh(OBJ_ALL_PERIODS);
     }
   return true;
  }

CSignal の refresh メソッドの実際の呼び出しは、同じクラスの Check メソッド内で行われます。 以下のコードに示すように、このメソッドはデータを更新できなかった場合、さらなる処理を中止します。(不正確なシグナルにつながる可能性がある)

void CSignalBase::Check(void)
  {
   if(!Active())
      return;
   if(!Refresh())
      return;
   if(!Calculate())
      return;
   int res=CMD_NEUTRAL;
   if(LongCondition())
     {
      if (Entry())
         m_signal_open=CMD_LONG;
      if (Exit())
         m_signal_close=CMD_LONG;
     }
   else if(ShortCondition())
     {
      if (Entry())
         m_signal_open=CMD_SHORT;
      if (Exit())
         m_signal_close=CMD_SHORT;
     }
   else
   {
      if (Entry())
         m_signal_open=CMD_NEUTRAL;
      if (Exit())
         m_signal_close=CMD_NEUTRAL;
   }
   if(m_invert)
     {
      SignalInvert(m_signal_open);
      SignalInvert(m_signal_close);
     }
   Update();
  }

CSignal の Check メソッドの中では、実際のシグナルは、mql5 標準ライブラリの CExpertSignal によって採用されるのとほぼ同じ方法である LongCondition と ShortCondition のメソッドを呼び出すことによって決定されます。

実際のシグナルの取得は、クラスの外部から呼び出されなければならないメソッド CheckOpenLong と CheckOpenShort を呼び出すことによって実現されます (別のクラスのオブジェクトまたは ontick 関数内の右から):

boolCSignalBase:: CheckOpenLong (void)
  {
   return m_signal_open==CMD_LONG;
  }
boolCSignalBase:: CheckOpenShort (void)
  {
   return m_signal_open==CMD_SHORT;
  }

CSignal は実際のシグナルを与えません。 したがって、このメソッドは仮想であり、CSignal が拡張されるとすぐに実際に行わせるしかありません。

virtual bool      LongCondition(void)=0;
virtual bool      ShortCondition(void)=0;
しかし、上記の方法が実装される前に、時系列データとインジケーターからの情報が十分でなく、さらに計算が必要な場合には、最初に計算方法を実装する必要があります。 CSignal によって使用するインジケーターと同様に、値が格納される変数もクラスのメンバである必要があるため、変数は LongCondition および ShortCondition メソッドによってアクセスできるようになります。
virtual bool      Calculate(void)=0;
virtual void      Update(void)=0;

update メソッドが値を返さない間、計算メソッドはブール型であることに注意してください。 特定の計算を実行できなかった場合にシグナルのチェックを取り消すように EA を設定することが可能です。 一方、Update メソッドは void 型であり、実際のシグナルが取得された後にのみ呼び出されるため、ブール型を指定する必要はなくなります。

CSignal の特定のインスタンスは、エントリーシグナル、決済シグナル、あるいは両方の出力を与えるために作ることができます。 クラスのメソッドのEntry() と Exit() を切り替える設定をすることができます。 通常、EAの初期化内で行われます。

CSignals クラス

CSignals は CArrayObj の孫です。 前者は CObject のインスタンスを格納し、CSignal のインスタンスを格納できます。

このクラスの初期化には、以前の記事で説明したシンボルマネージャーオブジェクト (CSymbolManager) の受け渡しが含まれます。 別のシンボルからのデータを取得するためのオプションがシグナルに許可されます。 各シグナルの init メソッドが呼び出されるこのメソッドの下にあります:

boolCSignalsBase:: init (CSymbolManager * symbol_man)
  {
   m_symbol_man= symbol_man;
   m_event_man = aggregator;
   if(!CheckPointer(m_symbol_man))
      return false;
   for(int i=0;i<Total();i++)
     {
      CSignal *signal=At(i);
      if(!signal.Init(symbol_man))
         return false;
     }
   return true;
  }

Check メソッドは、シグナルをニュートラルシグナルに初期化し、各シグナルを繰り返し処理して出力を取得します。 void シグナルまたは有効なシグナル (買いまたは売り) を取得するが、以前の有効なシグナルとは異なる場合は、最終出力として void シグナルが与えられます。 

CSignalsBase::Check(void)
  {
   if(m_signal_open>0)
      m_signal_open_last=m_signal_open;
   if(m_signal_close>0)
      m_signal_close_last=m_signal_close;
   m_signal_open=CMD_NEUTRAL;
   m_signal_close=CMD_NEUTRAL;
   for(int i=0;i<Total();i++)
     {
      CSignal *signal=At(i);      
      signal.Check();
      if(signal.Entry())
        {
         if(m_signal_open>CMD_VOID)
           {
            ENUM_CMD signal_open=signal.SignalOpen();
            if(m_signal_open==CMD_NEUTRAL)
              {    
               m_signal_open=signal_open;
              }
            else if(m_signal_open!=signal_open)
              {               
               m_signal_open=CMD_VOID;
              }
           }
        }
      if(signal.Exit())
        {
         if(m_signal_close>CMD_VOID)
           {
            ENUM_CMD signal_close=signal.SignalClose();
            if(m_signal_close==CMD_NEUTRAL)
              {
               m_signal_close=signal_close;
              }
            else if(m_signal_close!=signal_close)
              {
               m_signal_close=CMD_VOID;
              }
           }
        }
     }
   if(m_invert)
     {
      CSignal::SignalInvert(m_signal_open);
      CSignal::SignalInvert(m_signal_close);
     }
   if(m_new_signal)
     {
      if(m_signal_open==m_signal_open_last)
         m_signal_open = CMD_NEUTRAL;
      if(m_signal_close==m_signal_close_last)
         m_signal_close= CMD_NEUTRAL;
     }
  }

インジケーターインスタンス

CSignal の各インスタンスには、m_indicators (CIndicators のインスタンス) に格納される独自のインジケーターインスタンスのセットがあります。 理想的には、CSignal の特定のインスタンスに属する各インジケーターインスタンスは、CSignal の他のインスタンスには依存しません。 MQL5 標準ライブラリで使用されるメソッドからの偏差で、EAが CExpert のクラスメンバであるCIndicators インスタンスで使用されるすべてのインディケータをまとめて格納します。 このアプローチは、重複した計算につながるオブジェクト (例えば、シグナル1上の移動平均インジケータオブジェクト、シグナル2上の重複するインジケータオブジェクト) になりやすいですが、特定の利点があります。 まず、シグナルオブジェクトを独立した単位として扱います。 シンボルと時間枠の選択で、特に、インジケーターの使用の各シグナルをより自由にできます。 たとえば、マルチ通貨ペアEAの場合、他の計測器からデータを処理するためのインジケーターを作成することは、標準ライブラリだけでエキスパートクラスを使用してセットアップするのが難しい場合があります。 おそらく、はるかに簡単な方法は、そのシグナルおよび/またはフィルタのために CExpert によって使用することができる新しいカスタムインジケーター (アクセスおよび/または必要なデータの処理) をコーディングすることです。

制限

  1. インジケーターの可用性。MT4 で利用可能なすべてのインジケーターが MT5 でも利用可能であるわけではありません (逆も同様です)。 したがって、クロスプラットフォームのEAを望んでいるなら、MT4のインジケーターはMT5に対応する必要があります。 そうしなければ、他のプラットフォームで使用できなくなります。 通常、標準的なインジケーターでも問題あります (例えば、MT4 のボリュームインジケータは、MT5 のバージョンとは異なります). カスタム指標は、その一方、MT4 と MT5 のバージョンを持っている必要がありますので、クロスプラットフォームのエキスパートアドバイザーは、それらを使用して、通常の2つのプラットフォーム上で動作することができます。
  2. 特定のデータの可用性時系列データは、MT4 では利用できません。 したがって、MT5でのみ利用可能なデータに依存するいくつかの戦略 (すなわち、ティック・ボリューム) は、mql4 コードに翻訳することが困難あるいは不可能かもしれません。

例 1: オーダーマネージャのサンプル

前回の記事「クロスプラットフォームEA: オーダーマネージャ」では、オーダーマネージャが実際のEA でどのように実行されるかを確認するためのEAの例を示しました。 このEAのメソッドは、新しいバーの開始時に売買トレードの間で交互にすることです。 次のコードスニペットは、上記のEAの ontick 関数を示しています。

void OnTick()
  {
//---
   static int bars = 0;
   static int direction = 0;
   int current_bars = 0;
   # ifdef__MQL5__
      current_bars = Bars(NULL,PERIOD_CURRENT);
   #else 
      current_bars = Bars;
   #endif
   if (bars<current_bars)
   {   
      symbol_info.RefreshRates();
      COrder *last = order_manager.LatestOrder();
      if (CheckPointer(last) && !last.IsClosed())
         order_manager.CloseOrder(last);
      if (direction<=0)
      {
         Print("Entering buy trade..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
         direction = 1;
      }
      else
      {
         Print("Entering sell trade..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
         direction = -1;
      }   
      bars = current_bars;
   }
  }

EA の動作を処理するコードの行を見れるように (シグナルの生成が懸念)、関数のさまざまな部分に配置されます。 このような簡単な EAは、コードが解読し易く、従って、管理しやすい。 ただし、EAの複雑さが増すにつれて、ソースコードの保守が難しくなることがあります。 この記事で説明したシグナル・クラスを使用して、この EA のシグナル生成を整理することを目標としています。

これを行うには、次のコードに示すように、(1) 前のバーのカウント、(2) 前のバーの前のカウント、(3) 現在の方向の3つのプロテクトメンバを使用して、メインヘッダーファイルの CSignal クラスを拡張する必要があります。

class SignalOrderManagerExample: public CSignal
  {
protected:
   int               m_bars_prev;
   int               m_bars;
   int               m_direction;
   //クラスの残り

また、CSignal クラスで見つかったメソッドを拡張する必要もあります。 計算方法については、古い例と同じ計算方法を使用しました。

bool SignalOrderManagerExample::Calculate(void)
  {
   # ifdef__MQL5__
      m_bars=Bars(NULL,PERIOD_CURRENT);
   #else
      m_bars=Bars;
   #endif
   return m_bars>0 && m_bars>m_bars_prev;
  }

現在のチャート上の棒の数を取得する方法は、2つのプラットフォーム上で異なっているので、古い例と同様に、実装を分割する必要があります。 また、クラスの計算メソッドはブール型変数であることに注意してください。 前に説明したように、計算メソッドが false を返す場合、特定の tick イベントのシグナルのさらなる処理はストップされます。 ここでは、特定のティックのシグナルをさらに処理するときに、次の2つのルールを明示的に定義します。 (1) バーの現在のカウントが0より大きく、(2) バーの現在のカウントが前の集計より大きい。 

次のコード行に示すように、カスタムクラスのメソッドを拡張することによって、クラスの update メソッドを処理します。

void SignalOrderManagerExample::Update(void)
  {
   m_bars_prev=m_bars;
   m_direction= m_direction<=0?1:-1;
  }

シグナルのチェックの後、現在 (m_bars) にバー (m_bars_prev) の前のカウントを更新します。 方向も更新します。 現在の値が0以下の場合 (前の方向が売りか、または最初にトレードを行う場合)、この変数に対して新しい値が1に設定されます。 それ以外の場合は、-1 の値です。

最後に、シグナル自体の生成に対処する。 現在のティックのシグナルの評価に必要な変数に基づいて、出力シグナルを買いシグナルまたは売りシグナルにするかどうかを決定する条件を指定します。 CSignal の LongCondition と ShortCondition のメソッドを拡張することによって行われます。

boolSignalOrderManagerExample:: LongCondition (void)
  {
   return m_direction<=0;
  }

boolSignalOrderManagerExample:: ShortCondition (void)
  {
   return m_direction>0;
  }

この例の init 関数は、古い例とよく似ています。 この例では、先ほど定義した CSignal クラスの子孫 (SignalOrderManagerExample) とコンテナ (CSignals) をインスタンス化する必要があります。

int()
  {
//---
   order_manager=new COrderManager();
   symbol_manager=new CSymbolManager();
   symbol_info=new CSymbolInfo();
   if(!symbol_info.Name(Symbol()))
      Print("symbol not set");
   symbol_manager.Add(GetPointer(symbol_info));
   order_manager.Init(symbol_manager,NULL);
   SignalOrderManagerExample *signal_ordermanager=new SignalOrderManagerExample();
   signals=new CSignals();
   signals.Add(GetPointer(signal_ordermanager));
//---
   return(INIT_SUCCEEDED);
  }

ここでは、定義されたタイプ SignalOrderManagerExample の新しいオブジェクトへのポインタであると signal_ordermanager を宣言しました。 次に、シグナルポインターを通じて CSignals に対して同じ操作を行い、add メソッドを呼び出して、SignalOrderManagerExample へのポインターを追加します。

EAの CSignal と CSignals は、シンプルな ontick 関数になります:

void OnTick()
  {
//---
   symbol_info.RefreshRates();   
   signals.Check();
   if(signals.CheckOpenLong())
     {
      close_last();
      Print("Entering buy trade..");
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
     }
   else if(signals.CheckOpenShort())
     {
      close_last();
      Print("Entering sell trade..");
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
     }
  }

現在のシグナルの生成に必要なその他の計算はすべて、CSignal および CSignals オブジェクトに移動されました。 したがって、あとはCSignals がチェックを実行し、そのメソッドの CheckOpenLong と CheckOpenShort を呼び出してその出力を取得することだけです。 次のスクリーンショットは、MT4とMT5のエキスパートをテストした結果を示しています。

(MT4)

signal_ordermanager (mt4)

(MT5)

signal_ordermanager (mt5)

例 2: MA EA

次の例では、トレードシグナルの評価で移動平均インジケーターを使用します。 MA はMT4とMT5の両方の標準インジケーターであり、クロスプラットフォームEAをコーディングする際に使用できる最もシンプルなインジケーターの1つです。

前の例と同様に、CSignal を拡張してカスタムシグナルを作成します。

class SignalMA: public CSignal
  {
protected:
   CiMA             *m_ma;
   CSymbolInfo      *m_symbol;
   string            m_symbol_name;
   ENUM_TIMEFRAMES   m_timeframe;
   int               m_signal_bar;
   double            m_close;   
   //クラスの残り

ご覧の通り、両方の mql4 と MQL5 ライブラリはすでに移動平均インジケーターのクラスオブジェクトを提供しました。 インジケーターをカスタムシグナルクラスにシンプルに組み込むことができるようになります。 必要ない場合もありますが、この例では、CSybmolInfo オブジェクトへのポインターである m_symbol を通じてターゲットシンボルも格納します。 また、シグナルバーの終値の値が格納される m_close という名前の変数も宣言します。 保護されたクラスメンバーの残りの部分は、移動平均インジケーターのパラメータです。

前の例では、使用する前に準備する複雑なデータ構造がありません。 ただし、この例では (インジケーター) があり、クラスコンストラクターで初期化する必要があります。

void SignalMA::SignalMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int period,const int shift,const ENUM_MA_METHOD method,const ENUM_APPLIED_PRICE applied,const int bar)
  {
   m_symbol_name= symbol;
   m_timeframe = timeframe;
   m_signal_bar = bar;
   m_ma=new CiMA();
   m_ma.Create(symbol,timeframe,period,0,method,applied);
   m_indicators.Add(m_ma);   
  }

移動平均のシグナルを得ることは、特定の価格の比較を含んでいます。 チャート上の特定の価格は、始値または終値、または現在のBidとAskを選択できます。 後者では、シンボルオブジェクトを使用する必要があります。 この例では、init メソッドを拡張して、シンボルマネージャから使用する正しいシンボルを取得し、OHLC データ (およびそのデリバティブ) ではなく、Bid Askを使用して価格を要求するように初期化します。

boolSignalMA:: init (CSymbolManager * symbol_man、CEventAggregator * event_man =null)
  {
   if(CSignal::Init(symbol_man,event_man))
     {
      if(CheckPointer(m_symbol_man))
        {
         m_symbol=m_symbol_man.Get();
         if(CheckPointer(m_symbol))
            return true;
        }
     }
   return false;
  }

次のコードに示すように、拡張する計算方法です。

bool SignalMA::Calculate(void)
  {
   double close[];
   if(CopyClose(m_symbol_name,m_timeframe,signal_bar,1,close)>0)
     {
      m_close=close[0];
      return true;
     }   
   return false;
  }

CSignal の Refresh メソッド内で既に実行されているため、インジケーターのデータを更新する必要はなくなりました。 また、CSignal クラスの子孫を実装して、シグナルバーの終値を取得するために、クラス CCloseBuffer を使用することもできます。 CSeries の子孫でもあるため、m_indicators に追加して、CCloseBuffer インスタンスを他のインジケーターと共に更新することもできます。 その場合、CSignal の更新メソッドまたは計算方法のいずれかを拡張する必要はなくなります。 

特定のシグナルに対しては、Update メソッドをさらに拡張する必要はないので、シグナルの生成に進むことができます。 次のコードスニペットは、LongCondition と ShortCondition のメソッドを示しています。

boolSignalMA:: LongCondition (void)
  {
   return m_close>m_ma.Main(m_signal_bar);
  }

boolSignalMA:: ShortCondition (void)
  {
   return m_close<m_ma.Main(m_signal_bar);
  }

この条件は非常に簡単です。シグナルバーの終値がシグナルバーの移動平均値より大きい場合、それは買いシグナルです。 一方、終値が低い場合、売りシグナルです。

前の例と同様に、他の必要なポインタをすべて初期化してから、CSignal インスタンスをコンテナ (CSignals インスタンス) に追加します。 次に、シグナルの初期化に必要な追加コードを示します。

SignalMA *signal_ma=new SignalMA(Symbol(),(ENUM_TIMEFRAMES) Period(),maperiod,0,mamethod,maapplied,signal_bar);
signals=new CSignals();
signals.Add(GetPointer(signal_ma));
signals.Init(GetPointer(symbol_manager),NULL);

次のコードは、前の例のOntick 関数と同じです。

void OnTick()
  {
//---
   symbol_info.RefreshRates();
   signals.Check();
   if(signals.CheckOpenLong())
     {
      close_last();
      Print("Entering buy trade..");
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
     }
   else if(signals.CheckOpenShort())
     {
      close_last();
      Print("Entering sell trade..");
      order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
     }
  }

次のスクリーンショットは、MT4 と MT5 のEAのテストの結果です。 ご覧の通り、これらのEAは、同じロジックを実行します。

(MT4)

signal_ma (mt4)

(MT5)

signal_ma (mt5)

例 3: HA EA

次の例では、 EA の 平均足に対処します。 移動平均インジケーターとは異なり、平均足はカスタムインジケーターなので、CiCustom を拡張することによって 平均足のクラスを宣言する必要があります。このEAのコーディングは前の例よりも若干複雑になります。 まず、HA インジケーターのクラスオブジェクトであるCiHAのクラス定義を示します。

class CiHA: public CiCustom
  {
public:
                     CiHA(void);
                    ~CiHA(void);
   bool              Create(const string symbol,const ENUM_TIMEFRAMES period,
                            const ENUM_INDICATOR type,const int num_params,const MqlParam &params[],const int buffers);
   double            GetData(const int buffer_num,const int index) const;
  };

2つのメソッドを拡張する必要があります。Createおよび Getdata メソッドです。 Create メソッドでは、インジケーターのインスタンスを準備するために他のタスクを実行する必要があるため、クラスコンストラクターを再定義する必要があります (標準のインジケーターオブジェクトが CIndicator から拡張されたときに見られるように初期化します):

bool CiHA::Create(const string symbol,const ENUM_TIMEFRAMES period,const ENUM_INDICATOR type,const int num_params,const MqlParam &params[],const int buffers)
  {
   NumBuffers(buffers);
   if(CIndicator::Create(symbol,period,type,num_params,params))
      return Initialize(symbol,period,num_params,params);
   return false;
  }

ここでは、インジケーターが持つことになっているバッファの数を宣言し、渡されたパラメータを初期化します。 インジケーターのパラメータは、構造体 (MqlParam) に格納されます。

GetDataメソッドの場合、2つの言語は実装によって異なります。 MQL4 では、チャート上の特定のバーに表示されるインジケーターの値を与えるiCustom関数に対して直接呼び出しが行われます。 一方、MQL5 では、インディケータの呼出プロセスが異なります。 iCustomは、インジケーターへのハンドルを提供します (ファイル操作で行われる処理とほぼ同じです)。 特定のバーでMT5インジケーターの値にアクセスするには、iCustom 関数の呼び出しではなく、そのハンドルを使用する必要があります。 この場合、実装を分割します。

double CiHA::GetData(const int buffer_num,const int index) const
  {
   # ifdef__MQL5__
      return CiCustom::GetData(buffer_num,index);
   #else
      return iCustom(m_symbol,m_period,m_params[0].string_value,buffer_num,index);
   #endif
  }

このメソッドでは、MQL5 バージョンでは、シンプルに親メソッド (CiCustom) への呼び出しの結果を返すことに注意してください。 一方、MQL4 では、親メソッド (CiCustom) は0を返すだけなので、実際には mql4 icustom 関数を呼び出すことによって拡張する必要があります。 この MQL4 関数は、インジケーターパラメータを格納するために構造体 (MqlParams) を使用しないため、この関数の呼び出しは、各カスタムインジケーターに対して常に異なります。

この EA の CSignal の拡張では、前の例と比較してもあまり違いはありません。 コンストラクタでは、シグナルの評価に必要なインディケータのパラメータに対応するために、メソッドに引数を再定義するだけです。 特定のシグナルでは、1つのインジケーターのみを使用します。

void SignalHA::SignalHA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int numparams,const MqlParam &params[],const int bar)
  {
   m_symbol_name= symbol;
   m_signal_bar = bar;
   m_ha=new CiHA();
   m_ha.Create(symbol,timeframe,IND_CUSTOM,numparams,params,4);
   m_indicators.Add(m_ha);
  }

計算方法については、MT4とMT5の 平均足インジケーターは、そのバッファの配置が異なるので、実装を分割する必要があります。 MQL4の場合は、最初の (バッファ 0)、第2、第3、および第4のバッファは、安値/高値、安値/高値、始値、終値です。 一方、MQL5 バージョンの場合、配置は始値、高値、安値、終値です。 したがって、使用プラットフォームに応じて、インジケーターから値を取得するときに、特定のバッファにアクセスすることを検討する必要があります。

bool SignalHA::Calculate(void)
  {
   # ifdef__MQL5__
      m_open=m_ha.GetData(0,signal_bar);
   #else
      m_open=m_ha.GetData(2,signal_bar);
   #endif
      m_close=m_ha.GetData(3,signal_bar);
   return true;
  }

MQL5 バージョンでは、HA ロウソク足の始値は最初のバッファ (バッファ 0) 、MQL4 バージョンでは3番目のバッファ (バッファ 2) にあります。 HA ロウソク足の終値は、両方のバージョンの4番目のバッファ (バッファ 3) に記載されているので、プリプロセッサ宣言の外部でステートメントを記述することができます。

シグナルの評価では、格納された値を評価するために使用される特定の条件に応じて、常に LongCondition および ShortCondition メソッドを更新する必要があります。 このシグナルバーが強気または弱気であるかどうかを確認することによって 平均足の典型的な利用ができます。

boolSignalHA:: LongCondition (void)
  {
   return m_open<m_close;
  }

boolSignalHA:: ShortCondition (void)
  {
   return m_open>m_close;
  }

このEAの OnTick 関数は、前の例と似ているので、次のように移動してみましょう。

int()
  {
//---
   order_manager=new COrderManager();
   symbol_manager=new CSymbolManager();
   symbol_info=new CSymbolInfo();
   if(!symbol_info.Name(Symbol()))
      Print("symbol not set");
   symbol_manager.Add(GetPointer(symbol_info));
   order_manager.Init(symbol_manager,NULL);

   MqlParam params[1];
   params[0].type=TYPE_STRING;
   # ifdef__MQL5__
      params[0].string_value="Examples\\Heiken_Ashi";
   #else
      params[0].string_value="Heiken Ashi";
   #endif
      SignalHA *signal_ha=new SignalHA(Symbol(),0,1,params,signal_bar);
   signals=new CSignals();
   signals.Add(GetPointer(signal_ha));
   signals.Init(GetPointer(symbol_manager),NULL);
//---
   return(INIT_SUCCEEDED);
  }

ここでは、使用されるトレードプラットフォームによって、平均足の ex4 ファイルのポジションが異なっていることがわかります。 MqlParams は、格納する最初のパラメータがカスタムインディケータの名前 (拡張子なし) である必要があるため、もう一度、最初のパラメータを指定するために実装を分割する必要があります。 MQL5 では、インジケーターは "Indicators\Examples\Heikin Ashi" の下にデフォルトであり、MQL4では、"Indicators\Heikin Ashi" にあります。

次のスクリーンショットは、MT4とMT5のEAのテストの結果を示しています。 図に示すように、インジケーターはグラフのプロット方法によって異なりますが、両方のインジケータが同じロジックで実行されていることがわかります。

(MT4)

signal_ha (mt4)

(MT5)

signal_ha (mt5)

例 4: HA と MA に基づくEA

最後の例は、MA と HA のインジケーターの組み合わせです。 この例とはあまり違いはありません。 2つ目と3番目の例にあるクラスの定義を追加し、CSignalMA と CSignalHA のインスタンスへのポインタを CSignals のインスタンスに追加するだけです。 このEAを使用したテストの結果を次に示します。

(MT4)

signal_ha_ma (mt4)

(MT5)

signal_ha_ma (mt5)

結論

この記事では、クロスプラットフォームEAのシグナルを処理するために使用するクラスオブジェクトCSignal クラスと CSignals class について説明しました。 このクラスは、シグナルの評価に関与するプロセスで設計されたEAのコードから分離されています。