English Русский 中文 Español Deutsch Português
ユニバーサルEA:カスタムストラテジーと補助トレードクラス(その3)

ユニバーサルEA:カスタムストラテジーと補助トレードクラス(その3)

MetaTrader 5 | 22 7月 2016, 13:09
879 0
Vasiliy Sokolov
Vasiliy Sokolov

コンテンツ

 

イントロダクション

このパートでは、ストラテジーの取引エンジンを議論します。前の2つのパートの簡単な内容を含みます。最初の記事ではユニバーサルEA:ストラテジーの取引モード、時間や曜日に応じて、EAのロジックを構成できるように取引モードで行いました。2番目の記事では、ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ。取り扱い集中型のイベントだけでなく、基本的なCStrategyクラスの基本となるカスタムEAの主なアルゴリズムに基づいて、イベントモデルを分析しました。

第三部では、CStrategyエンジンに基づいて、EAの開発に必要とされる補助的なアルゴリズムを説明します。特にログに注意が必要です。脇役にもかかわらず、ログは複雑なシステムにおいて重要な要素です。良いログは、すぐに問題の原因を突き止め、発生した場所を見つけることができます。このログは、シングルトンパターン呼ばれる特別なプログラミング技術を使用して書かれています。これに関する情報はなく、取引プロセスを整理する人のためだけでなく、非標準タスクを実行するアルゴリズムを作成する人に、興味深いものになります。

また、この記事では、利便性と直感的なインデックスを介して、市場データにアクセスすることを可能にするアルゴリズムを説明します。実際、Close[1]とHigh[0]などのインデックスを介したデータアクセスは、MT4の人気のある関数です。MT5で使用することができれば、是非使いましょう。この記事では、詳細にこのアイデアを実現するアルゴリズムについて説明します。

終了するころには、前の記事の単語を使用したいと思います。そのすべてのアルゴリズムとストラテジーの取引エンジンは、かなり複雑です。しかし、その原理の完全な理解は必要とされていません。一般的な原則と取引エンジンの機能を理解しさえすればいいのです。したがって、記事のパートのいずれかが明確でない場合、スキップしても結構です。これは、オブジェクト指向アプローチの基本的な原則の一つ:その構造を知らなくても、複雑なシステムを使用することができます。

 

ログ、CMessageとCLogクラス、シングルトンパターン

ログは、従来の補助作業の一つです。原則として、簡単なアプリケーションは、一般的なMT5の端末にエラーメッセージを表示するPrintまたは printf関数を使用します。:

...
double closes[];
if(CopyClose(Symbol(), Period(), 0, 100, closes) < 100)
   printf("Not enough data.");
...

しかし、この単純なアプローチは、ソースコードが数百行になるような大規模で複雑なプログラムにおいて、何が起こっているかを理解することは必ずしも十分ではありません。したがって、このようなタスクの最善の解決策は、特別なログモジュールCLogクラスを開発することです。

ログの最も明白なメソッドはAddMessage()です。たとえば、LogがCLogのオブジェクトの場合、次のような構成を記述することができます。

Log.AddMessage("Warning!受け取ったバーの数は、必要数以下です。

しかし、警告がデバッグに必要な情報が含まれていました。それが作成されたときに、どのようにこのメッセージから知ることができますか?どのような関数が、作成したのか?どのような重要な情報がそれに含まれているかを知ることができますか?これを回避するために、メッセージの概念を拡張する必要があります。テキストに加えて、各メッセージには、次の属性が含まれている必要があります。

  • 作成時間
  • 情報源
  • メッセージタイプ (情報、警告、エラー)

メッセージは、いくつかの追加情報が含まれている場合、有用です:

  • システムエラーID
  • 取引エラーID(取引アクションが実行された場合)
  • メッセージ作成の取引サーバー時間

この情報はすべて、 CMessageクラスで組み合わせることができます。メッセージはクラスなので、簡単にログで動作するように、より多くのデータとメソッドを追加することができます。ここで、クラスのヘッダーは次のとおりです。

//+------------------------------------------------------------------+
//|                                                         Logs.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>

#define UNKNOW_SOURCE "unknown"     // メッセージの不明なソース
//+------------------------------------------------------------------+
//|メッセージの種類|
//+------------------------------------------------------------------+
enum ENUM_MESSAGE_TYPE
  {
   MESSAGE_INFO、<S0>//情報メッセージ
   MESSAGE_WARNING,                 //警告メッセージ
   MESSAGE_ERROR                    // エラーメッセージ
  };
//+------------------------------------------------------------------+
//|ログクラスに渡されるメッセージ|
//+------------------------------------------------------------------+
class CMessage : public CObject
  {
private:
   ENUM_MESSAGE_TYPE m_type;               // メッセージタイプ
   string            m_source;             // メッセージソース
   string            m_text;               // メッセージテキスト
   int               m_system_error_id;    // システムエラーのIDの生成
   int               m_retcode;            // トレードサーバーの返りコード
   datetime          m_server_time;        // メッセージが生成されたときのトレードサーバー時間
   datetime          m_local_time;         // メッセージが生成されたローカル時間
   void              Init(ENUM_MESSAGE_TYPE type,string source,string text);
public:
                     CMessage(void);
                     CMessage(ENUM_MESSAGE_TYPE type);
                     CMessage(ENUM_MESSAGE_TYPE type,string source,string text);
   void              Type(ENUM_MESSAGE_TYPE type);
   ENUM_MESSAGE_TYPE Type(void);
   void              Source(string source);
   string            Source(void);
   void              Text(string text);
   string            Text(void);
   datetime          TimeServer(void);
   datetime          TimeLocal();
   void              SystemErrorID(int error);
   int               SystemErrorID();
   void              Retcode(int retcode);
   int               Retcode(void);
   string            ToConsoleType(void);
   string            ToCSVType(void);
  };

すべてのヘッダーの最初のENUM_MESSAGE_TYPEが含まれています。これは、メッセージのタイプを定義します。メッセージには、エラー(MESSAGE_ERROR)、警告(MESSAGE_WARNING)、情報(MESSAGE _INFO)があります。

このクラスは、メッセージのさまざまな属性を設定したり、さまざまなGet /Setのメソッドで構成されています。1行で簡単にメッセージ作成のために、メッセージはそのタイプとソースのテキストを定義するパラメータと呼ばれるべき、オーバーロードされたコンストラクタを提供します。たとえば、OnTick関数で警告メッセージを作成する必要がある場合、次のように行うことができます。

void OnTick(void)
  {
   double closes[];
   if(CopyClose(Symbol(),Period(),0,100,closes)<100)
      CMessage message=new CMessage(MESSAGE_WARNING,__FUNCTION__,"Not enough data");
  }

このメッセージは、以前のものよりも多くの情報が含まれています。メッセージ自体に加えて、呼び出した関数、およびメッセージタイプの名前を含みます。また、メッセージは、作成時に入力する必要はありません。たとえば、メッセージオブジェクトは、メッセージの作成時間と取引エラーの現在のコードが含まれています。

さて、 CLogログを検討しましょう。クラスは、CMessageメッセージのストレージとして機能します。その最も興味深い機能は、SendNotification関数を使用して、モバイル端末にプッシュ通知を送信できることです。EAの常時監視が不可能な場合に非常に便利な機能です。また、何かが間違っている場合、ユーザにプッシュを送信することができます。

ログの具体的な特徴は、それがプログラムのすべてのパートにおいて単一のプロセスでなければならないということです。それぞれの関数やクラスが独自のログメカニズムを持っているのはおかしなことです。したがって、CLogクラスと呼ばれるSingletonのプログラミングパターンが実装されている。このパターンは、特定のタイプのオブジェクトのコピーが1つだけであることが保証されています。たとえば、プログラムがCLog型のオブジェクトを参照する、二つのポインタを使用している場合、ポインタが同じオブジェクトを指します。クラスのprivateメソッドでは、オブジェクトが実際に作成し、舞台裏で削除されます。

このクラスのタイトルとSingletonパターンを実装する方法を考えてみましょう:

//+------------------------------------------------------------------+
//|                                                         Logs.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#include "Message.mqh"

//+------------------------------------------------------------------+
//||クラスはシングルトンとしてメッセージのログを実装します
//+------------------------------------------------------------------+
class CLog
{
private:
   static CLog*      m_log;                     // グローバルスタティックサンプルのポインタ
   CArrayObj         m_messages;                // 保存したメッセージのリスト
   bool              m_terminal_enable;         // トレードターミナルにメッセージを保存した場合True
   bool              m_push_enable;             // trueなら、プッシュ通知
   ENUM_MESSAGE_TYPE m_push_priority;           //ターミナルウィンドウにメッセージ表示の指定された優先順位が含まれています
   ENUM_MESSAGE_TYPE m_terminal_priority;       // モバイルデバイスにプッシュを送信する指定された優先順位が含まれています
   bool              m_recursive;               // デストラクタの再帰呼び出しを示すフラグ
   bool              SendPush(CMessage* msg);
   void              CheckMessage(CMessage* msg);
                     CLog(void);                // プライベートコンストラクタ
   string            GetName(void);
   void              DeleteOldLogs(int day_history = 30);
   void              DeleteOldLog(string file_name, int day_history);
                     ~CLog(void){;}
public:
   static CLog*      GetLog(void);              // このメソッドは、静的オブジェクトを受信します
   bool              AddMessage(CMessage* msg);
   void              Clear(void);
   bool              Save(string path);
   CMessage*         MessageAt(int index)const;
   int               Total(void);
   void              TerminalEnable(bool enable);
   bool              TerminalEnable(void);
   void              PushEnable(bool enable);
   bool              PushEnable(void);
   void              PushPriority(ENUM_MESSAGE_TYPE type);
   ENUM_MESSAGE_TYPE PushPriority(void);
   void              TerminalPriority(ENUM_MESSAGE_TYPE type);
   ENUM_MESSAGE_TYPE TerminalPriority(void);
   bool              SaveToFile(void);
   static bool       DeleteLog(void);
};
CLog* CLog::m_log;

CLogクラスは、プライベートメンバとして自身の静的オブジェクトへのポインタを格納します。それはおかしなプログラミング構造を見えるかもしれませんが、実は理にかなっています。クラスのコンストラクタはプライベートであり、呼び出すことはできません。代わりにコンストラクタを呼び出す、GetLogメソッドを用いることができます。

//+------------------------------------------------------------------+
//||ログオブジェクトを返します。
//+------------------------------------------------------------------+
static CLog* CLog::GetLog()
{
   if(CheckPointer(m_log) == POINTER_INVALID)
      m_log = new CLog();
   return m_log;
}

既存のオブジェクトへの静的ポインタポイントかどうかをチェックし、そうであれば、参照を返します。それ以外の場合、新しいオブジェクトを作成し、内部のm_logポインタを関連付けます。これは、オブジェクトが一度だけ作成されることを意味します。GetLogメソッドは、以前に作成したオブジェクトが返されます。

オブジェクトの削除はまた、一度だけ実行されます。これは、削除ログのメソッドを使用して行われます。

//+------------------------------------------------------------------+
//||ログオブジェクトを削除
//+------------------------------------------------------------------+
bool CLog::DeleteLog(void)
{
   bool res = CheckPointer(m_log) != POINTER_INVALID;
   if(res)
      delete m_log;
   return res;
}

m_logが存在する場合、削除され、trueが返されます。

しかし、ログシステムは、関数が非常に特徴的であり、複雑に思えるかもしれません。たとえば、タイプによってメッセージをランク付けすることができ、またはプッシュ通知として送信します。ユーザは、このシステムを使用するかどうかを最終的に決定します。これは、別々のモジュールMessage.mqhとLogs.mqhで実装されているので、いずれかを使用することができます。

 

MT4からインデックスを使用しアクセス

前と比較して、MT5の大きな変化の一つは、クオートと指標データにアクセスするモデルです。たとえば、現在の足の終値を見つける必要がある場合、以下の手順を追加することで、MT4でそれを行うことができます:

double close = Close[0];

適切な時系列のインデックス作成を通じて、ほぼ直接的にデータにアクセスできます。その他の操作は、MT5に現在のバーの終値を見つけるのに必要です。

  1. クオートの必要量をコピーする受信配列を定義します。
  2. (時系列と指標データにアクセスするための機能)コピー*グループの関数のいずれかを使用して、必要なクオートをコピーします。
  3. コピーされた配列の必要なインデックスを参照してください。

MT5の現在のバーの終値を検索するには、以下のアクションが必要です。

double closes[];
double close = 0.0;
if(CopyClose(Symbol(), Period(), 0, 1, closes))
   close = closes[0];
else
   printf("Failed to copy the close price.");

データのこのアクセスはMT4よりも困難です。しかし、このアプローチは、データアクセスが普遍的になります。統一されたインタフェースとメカニズムが異なるシンボルおよびインジケーターから受け取ったデータにアクセスするのに使用されます。

原則として、日々の作業で必要ではありません。多くの場合、現在のシンボルの最後の値を取得する必要があります。これは、始めんまたは終値の価格、ならびに高値または安値にすることもできます。とにかく、MT4で採用されているデータアクセスモデルを使用すると便利でしょう。MQL5はオブジェクト指向ですが、MT4で使用されるように、取引データにアクセスするのと同じ方法を使用することができます。例えば、このように終値を得ることができるようにします。

double close = Close[0];

終値に対して次のラッパーを追加する必要があります。

//+------------------------------------------------------------------+
//|シンボルの終値のアクセス|
//+------------------------------------------------------------------+
class CClose : public CSeries
  {
public:
   double operator[](int index)
     {
      double value[];
      if(CopyClose(m_symbol, m_timeframe, index, 1, value) == 0)return 0.0;
      return value[0];
     }
  };

時間、容量、ならびに、始値、高値、安値を含む他のシリーズに同じコードを書く必要があります。もちろん、いくつかのケースではCopy*関数を使用して、1回限りのコピーよりはるかに遅い可能性があります。しかしながら、上述したように、多くの場合、すべての要素は移動ウィンドウ内で考慮されるため、最後の要素にアクセスする必要があります。

クラスのこの簡単なセットは、Series.mqhに含まれています。これは、MT4で、積極的に取引エンジンで使用されているクオートにアクセスする便利なインターフェイスです。

これらのクラスの特徴は、それらのプラットフォームです。例えば、MT5のEAは、それが直接クオートを参照していることを「考える」、これらのクラスの1つのメソッドを呼び出すことができます。このアクセスメソッドは、MT4でも動作しますが、代わりに特殊なラッパーが必要です。

 

オブジェクト指向のインジケータを使用

ほぼすべてのインジケータは、構成の設定を持っています。MT5でインジケーターを操作する前に、いわゆるインジケーターハンドルを作成する必要があります。つまり、計算値を含むいくつかの内部オブジェクトへインジケータデータをコピーします。インジケータのパラメータは、ハンドルの作成時に設定されています。いくつかの理由で、インジケータのいずれかのパラメータを編集する必要がある場合、古いインジケータハンドルを削除し、更新されたパラメータで新しいものを作成する必要があります。例えば、インジケータ・パラメータは、外部の場所に格納する必要があります。

その結果、インジケータの操作のほとんどは、EAに渡されます。それは、必ずしも便利ではありません。MAのクロスのシグナルを使用して、EAの簡単な例を考えてみましょう。そのシンプルさにもかかわらず、移動平均インジケータには6つのパラメータがあります。

  1. 移動平均を計算するシンボル
  2. チャートの時間枠または期間
  3. 平均期間
  4. 移動平均タイプ(単純、指数、加重、等)
  5. 価格バーからインジケータのシフト
  6. 適用価格(バーのOHLC価格や他の指標の計算用バッファの1)

短期移動平均と長期移動平均の6つのパラメータ - つまり、12のパラメータがあります。ユーザーがEAが実行されるチャートのタイムフレームまたはシンボルを変更したい場合、使用されたインジケータのハンドルも再初期化する必要があります。

インジケータハンドルに関連するタスクからEAを解放するために、指標のオブジェクト指向を使用する必要があります。オブジェクト指向のインジケータクラスを使用することにより、構造を次のように書くことができます。

CMovingAverageExp MAExpert;     //2つの移動平均値に基づいたトレードそのEAを作成します。
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---EAの短期移動平均の設定
   MAExpert.FastMA.Symbol("EURUSD");
   MAExpert.FastMA.Symbol(PERIOD_M10);
   MAExpert.FastMA.Period(13);
   MAExpert.FastMA.AppliedPrice(PRICE_CLOSE);
   MAExpert.FastMA.MaShift(1);
//---EAの長期移動平均の設定
   MAExpert.SlowMA.Symbol("EURUSD");
   MAExpert.SlowMA.Symbol(PERIOD_M15);
   MAExpert.SlowMA.Period(15);
   MAExpert.SlowMA.AppliedPrice(PRICE_CLOSE);
   MAExpert.SlowMA.MaShift(1);

   return(INIT_SUCCEEDED);
  }

エンドユーザーは、EAが使用する指標のパラメータを設定する場合、EAはそれらからデータを読み込みます。

インジケータオブジェクトを使用することには、別の重要な利点があります。オブジェクト指向のインジケーターは、その実装を隠します。これは、自分自身で、または適切なハンドルを呼び出すことによって、それらの値を計算できることを意味します。複数の計算指標が使用され、高い実行速度が要求される場合には、EAに直接指標計算をさせることをお勧めします。オブジェクト指向アプローチのおかげで、EAを書き換えることなく行うことができます。ハンドルを使用することなく、適切なクラス内の指標値を計算する必要があります。

上記を説明するために、以下のシステムは、iMAに基づいています。

//+------------------------------------------------------------------+
//|                                                MovingAverage.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Strategy\Message.mqh>
#include <Strategy\Logs.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
class CIndMovingAverage
  {
private:
   int               m_ma_handle;         // インジケーターハンドル
   ENUM_TIMEFRAMES   m_timeframe;         //タイムフレーム
   int               m_ma_period;         // 期間
   int               m_ma_shift;          //シフト
   string            m_symbol;            //シンボル
   ENUM_MA_METHOD    m_ma_method;         // MA適応方法
   uint              m_applied_price;     // 移動平均値の計算のインジケータのハンドル
                                          //またはENUM_APPLIED_PRICEの値
   CLog*             m_log;               // Logging
   void              Init(void);
public:
                     CIndMovingAverage(void);

/*Params*/
   void              Timeframe(ENUM_TIMEFRAMES timeframe);
   void              MaPeriod(int ma_period);
   void              MaShift(int ma_shift);
   void              MaMethod(ENUM_MA_METHOD method);
   void              AppliedPrice(int source);
   void              Symbol(string symbol);

   ENUM_TIMEFRAMES   Timeframe(void);
   int               MaPeriod(void);
   int               MaShift(void);
   ENUM_MA_METHOD    MaMethod(void);
   uint              AppliedPrice(void);
   string            Symbol(void);

/*Out values*/
   double            OutValue(int index);
  };
//+------------------------------------------------------------------+
//| Default constructor.                                             |
//+------------------------------------------------------------------+
CIndMovingAverage::CIndMovingAverage(void) : m_ma_handle(INVALID_HANDLE),
                                             m_timeframe(PERIOD_CURRENT),
                                             m_ma_period(12),
                                             m_ma_shift(0),
                                             m_ma_method(MODE_SMA),
                                             m_applied_price(PRICE_CLOSE)
  {
   m_log=CLog::GetLog();
  }
//+------------------------------------------------------------------+
//| Initialization.                                                  |
//+------------------------------------------------------------------+
CIndMovingAverage::Init(void)
  {
   if(m_ma_handle!=INVALID_HANDLE)
     {
      bool res=IndicatorRelease(m_ma_handle);
      if(!res)
        {
         string text="Realise iMA indicator failed. Error ID: "+(string)GetLastError();
         CMessage *msg=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
         m_log.AddMessage(msg);
        }
     }
   m_ma_handle=iMA(m_symbol,m_timeframe,m_ma_period,m_ma_shift,m_ma_method,m_applied_price);
   if(m_ma_handle==INVALID_HANDLE)
     {
      string params="(Period:"+(string)m_ma_period+", Shift: "+(string)m_ma_shift+
                    ", MA Method:"+EnumToString(m_ma_method)+")";
      string text="Create iMA indicator failed"+params+". Error ID: "+(string)GetLastError();
      CMessage *msg=new CMessage(MESSAGE_ERROR,__FUNCTION__,text);
      m_log.AddMessage(msg);
     }
  }
//+------------------------------------------------------------------+
//|時間枠を設定します。 |
//+------------------------------------------------------------------+
void CIndMovingAverage::Timeframe(ENUM_TIMEFRAMES tf)
  {
   m_timeframe=tf;
   if(m_ma_handle!=INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//|現在の時間枠を返します。 |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CIndMovingAverage::Timeframe(void)
  {
   return m_timeframe;
  }
//+------------------------------------------------------------------+
//|移動平均平均期間を設定します。|
//+------------------------------------------------------------------+
void CIndMovingAverage::MaPeriod(int ma_period)
  {
   m_ma_period=ma_period;
   if(m_ma_handle!=INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//|移動平均の現在の平均期間を返します。 |
//+------------------------------------------------------------------+
int CIndMovingAverage::MaPeriod(void)
  {
   return m_ma_period;
  }
//+------------------------------------------------------------------+
//|移動平均タイプを設定します。|
//+------------------------------------------------------------------+
void CIndMovingAverage::MaMethod(ENUM_MA_METHOD method)
  {
   m_ma_method=method;
   if(m_ma_handle!=INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//|移動平均型を返します。|
//+------------------------------------------------------------------+
ENUM_MA_METHOD CIndMovingAverage::MaMethod(void)
  {
   return m_ma_method;
  }
//+------------------------------------------------------------------+
//|移動平均シフトを返します。|
//+------------------------------------------------------------------+
int CIndMovingAverage::MaShift(void)
  {
   return m_ma_shift;
  }
//+------------------------------------------------------------------+
//|移動平均シフトを設定します。                                  |
//+------------------------------------------------------------------+
void CIndMovingAverage::MaShift(int ma_shift)
  {
   m_ma_shift=ma_shift;
   if(m_ma_handle!=INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//|MAの計算に使用される価格の種類を設定します。     |
//+------------------------------------------------------------------+
void CIndMovingAverage::AppliedPrice(int price)
  {
   m_applied_price = price;
   if(m_ma_handle != INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//|MAの計算に使用される価格の種類を返します。        |
//+------------------------------------------------------------------+
uint CIndMovingAverage::AppliedPrice(void)
  {
   return m_applied_price;
  }
//+------------------------------------------------------------------+
//||指標を計算するためのシンボルを設定します。
//+------------------------------------------------------------------+
void CIndMovingAverage::Symbol(string symbol)
  {
   m_symbol=symbol;
   if(m_ma_handle!=INVALID_HANDLE)
      Init();
  }
//+------------------------------------------------------------------+
//||インジケータが計算されたシンボルを返します。
//+------------------------------------------------------------------+
string CIndMovingAverage::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//||インデックスとMAの値を返します。
//+------------------------------------------------------------------+
double CIndMovingAverage::OutValue(int index)
  {
   if(m_ma_handle==INVALID_HANDLE)
      Init();
   double values[];
   if(CopyBuffer(m_ma_handle,0,index,1,values))
      return values[0];
   return EMPTY_VALUE;
  }

このクラスは非常に簡単です。その主なタスクは、パラメータの変化の一つ、ならびにインデックスで算出される値を返すために、指標を再初期化することです。ハンドルはInitメソッドを使用して再初期化され、必要な値がOutValueを使用して返されます。指標値を返すメソッドは、Outで始まります。このようなMetaEditorなどのパラメータの置換するプログラミングをする場合、必要なメソッドの検索を容易にします。

取引エンジンパッケージは、オブジェクト指向の指標の数を含みます。これは、古典的な指標において独自のオブジェクト指向のバージョンを作成する方法を理解するのに役立ちます。カスタムEAのセクションは、原理を例示するものです。

 

カスタムEAでオーバーライドするメソッド

最初の記事ユニバーサルEA:ストラテジーの取引モード(その1)では、取引ストラテジーのモードと上書きされるメソッドで検討しました。さて、いくつかの練習をしてみましょう。

各EAは、EAのいくつかのプロパティに責任がある仮想メソッドをオーバーライドする必要があります。すべてのメソッドは、3つのテーブルとして上書きされるように一覧表示します。第1に、仮想メソッドの名前が含まれ、イベントやアクションが追跡または実行することを示しています。3列目は、メソッドを使用しての目的の記述が含まれています。よって、次のとおりです。

仮想メソッド イベント/アクション 目的
OnSymbolChanged 取引シンボルの名前が変更されたときに呼び出されます 取引ツールを変更すると、EAの指標は再初期化する必要があります。イベントは、EAの指標の再初期化を実行することができます。
OnTimeframeChanged 時間枠の変更 時間枠を変更すると、EAの指標は再初期化する必要があります。イベントは、EAの指標の再初期化を実行することができます。
ParseXmlParams XMLファイルを介したストラテジーの解析のカスタムパラメータ ストラテジーは、メソッドに渡されたXMLパラメータを認識し、それに応じて設定を構成する必要があります。
ExpertNameFull EAの完全な名前を返します EAのフルネームは、ルール、ストラテジーパラメータの固有のセットとして、ストラテジー名で構成されています。ストラテジーのインスタンスは、独立して、その完全な名前を決定する必要があります。この名前は、ドロップダウンエージェントのリストで、視覚的なパネルで使用されています。
OnTradeTransaction トレードイベントの場合に発生します。 いくつかのストラテジーが適切に動作するよう、トレードイベントを分析する必要があります。このイベントは、EAのトレードイベントを通過させ、それを分析することを可能にします。
InitBuy 買いを実行します オーバーライドされなければならない基本的なメソッドの一つ。適切な取引条件が形成されている場合、このメソッドは、買いを実行します。
InitSell 売りの実行 オーバーライドされなければならない基本的なメソッドの一つ。適切な取引条件が形成されている場合、このメソッドは、売りを実行します。
SupportBuy 保有ロングポジションを管理します 保有ロングポジションを管理する必要があります。たとえば、ストップロスを設定する必要があります。これらのステップは、このメソッドで実行する必要があります。
SupportSell 保有ショートポジションを管理します 保有ショートポジションを管理する必要があります。たとえば、ストップロスを設定する必要があります。これらのステップは、このメソッドで実行する必要があります。

 表1。仮想メソッドとその目的

オーバーライドする必要がある最も重要なメソッドである。InitBuy, InitSell, SupportBuy, and SupportSell. これらは、太字で示されています。上書きを忘れた場合は、InitBuyは買いを実行することはありません。オーバーライドしない場合は、保有ポジションは永久に保持したままにすることができます。そのため、EAを作成するときにこれらのメソッドをオーバーライドするように注意してください。

トレードエンジンが、自動的にXMLファイルからストラテジーをロードして、ファイルで提供設定に従って、そのパラメータを設定した場合は、ParseXmlParamsメソッドをオーバーライドする必要があります。このメソッドでは、ストラテジーはそれに渡されるパラメータを決定し、パラメータに応じて独自の設定を変更するメソッドを理解する必要があります。XMLパラメータを使ったシリーズの第四部ユニバーサルEA:グループ内取引とストラテジーのポートフォリオを管理する(その4)で、より詳細に説明します。"。ParseXmlParamsのオーバーライドの例はボリンジャーバンドに基づくストラテジーのリストに含まれています。

 

EAの二つの移動平均の例

今ではストラテジーを使用して、最初のEAを作成してみましょう。ソースコードはシンプルかつコンパクトにするために、ログ機能を使用しません。簡単に、EAで実行する必要のあるアクションを説明してみましょう:

  • 時間枠やシンボルを切り替えるとき、OnSymbolChangedとOnTimeframeChangedメソッドをオーバーライドすることにより、短期移動平均と長期移動平均の設定を変更します。
  • オーバーライドメソッドInitBuy、InitSell、SupportBuyとSupportSell。EAのこれらのメソッドにおける取引ロジック(エントリーと管理)を定義します。

EAの残りの部分は、EAによって使用される取引エンジンと指標によって行われるべきです。ここで、EAのソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//|                                                      Samples.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Strategy\Strategy.mqh>
#include <Strategy\Indicators\MovingAverage.mqh>
//+------------------------------------------------------------------+
//|2つの移動平均に基づいた古典的なストラテジーの例。 |
//|短期MAが長期MAを横切る場合|
//|買いを実行。差がた場合売り                      |
//+------------------------------------------------------------------+
class CMovingAverage : public CStrategy
  {
private:
   bool              IsTrackEvents(const MarketEvent &event);
protected:
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent &event,CPosition *pos);
   virtual void      SupportSell(const MarketEvent &event,CPosition *pos);
   virtual void      OnSymbolChanged(string new_symbol);
   virtual void      OnTimeframeChanged(ENUM_TIMEFRAMES new_tf);
public:
   CIndMovingAverage FastMA;        // 短期移動平均
   CIndMovingAverage SlowMA;        //長期移動平均
                     CMovingAverage(void);
   virtual string    ExpertNameFull(void);
  };
//+------------------------------------------------------------------+
//| Initialization.                                                  |
//+------------------------------------------------------------------+
CMovingAverage::CMovingAverage(void)
  {
  }
//+------------------------------------------------------------------+
//|シンボルの変化に対応|
//+------------------------------------------------------------------+
void CMovingAverage::OnSymbolChanged(string new_symbol)
  {
   FastMA.Symbol(new_symbol);
   SlowMA.Symbol(new_symbol);
  }
//+------------------------------------------------------------------+
//| タイムフレームの変更に対応                              |
//+------------------------------------------------------------------+
void CMovingAverage::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
  {
   FastMA.Timeframe(new_tf);
   SlowMA.Timeframe(new_tf);
  }
//+------------------------------------------------------------------+
//|短期MAが長期MAを超えているときに買います。       |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // イベント処理
   if(positions.open_buy > 0) return;                    // 少なくとも一つの保有ポジションがある場合、買いません
   if(FastMA.OutValue(1) > SlowMA.OutValue(1))           // 買いポジションがない場合は、チェック:
      Trade.Buy(MM.GetLotFixed(), ExpertSymbol(), "");   // 上側の場合買い
  }
//+------------------------------------------------------------------+
//|短期MAを下回るときにロングポジションを閉じる|
//| slow one.                                                        |
//+------------------------------------------------------------------+
void CMovingAverage::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   if(!IsTrackEvents(event))return;                      // イベント処理
   if(FastMA.OutValue(1) < SlowMA.OutValue(1))           // 短期MAが長期MAの下側の場合 
      pos.CloseAtMarket("Exit by cross over");           // ポジションを閉じる
  }
//+------------------------------------------------------------------+
//|短期MAが長期MAを超えているときに買います。       |
//+------------------------------------------------------------------+
void CMovingAverage::InitSell(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // イベント処理
   if(positions.open_sell > 0) return;                   // 少なくとも一つのショート・ポジションがある場合
   if(FastMA.OutValue(1) < SlowMA.OutValue(1))           // 保有ポジションがない場合:
      Trade.Sell(1.0, ExpertSymbol(), "");               // 上記の場合買い
  }
//+------------------------------------------------------------------+
//|短期MAが上にあるときにショートポジションを閉じる|
//| the slow one.                                                    |
//+------------------------------------------------------------------+
void CMovingAverage::SupportSell(const MarketEvent &event,CPosition *pos)
  {
   if(!IsTrackEvents(event))return;                      // イベント処理
   if(FastMA.OutValue(1) > SlowMA.OutValue(1))           // 短期MAが長期よりも上にある場合
      pos.CloseAtMarket("Exit by cross under");          // ポジションを閉じる
  }
//+------------------------------------------------------------------+
//| イベントのフィルタ. 渡されたイベントがない場合|
//|処理される場合falseを返す|
//| returns true.                                                    |
//+------------------------------------------------------------------+
bool CMovingAverage::IsTrackEvents(const MarketEvent &event)
  {
//---、シンボルと時間枠は新しいバーでのみを扱う
   if(event.type != MARKET_EVENT_BAR_OPEN)return false;
   if(event.period != Timeframe())return false;
   if(event.symbol != ExpertSymbol())return false;
   return true;
  }

上記のソースコードは理解しやすいです。しかし、いくつかのポイントを明確にする必要があります。CStrategyエンジンは、新しいティックまたはタイマーの変更などの任意のイベントの出現時、InitBuy、InitSell、SupportBuyとSuportSell(トレード・ロジック・メソッド)を呼び出します。一般的に、これらのメソッドは非常に頻繁に呼び出されます。しかし、EAは、非常に限られたセットを使用しています。これは新しいバーの形成のイベントを使用しています。そのため、トレード・ロジック・メソッドを呼び出すさい、他のすべてのイベントは無視する必要があります。IsTrackEventsメソッドは、そのために使用されます。渡されたイベントが追跡されているかどうかをチェックし、もしそうならば - trueを返します、それ以外の場合はfalseを返します。

positionsストラクチャは、補助変数として使用されます。これは、現在のストラテジーに属するロングとショートポジションの数が含まれています。すべてのオープンポジションを通過する必要がないように、ストラテジーエンジンは、統計情報を計算します。EAのポジションエントリーロジックは、実際には、以下の条件に縮小されています。

  1. トレードイベントは、新しいバーでのエントリーです。
  2. 同じ方向には他のオープンポジションはありません。
  3. 短期移動平均は長期移動平均を上(買い)か下(売り)にあります。

ポジションを閉じるために満たされなければならない条件は簡単です。

  1. トレードイベントは、新しいバーでのエントリーです。
  2. 短期移動平均が、長期移動平均をクロスしたとき、(ロングポジションをクローズする)または(ショート・ポジションをクローズする)。

パラメータとして、SupportBuyとSupportSellの呼び出しによるEAのポジションが存在しているので、この場合は、ポジションを確認する必要はありません。

アカウントにメソッドとEAでの実際のロジックは、コードの18行目に記述されています。また、これらの行の半分は他の半分の逆です。(買いと売りの条件は逆になっている)ストラテジーのような補助的なライブラリを使用する場合、ロジックの単純化は可能です。

 

ボリンジャーバンドチャンネルのブレークスルーに基づいたEAの例

ストラテジー取引エンジンを使用して、このストラテジーを作成します。第二の例では、ボリンジャーバンドのチャネルブレイクアウトを取引ストラテジーとして作成します。現在の価格が、ボリンジャーバンドの上にある場合は、買い。現在のバーに近い価格が、ボリンジャーバンドを下回っている場合、逆に売り。価格がインジケーターの中間線に達するとロングとショートのポジションを閉じます。

今回は、標準的なiBandsインジケータを使用します。これは、当社のインジケータを直接扱うと、必要とされていない特殊なオブジェクト指向のインジケータクラスを構築できていることを示すために行います。平均化期間と標準偏差値 - - このケースでは、インジケーターの主要なパラメータを二つ指定する必要があります。ソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//|                                                ChannelSample.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Strategy\Strategy.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
class CChannel : public CStrategy
  {
private:
   int               m_handle;   // インジケーターのハンドル
   int               m_period;   // ボリンジャーバンドの期間
   double            m_std_dev;  //標準偏差の値
   bool              IsTrackEvents(const MarketEvent &event);
protected:
   virtual void      OnSymbolChanged(string new_symbol);
   virtual void      OnTimeframeChanged(ENUM_TIMEFRAMES new_tf);
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent &event,CPosition *pos);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportSell(const MarketEvent &event,CPosition *pos);
   virtual bool      ParseXmlParams(CXmlElement *params);
   virtual string    ExpertNameFull(void);
public:
                     CChannel(void);
                    ~CChannel(void);
   int               PeriodBands(void);
   void              PeriodBands(int period);
   double            StdDev(void);
   void              StdDev(double std);
  };
//+------------------------------------------------------------------+
//| Default constructor                                              |
//+------------------------------------------------------------------+
CChannel::CChannel(void) : m_handle(INVALID_HANDLE)
  {
  }
//+------------------------------------------------------------------+
//||デストラクタはインジケータの使用のハンドルを解放します
//+------------------------------------------------------------------+
CChannel::~CChannel(void)
  {
   if(m_handle!=INVALID_HANDLE)
      IndicatorRelease(m_handle);
  }
//+------------------------------------------------------------------+
//|シンボルの変化に対応|
//+------------------------------------------------------------------+
void CChannel::OnSymbolChanged(string new_symbol)
  {
   if(m_handle!=INVALID_HANDLE)
      IndicatorRelease(m_handle);
   m_handle=iBands(new_symbol,Timeframe(),m_period,0,m_std_dev,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//| タイムフレームの変更に対応                              |
//+------------------------------------------------------------------+
void CChannel::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
  {
   if(m_handle!=INVALID_HANDLE)
      IndicatorRelease(m_handle);
   m_handle=iBands(ExpertSymbol(),Timeframe(),m_period,0,m_std_dev,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//||インジケータ期間を返します。
//+------------------------------------------------------------------+
int CChannel::PeriodBands(void)
  {
   return m_period;
  }
//+------------------------------------------------------------------+
//||インジケータ期間を設定します。
//+------------------------------------------------------------------+
void CChannel::PeriodBands(int period)
  {
   if(m_period == period)return;
   m_period=period;
   if(m_handle!=INVALID_HANDLE)
      IndicatorRelease(m_handle);
   m_handle=iBands(ExpertSymbol(),Timeframe(),m_period,0,m_std_dev,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//| 標準偏差の値をセット                                |
//+------------------------------------------------------------------+
double CChannel::StdDev(void)
  {
   return m_std_dev;
  }
//+------------------------------------------------------------------+
//| 標準偏差の値をセット                                |
//+------------------------------------------------------------------+
void CChannel::StdDev(double std)
  {
   if(m_std_dev == std)return;
   m_std_dev=std;
   if(m_handle!=INVALID_HANDLE)
      IndicatorRelease(m_handle);
   m_handle=iBands(ExpertSymbol(),Timeframe(),m_period,0,m_std_dev,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//| 買いポジションのルール                                      |
//+------------------------------------------------------------------+
void CChannel::InitBuy(const MarketEvent &event)
  {
   if(IsTrackEvents(event))return;                    //新しい足の形成でのみ許可
   if(positions.open_buy > 0)return;                  // 1つ以上のポジションをもたない
   double bands[];
   if(CopyBuffer(m_handle, UPPER_BAND, 1, 1, bands) == 0)return;
   if(Close[1]>bands[0])
      Trade.Buy(1.0,ExpertSymbol());
  }
//+------------------------------------------------------------------+
//| 買いポジションの決済ルール                                      |
//+------------------------------------------------------------------+
void CChannel::SupportBuy(const MarketEvent &event,CPosition *pos)
  {
   if(IsTrackEvents(event))return;                    //新しい足の形成でのみ許可
   double bands[];
   if(CopyBuffer(m_handle, BASE_LINE, 1, 1, bands) == 0)return;
   double b = bands[0];
   double s = Close[1];
   if(Close[1]<bands[0])
      pos.CloseAtMarket();
  }
//+------------------------------------------------------------------+
//| 買いポジションのルール                                      |
//+------------------------------------------------------------------+
void CChannel::InitSell(const MarketEvent &event)
  {
   if(IsTrackEvents(event))return;                    //新しい足の形成でのみ許可
   if(positions.open_sell > 0)return;                 //1つ以上のポジションを持たない
   double bands[];
   if(CopyBuffer(m_handle, LOWER_BAND, 1, 1, bands) == 0)return;
   if(Close[1]<bands[0])
      Trade.Sell(1.0,ExpertSymbol());
  }
//+------------------------------------------------------------------+
//| 買いポジションの決済ルール                                      |
//+------------------------------------------------------------------+
void CChannel::SupportSell(const MarketEvent &event,CPosition *pos)
  {
   if(IsTrackEvents(event))return;     // 新しい足の形成でのみ許可
   double bands[];
   if(CopyBuffer(m_handle, BASE_LINE, 1, 1, bands) == 0)return;
   double b = bands[0];
   double s = Close[1];
   if(Close[1]>bands[0])
      pos.CloseAtMarket();
  }
//+------------------------------------------------------------------+
//| イベントのフィルタ. 渡されたイベントがない場合|
//|処理される場合falseを返す|
//| returns true.                                                    |
//+------------------------------------------------------------------+
bool CChannel::IsTrackEvents(const MarketEvent &event)
  {
//---、シンボルと時間枠は新しいバーでのみを扱う
   if(event.type != MARKET_EVENT_BAR_OPEN)return false;
   if(event.period != Timeframe())return false;
   if(event.symbol != ExpertSymbol())return false;
   return true;
  }
//+------------------------------------------------------------------+
//||ストラテジーの具体的なパラメータは、それの内側に渡されます
//|ストラテジーからオーバーライドメソッド|
//+------------------------------------------------------------------+
bool CChannel::ParseXmlParams(CXmlElement *params)
  {
   bool res=true;
   for(int i=0; i<params.GetChildCount(); i++)
     {
      CXmlElement *param=params.GetChild(i);
      string name=param.GetName();
      if(name=="Period")
         PeriodBands((int)param.GetText());
      else if(name=="StdDev")
         StdDev(StringToDouble(param.GetText()));
      else
         res=false;
     }
   return res;
  }
//+------------------------------------------------------------------+
//|EAの完全な一意の名前|
//+------------------------------------------------------------------+
string CChannel::ExpertNameFull(void)
  {
   string name=ExpertName();
   name += "[" + ExpertSymbol();
   name += "-" + StringSubstr(EnumToString(Timeframe()), 7);
   name += "-" + (string)Period();
   name += "-" + DoubleToString(StdDev(), 1);
   name += "]";
   return name;
  }

今EAは、より多くの操作を実行します。EAはボリンジャーの平均化パラメータとその標準偏差値が含まれています。また、EAはインジケータハンドルを作成し、適切な方法でそれらを破棄します。これは、ラッパーを使用せずに指標を直接使用しているためです。残りのコードは、前のEAに似ています。これは、最後の足の終値は、上記のボリンジャーバンドで(買い)または(売り)待機し、新しいポジションを開きます。

時系列のクラスを介して、バーの直接アクセスを使用することに注意してください。例えば、このメソッドは、購入セクションの上部ボリンジャーバンド(InitBuyメソッド)と、最後のバーに近い価格を比較するために使用されます。

double bands[];
if(CopyBuffer(m_handle, UPPER_BAND, 1, 1, bands) == 0)return;
if(Close[1] > bands[0])
   Trade.Buy(1.0, ExpertSymbol());

既によく知られた方法に加えて、EAはオーバーライドされたExpertNameFullとParseXmlParamsが含まれています。第1に、ユーザパネルにEAの名前として表示されるEAで、一意の名前を決定します。第二に、ボリンジャー・インジケータの設定は、XMLファイルを形成します。XMLファイルに格納された、ユーザパネルとEAの設定は次の記事にあります。EA操作の残りの部分は、以前のものと同様です。EA開発の完全な統一:それが提案された今回のアプローチの目的です。

 

取引エンジンにカスタムストラテジーのロード

すべてのストラテジーを説明した後、そのインスタンスを作成し、必要なパラメータを指定して、それらを初期化し、取引エンジンに追加する必要があります。エンジンにロードされたストラテジーは、それが返すべきいくつかの必要な属性(完全なプロパティ)を持っている必要があります。これらの属性は、次のプロパティが含まれます。

  • ストラテジーの一意の識別子(マジックナンバーです)。ストラテジーIDは、それらが同じクラスのインスタンスとして作成された場合でも、一意である必要があります。一意の番号を指定するには、ストラテジーのExpertMagic()を設定し、メソッドを使用します。
  • ストラテジーの時間枠(またはその動作期間)。ストラテジーは、同時に複数の期間で実行されている場合でも、作業時間枠を指定する必要があります。この場合、例えば、最も頻繁に使用される時間枠であってもよいです。期間を指定するには、時間枠を設定し、メソッドを使用します。
  • ストラテジーのシンボル(またはその作業インストゥルメンタル)。ストラテジーは、複数のシンボル(多通貨ストラテジー)で動作する場合でも、作業シンボルを指定する必要があります。これは、ストラテジーによって使用されるシンボルのいずれかになります。
  • ストラテジー名。上記の属性に加えて、各ストラテジーはまた、独自のstringの名前を持つ必要があります。EA名はExpertNameセットメソッドを使用して指定されています。Strategies.xmlファイルからストラテジーの自動生成に使用されるため、このプロパティは必要とされます。同じプロパティは、第四の記事で説明するユーザパネルでストラテジーを表示するために使用されます。

これらの属性の少なくとも一方が指定されていない場合、取引エンジンは、アルゴリズムをロードすることを拒否し、不足しているパラメータを指定して、警告メッセージが返されます。

取引エンジンは、主に2つの部分から構成されます。

  • ストラテジーを管理するための外部モジュールCStrategyList。このモジュールは、ストラテジーのマネージャであり、それらを制御するために使用されるアルゴリズムが含まれています。このシリーズの次のパートでは、このモジュールを説明します。
  • ストラテジーの内部モジュールCStrategy。このモジュールは、ストラテジーの基本的な機能を定義します。この中のディテールと前回の記事で説明した:"ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ(パート2)"。です。

ストラテジーの各インスタンスは、ストラテジーのストラテジーリストマネージャにロードする必要があります。ストラテジーのマネージャーは、2つの方法でロードストラテジーを可能にします:

  • 自動的 Strategies.xml構成ファイルを使用。たとえば、ストラテジーのセットと、このファイル内のパラメータを記述することができます。チャート上でEAを実行するときに、ストラテジーマネージャは、そのパラメータを初期化し、そのリストに追加されます。このメソッドは、次の記事で詳細に説明します。
  • 手動実行モジュールを追加。この場合、ストラテジーの目的は、命令のセットを使用して、EAのOnInitセクションで作成され、必要なパラメータで初期化し、CStrategyListストラテジーマネージャに追加されます。

ここでは、手動設定のプロセスの説明があります。Agent.mq5ファイルを生成します。

//+------------------------------------------------------------------+
//|                                                        Agent.mq5 |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\ChannelSample.mqh>
#include <Strategy\Samples\MovingAverage.mqh>
CStrategyList Manager;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//移動平均ストラテジーのリスト---設定と追加
   CMovingAverage *ma=new CMovingAverage();
   ma.ExpertMagic(1215);
   ma.Timeframe(Period());
   ma.ExpertSymbol(Symbol());
   ma.ExpertName("Moving Average");
   ma.FastMA.MaPeriod(10);
   ma.SlowMA.MaPeriod(23);
   if(!Manager.AddStrategy(ma))
      delete ma;

//--- 戦略のリストチャンネルに追加
   CChannel *channel=new CChannel();
   channel.ExpertMagic(1216);
   channel.Timeframe(Period());
   channel.ExpertSymbol(Symbol());
   channel.ExpertName("Bollinger Bands");
   channel.PeriodBands(50);
   channel.StdDev(2.0);
   if(!Manager.AddStrategy(channel))
      delete channel;

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|エキスパート初期化解除機能|
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Manager.OnTick();
  }
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
   Manager.OnBookEvent(symbol);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+//||
//+------------------------------------------------------------------+//||
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   Manager.OnChartEvent(id,lparam,dparam,sparam);
  }

このリストから、その戦略の設定がOnInit関数内で行われることを参照してください。ストラテジーに必要なパラメータのいずれかを指定するのを忘れた場合は、ストラテジーマネージャはそのリストにストラテジーを追加することを拒否します。この場合、AddStartegyメソッドは、falseを返し、作成されたストラテジーのインスタンスを削除する必要があります。ストラテジーマネージャを使用すると、潜在的な問題を理解するために、警告メッセージを生成します。そのようなメッセージをコールしてみましょう。これを行うには、マジックナンバーを設定する命令をコメントアウトします。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//移動平均ストラテジーのリスト---設定と追加
   CMovingAverage *ma=new CMovingAverage();
 //ma.ExpertMagic(1215);
   ma.Timeframe(Period());
   ma.ExpertSymbol(Symbol());
   ma.ExpertName("Moving Average");
   ma.FastMA.MaPeriod(10);
   ma.SlowMA.MaPeriod(23);
   if(!Manager.AddStrategy(ma))
      delete ma;
   return(INIT_SUCCEEDED);
  }

実行モジュールの開始後、次のメッセージがターミナルに表示されます。

2016.01.20 14:08:54.995 AgentWrong (FORTS-USDRUB,H1)    WARNING;CStrategyList::AddStrategy;The strategy should have a magic number. Adding strategy Moving Average is impossible;2016.01.20 14:09:01

マジックナンバーが設定されていなかったので、CStrategyList::AddStrategy メソッドはストラテジーを追加することができませんでした.メッセージからも明らかです。

構成ストラテギーに加えて、Agent.mq5ファイルは、取引イベントの処理が含まれます。この処理は、イベントの追跡とCStrategyListクラスのメソッドに渡すことを含みます。

実行可能ファイルが作成されたら、それをコンパイルすることができます。分析ストラテジーのソースコードは、zip形式の添付ファイル内のInclude\Strategy\Samplesディレクトリにあります。コンパイルされたEAが使用できるようになると、2つのトレーディングストラテジーのロジックが含まれます。

 

結論

カスタムストラテジーの例と簡単なインデクサーを通じて、クオートへのアクセスを提供するクラスを分析しました。また、ログおよびオブジェクト指向での指標の例を実装するクラスについて説明してきました。EAを構築するコンセプトで、取引システムのロジックの形式化が容易になります。必要とされることは、いくつかのオーバーライドメソッドでルールを定義することです。

シリーズの第4部"ユニバーサルEA:グループとトレーディングの管理ポートフォリオストラテジー(その4)"では、取引を無制限に使用して、アルゴリズムを記述します。第四部では、また、実行可能モジュール内部のEAを管理します。たとえば、取引モードを変更したり、簡単なユーザパネルを検討します。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2170

添付されたファイル |
strategyarticle.zip (100.23 KB)
MQL5の料理本 - 移動チャネルのプログラミング MQL5の料理本 - 移動チャネルのプログラミング
本稿では、等間隔チャネルシステムのプログラミング法について取り扱っています。等間隔チャネル構築の細部まで本稿で紹介します。チャネル分類について説明したのち、移動チャネルのプログラミング法について提案します。コード実行にはオブジェクト指向型プログラムを使用しています。
ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ(パート2) ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ(パート2)
この記事は、ユニバーサルEAのシリーズです。このパートでは、データ処理に基づいて、オリジナルのイベント・モデルについて解説し、エンジンのストラテジーの基本クラスの構造を扱います。
ユニバーサルEA:グループでの取引とストラテジーのポートフォリオを管理する(その4) ユニバーサルEA:グループでの取引とストラテジーのポートフォリオを管理する(その4)
CStrategyの取引エンジンについての一連の記事の最後のパートでは、XMLファイルからストラテジーをロードする方法を行います。複数の取引アルゴリズムの同時動作を考慮し、単一の実行可能モジュールからのEAを選択する簡単なパネルを提示し、その取引モードを管理します。
エキスパートアドバイザーを使って自分ルールでシグナルをコピーするには? エキスパートアドバイザーを使って自分ルールでシグナルをコピーするには?
シグナルを購読していると、このような状況が起こることがあります。貴方の取引口座はレバレッジが1:100であるのに、プロバイダのレバレッジは1:500かつ最小ロットでトレードを行っていて、貴方の取引残高はほぼ同じ。そしてこの時コピー係数は10%から15%になるという状況です。この記事では、このような場合にどのようにコピー係数を上げたら良いかをお話しします。