English Русский 中文 Español Deutsch Português
ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ(パート2)

ユニバーサルEA:イベントモデルと取引ストラテジープロトタイプ(パート2)

MetaTrader 5 | 22 7月 2016, 13:01
789 0
Vasiliy Sokolov
Vasiliy Sokolov

コンテンツ

 

イントロダクション

この記事では、ユニバーサル取引エンジンCStrategyのさらなる解説を行います。最初の記事ユニバーサルEA:ストラテジーの取引モード(その1)では、実装を可能にする詳細や取引モードや機能について議論してきました。新しいポジションを開き、閉じる4つのメソッドからなるユニバーサルEAを分析しました。メソッドの組み合わせが、特定の取引モードを定義します。例えば、EAでは、保有ポジションを管理したり、買いエントリーや売りエントリーなどを許可することができます。これらのモードは、柔軟に、取引時間や曜日に応じて設定することができます。

しかし、取引モードは、唯一のものではありません。第二部では、中央集中型のイベント処理に基づいた、CStrategy取引モードのイベントモデルについて説明します。提案のイベント処理方式は、すべてのイベントが一箇所に集まっているという点で、システムイベントとは異なります。このような実装の利点は、後に検討します。

また、この記事では、取引エンジンの二つの重要なクラス CStrategy CPositionについて説明します。EAロジックのコアは、カスタムEAが直接継承し、単一の柔軟なフレームワークにイベントとモードを融合しています。第二のクラスは、ユニバーサルトレーディングの基礎です。これには、オープンポジションに適用されるアクション(ストップロスまたはテイクプロフィット、決済や変更など)があります。すべての取引のアクションを形式し、プラットフォームに依存せずに作ることができます。

準拠する必要があるエンジンでの作業のすべては、最終的な利益のために作成されていることに注意してください。例えば、通常のポジションの検索は、ストラテジーでは使用できません。したがって、一連の動作について、イベントを処理するハンドラについて考慮する必要はありません。その代わりに、CStrategyは、他の多くの操作の実装に着手することによって、その取引ロジックに集中します。

記載したものと同様の取引エンジンは、容易に機能を使用できるように、通常のユーザーのために設計されています。記述されているアルゴリズムの詳細を分析する必要はありません。一般的な原則とCStrategyの関数を理解する必要があります。そのため、理解することが困難な場合、それらをスキップしても構いません。

 

集中処理、ENUM_MARKET_EVENT_TYPEに基づいたイベントモデル

MT5は、多くのイベントを提供しています。これらのイベントは、市場価格の変更の通知を含みます。(NewTick, BookEvent) や TimerTradeTransaction. 同じ接頭辞を持つ名前のシステムは、各イベントに利用可能です。この関数は、イベントのハンドラです。たとえば、新しいティックで処理したい場合OnTick関数へ適切なセットを追加します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //新しいティックの取り扱い
   // ...
  }

OnBookEventが発生した場合、オーダーブックの変更を処理するコード(板情報)の別のユニットでは、価格が呼び出されなければなりません。

void OnBookEvent (const string& symbol)
  {
   //ここでは、オーダーブックの価格の変更を処理します
   // ...
  }

このアプローチでは、より多くのイベントがEAで処理され、より細分化されたロジックになります。ポジションの管理の条件は、異なるユニットに分けることができます。効率的なプログラミングの観点から、異なるユニットへの分離は良い解決策になることがありますが、この場合は望ましくありません。逆に、よりよい解決策は、一か所に割り当てられた場所で取引ロジックを構築することです。

また、一部のイベントは、MT5でサポートされていません。この場合、EAは、自分自身で条件の発生を検出する必要があります。例えば、MT5は、新しいバーの形成を取り扱うイベントがありません。しかし、EAによって最も一般的に使用されます。したがって、取引エンジンのイベントモデルだけでなく、カスタムイベントがEAの発展を促進するシステム・イベントをサポートしています。(チャート上のユーザイベントと混同しないように)。例えば、このようなイベントとして、チャート上の新しいバーの形成があります。

提案されたイベントモデルを理解するために、ENUM_MARKET_EVENT_TYPEを使用して、価格や時間の変更に関連するイベントを解説しましょう:

//+------------------------------------------------------------------+
//|市場イベントのタイプを決定します。|
//+------------------------------------------------------------------+
enum ENUM_MARKET_EVENT_TYPE
  {
   MARKET_EVENT_TICK,               // 新規ティック
   MARKET_EVENT_BAR_OPEN、//新しいバーの始値
   MARKET_EVENT_TIMER,//トリガータイマー
   MARKET_EVENT_BOOK_EVENT//(新ティックを含む)市場のマーケットボード。
  };

見ることもわかるように、MT5でサポートされるシステムイベントおよびイベントの詳細があります。( - EAの作業シンボルの新しいバーの始めMARKET_EVENT_BAR_OPEN)

ユニバーサルEAは、取引ロジックの4つのメソッドを持っているとします。: InitBuy、InitSell、SupportBuy、SupportSellユニバーサルEAのパート1で、これらのメソッドについて記載しています。これらのメソッドのパラメータとして使用されている場合、EAの処理ロジックは、いつでもメソッドが呼び出されたイベントを見つけることができます。EAの簡易スキームをみてみましょう:

//+------------------------------------------------------------------+
//|                                                     ExampExp.mq5 |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Strategy\Series.mqh>
ulong ExpertMagic=12345; // マジックナンバー
//+------------------------------------------------------------------+
//|市場イベントのタイプを決定します。|
//+------------------------------------------------------------------+
enum ENUM_MARKET_EVENT_TYPE
  {
   MARKET_EVENT_TICK,               //新しいティック
   MARKET_EVENT_BAR_OPEN、//現在の新しいバーの始値
   MARKET_EVENT_TIMER,//トリガータイマー
   MARKET_EVENT_BOOK_EVENT//(ティックを含む)市場のマーケットボード
  };
//+------------------------------------------------------------------+
//|ユニバーサルEAのプロトタイプ。 |
//+------------------------------------------------------------------+
class CExpert
  {
public:
   void              InitBuy(ENUM_MARKET_EVENT_TYPE &event_id);
   void              InitSell(ENUM_MARKET_EVENT_TYPE &event_id);
   void              SupportBuy(ENUM_MARKET_EVENT_TYPE &event_id);
   void              SupportSell(ENUM_MARKET_EVENT_TYPE &event_id);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::InitBuy(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::InitSell(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::SupportBuy(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::SupportSell(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }

ENUM_MARKET_EVENT_TYPE event_type;
CExpert Expert;
datetime last_time;
CTime Time;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   EventSetTimer(1);
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   event_type=MARKET_EVENT_TICK;
   CallExpertLogic(event_type);
   if(last_time!=Time[0])
     {
      event_type=MARKET_EVENT_BAR_OPEN;
      CallExpertLogic(event_type);
      last_time=Time[0];
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTimer()
  {
   event_type=MARKET_EVENT_TIMER;
   CallExpertLogic(event_type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
   event_type=MARKET_EVENT_TIMER;
   CallExpertLogic(event_type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CallExpertLogic(ENUM_MARKET_EVENT_TYPE &event)
  {
   Expert.InitBuy(event);
   Expert.InitSell(event);
   Expert.SupportBuy(event);
   Expert.SupportSell(event);
  }
//+------------------------------------------------------------------+

市場価格の変更は、現在のイベントがパラメータとして渡されたCallExpertMagic関数を呼び出し示すイベントです。この関数はCExpertの4つのメソッドを呼び出します。これらが呼び出されると、各メソッドは、その名前とコールの原因となったイベントの識別子を出力します。チャート上でこのEAを実行する場合、イベントIDがいくつかの時間後に表示され、処理されたことを示します。:

2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:40.015 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:40.015 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_TICK

 

その他のインストゥルメントで発生したイベントへのアクセス、市場のイベントストラクチャ

複数のシンボルを解析するシステムを設計するときは、複数のインストゥルメントの価格変化を追跡するメカニズムを作成する必要があります。しかし、標準の OnTick関数のみでは、EAが実行されているチャートの新しいティックで呼ばれています。その代わり、開発者は、オーダーブック(市場の深さ)の変化に対応する OnBookEvent関数を使用すると良いでしょう。 OnTickとは異なり、 OnBookEventは、インストゥルメントのオーダーブックの変更で呼び出されますが、 MarketBookAdd関数を使用してサブスクライブします。

このイベントを監視すると、オーダーブックの変更が頻繁に起こり、リソースを大量に消費します。原則として、必要なシンボルにおける変化を監視するにはEAで十分です。一方、オーダーブックの変更には、新しいティックの到着が含まれています。 OnBookEventとは分けて、指定した間隔でOnTimerの呼び出しを設定し、この関数で複数のシンボルの価格の変化を分析することができます。

よって、NewTick、BookEventやTimerイベントに反応する関数で、いくつかの中間モジュールの呼び出しを追加することができ、同時に複数の価格変化を分析することができます。各イベントには、構造体の形で統一された記述を持ち、ストラテジーによって送信されます。構造として適切なイベントを受信したストラテジーは、どちらかに反応、または無視されることがあります。この場合、実際にEAのイベントを開始する関数は不明です。

EAは、新しいティックを受信した場合、 OnTickOnTimer OnBookEventを介して受信されるかどうかは関係ありません。問題は、指定されたシンボルの新しいティックがあるということです。一つのイベントハンドラで、多くのストラテジーを用いることができます。それぞれのストラテジーは、カスタムクラスとして表現される場合、これらのクラスの複数のインスタンスは、ストラテジーの特別なリストに格納することができます。この場合、任意のストラテジーはEventProcessorによって生成された新しいイベントを受信することができます。次の図は、イベントが生成され、送信される方法を示しています。

図1。イベント生成と送信のダイアグラム

今、イベントとしてEAに渡される実際の構造を考えてみましょう。この構造体はMarketEventと呼ばれ、次のように定義されます。

//+------------------------------------------------------------------+
//|この構造は、イベントのタイプを定義する|
//||それが発生した(BarOpenイベントの)時間枠
//+------------------------------------------------------------------+
struct MarketEvent
  {
   ENUM_MARKET_EVENT_TYPE type;     // イベントタイプ
   ENUM_TIMEFRAMES   period;        // チャートイベントのタイムフレーム ( MARKET_EVENT_BAR_OPEN).
   string            symbol;        //イベントが発生したシンボルの名前
  };

構造体のインスタンスを受信した後、取引の決定を行うメソッドは、以下の情報に基づいて行われます。:

  • イベントのタイプ
  • イベントが発生したチャートのタイムフレーム、
  • イベントが発生した名称、

チャートのタイムフレームを分析するイベント(例えばNewTickまたはTimer)が無駄である場合、市場イベント構造のピリオドフィールドは常にPERIOD_CURRENTの値が充填されています。

 

新しい足イベント新ティック&バー検出アルゴリズム

新しいティックや複数の新しい足の形成を追跡するため、このタスクを実装する適切なモジュールクラスを作成する必要があります。これらのモジュールは、CStrategyクラスの内部パートであり、ユーザが直接操作しません。考慮すべき最初のモジュールは、 CTickDetectorクラスです。クラスのソースコードは、利用可能です:

//+------------------------------------------------------------------+
//|                                              NewTickDetector.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Object.mqh>
//+------------------------------------------------------------------+
//| 新しいティック                                                |
//+------------------------------------------------------------------+
class CTickDetector : public CObject
  {
private:
   string            m_symbol;         // 新しいティックのシンボル.
   MqlTick           m_last_tick;      //直前のティック
public:
                     CTickDetector(void);
                     CTickDetector(string symbol);
   string            Symbol(void);
   void              Symbol(string symbol);
   bool              IsNewTick(void);
  };
//+------------------------------------------------------------------+
//|現在のタイムフレームによるコンストラクタのセット|
//| and symbol.                                                      |
//+------------------------------------------------------------------+
CTickDetector::CTickDetector(void)
  {
   m_symbol=_Symbol;
  }
//+------------------------------------------------------------------+
//|プリセットシンボルと時間枠でオブジェクトを作成します。|
//+------------------------------------------------------------------+
CTickDetector::CTickDetector(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//||シンボルの名前を設定します
//|追跡|
//+------------------------------------------------------------------+
void CTickDetector::Symbol(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//||新しいティック時にシンボルの名前を返します。
//|追跡。|
//+------------------------------------------------------------------+
string CTickDetector::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//||与えられたシンボルと時間枠がある場合はtrueを返します 
//| a new tick.                                                      |
//+------------------------------------------------------------------+
bool CTickDetector::IsNewTick(void)
  {
   MqlTick tick;
   SymbolInfoTick(m_symbol,tick);
   if(tick.last!=m_last_tick.last || 
      tick.time!=m_last_tick.time)
     {
      m_last_tick=tick;
      return true;
     }
   return false;
  }

主に使用するメソッドは、IsNewTickです。対象のシンボルの新しいティックが受信された場合、trueを返します。Symbolメソッドを使用して設定されているシンボルを監視します。CTickDetectorクラスは、CObjectの由来です。したがってにCArrayObjの要素として加えることができます。これが必要としているものです。例えば、10のCTickDetectのコピーを作成することができ、それらのそれぞれが独自のシンボルを監視します。CArrayObj型のクラスを参照することにより、新しいティックが形成されたシンボルを見つけ、その後、EAにこの情報を渡すことになります。

既に述べたように、新規ティックに加えて、新たな足の出現で決定することがしばしば必要です。それはCBarDetectorクラスに、このタスクを任せます。それはCTickDetectorと同様の働きをします。CBarDetectorの主なメソッドはIsNewBarです - 前のシンボルメソッドを使用して、指定された新しい足がある場合、このメソッドは、trueを返します。ソースコードは下記です。:

//+------------------------------------------------------------------+
//|                                               NewBarDetecter.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
//+------------------------------------------------------------------+
//||このクラスは、指定の新しいバーの出現を検出
//|記号とピリオド。|
//+------------------------------------------------------------------+
class CBarDetector : public CObject
  {
private:
   ENUM_TIMEFRAMES   m_timeframe;      //新しいバーの形成を追跡する時間枠
   string            m_symbol;         //新しいバーの形成を追跡する記号
   datetime          m_last_time;      //最後の確定足の時間
public:
                     CBarDetector(void);
                     CBarDetector(string symbol,ENUM_TIMEFRAMES timeframe);
   void              Timeframe(ENUM_TIMEFRAMES tf);
   ENUM_TIMEFRAMES   Timeframe(void);
   void              Symbol(string symbol);
   string            Symbol(void);
   bool              IsNewBar(void);
  };
//+------------------------------------------------------------------+
//|現在のタイムフレームによるコンストラクタのセット|
//| and symbol.                                                      |
//+------------------------------------------------------------------+
CBarDetector::CBarDetector(void)
  {
   m_symbol=_Symbol;
   m_timeframe=Period();
  }
//+------------------------------------------------------------------+
//|プリセットシンボルと時間枠でオブジェクトを作成します。|
//+------------------------------------------------------------------+
CBarDetector::CBarDetector(string symbol,ENUM_TIMEFRAMES tf)
  {
   m_symbol=symbol;
   m_timeframe=tf;
  }
//+------------------------------------------------------------------+
//||追跡するため、先の時間枠を設定します。
//|新しいバー。 |
//+------------------------------------------------------------------+
void CBarDetector::Timeframe(ENUM_TIMEFRAMES tf)
  {
   m_timeframe=tf;
  }
//+------------------------------------------------------------------+
//||追跡する上での時間枠を返します。
//|新しいバー。                                                   |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CBarDetector::Timeframe(void)
  {
   return m_timeframe;
  }
//+------------------------------------------------------------------+
//||新しいバーの形成におけるシンボルの名前を設定します
//|追跡|
//+------------------------------------------------------------------+
void CBarDetector::Symbol(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//||新しいバーの形成におけるシンボルの名前を返します。
//|追跡。|
//+------------------------------------------------------------------+
string CBarDetector::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//||与えられたシンボルと時間枠がある場合はtrueを返します 
//|新しいバー。                                              |
//+------------------------------------------------------------------+
bool CBarDetector::IsNewBar(void)
  {
   datetime time[];
   if(CopyTime(m_symbol, m_timeframe, 0, 1, time) < 1)return false;
   if(time[0] == m_last_time)return false;
   return m_last_time = time[0];
  }

 

CPositionMT5 クラス - プラットフォームに依存しないアルゴリズムの基礎

さて、汎用的なEAに最も重要なクラスを分析しましょう。このクラスは、MT5のポジションを管理するためのメソッドが含まれています。技術的な観点からは、非常に簡単です。MT5のポジションの操作に関するEAと、システム機能との間の仲介として動作するラッパークラスです。 - PositionSelect及び PositionGet。しかし、プラットフォームに依存しない重要な機能が実装されています。

慎重に上記のモジュールを分析することにより、1つの取引プラットフォーム(メタトレーダー4またはMT5)にのみ有効である関数を使用していないと結論付けることができます。MQL4とMQL5には最新のバージョンがあるためです。2つのプラットフォームのための関数における具体的なセットは、主にトレーディング・ポジションの管理です。

当然のことながら、すべてのEAは、ポジション管理機能を使用します。しかし、これらの機能のうち、EAのポジションクラスにおいて、単一のインタフェースを使用した場合、メタトレーダー4とMT5の両方でコンパイルすることができ、取引ロボットを開発します。やらなければならないことは、ポジションを持つタスクを実装するクラスを開発です。あるクラスは、MT5から関数を使用しますが、またあるクラスはメタトレーダー4の関数を使用します。しかし、これら両方が、それによって「プラットフォーム独立」を提供し、最終的には同じセットを提供します。

しかし、実際には、プラットフォームに依存しないEAの作成はやや複雑であり、別の記事を必要とします。これは非常に長い記事になり、この記事で説明はしません。

ストラテジーの保有ポジションを表すクラスで二番目の引数は、それぞれのポジションを個別に管理します。取引エンジンは、ポジションのリストを通し、EAのロジックでそれぞれを通します。したがって、最大限の柔軟性を実現します。:EAは、個々のポジションを管理し、複数の取引ポジションをストラテジーのルールで記述することが可能です。もちろん、MT5だけでポジションを持つことができます。しかし、メタトレーダー4プラットフォームに取引エンジンをポートする場合、非常に重要です。

ここでは、このクラスのソースコードを紹介します。単純明快です。

//+------------------------------------------------------------------+
//|                                                  PositionMT5.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "Logs.mqh"
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//|古典的なストラテジーのポジションクラス|
//+------------------------------------------------------------------+
class CPositionMT5 : public CObject
  {
private:
   ulong             m_id;                // ユニークポジション識別子
   uint              m_magic;             //EAの一意の識別子
   ENUM_POSITION_TYPE m_direction;         //ポジションの方向
   double            m_entry_price;       //ポジションのエントリー価格
   string            m_symbol;            //シンボル
   datetime          m_time_open;         //約定日時
   string            m_entry_comment;     //受信コメント
   bool              m_is_closed;         //ポジションが閉じている場合true
   CLog*             Log;                 //ログ
   CTrade            m_trade;             //取引モジュール
public:
                     CPositionMT5(void);
   uint              ExpertMagic(void);
   ulong             ID(void);
   ENUM_POSITION_TYPE Direction(void);
   double            EntryPrice(void);
   string            EntryComment(void);
   double            Profit(void);
   double            Volume(void);
   string            Symbol(void);
   datetime          TimeOpen(void);
   bool              CloseAtMarket(string comment="");
   double            StopLossValue(void);
   bool              StopLossValue(double sl);
   double            TakeProfitValue(void);
   bool              TakeProfitValue(double tp);
  };
//+------------------------------------------------------------------+
//|ポジションの基本的なプロパティの初期化|
//+------------------------------------------------------------------+
void CPositionMT5::CPositionMT5(void) : m_id(0),
                                        m_entry_price(0.0),
                                        m_symbol(""),
                                        m_time_open(0)
  {
   m_id=PositionGetInteger(POSITION_IDENTIFIER);
   m_magic=(uint)PositionGetInteger(POSITION_MAGIC);
   m_direction=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
   m_entry_price=PositionGetDouble(POSITION_PRICE_OPEN);
   m_symbol=PositionGetString(POSITION_SYMBOL);
   m_time_open=(datetime)PositionGetInteger(POSITION_TIME);
   m_entry_comment=PositionGetString(POSITION_COMMENT);
   m_trade.SetExpertMagicNumber(m_magic);
  }
//+------------------------------------------------------------------+
//|ポジションの方向を返します。 |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE CPositionMT5::Direction(void)
  {
   return m_direction;
  }
//+------------------------------------------------------------------+
//||EAの一意のIDを返します。
//|現在ポジションが属すID。 |
//+------------------------------------------------------------------+
uint CPositionMT5::ExpertMagic(void)
  {
   return m_magic;
  }
//+------------------------------------------------------------------+
//|ユニークポジション識別子を返します。                     |
//+------------------------------------------------------------------+
ulong CPositionMT5::ID(void)
  {
   return m_id;
  }
//+------------------------------------------------------------------+
//|ポジションのエントリ価格を返します。                      |
//+------------------------------------------------------------------+
double CPositionMT5::EntryPrice(void)
  {
   return m_entry_price;
  }
//+------------------------------------------------------------------+
//|アクティブポジションのコメントを返します。             |
//+------------------------------------------------------------------+
string CPositionMT5::EntryComment(void)
  {
   return m_entry_comment;
  }
//+------------------------------------------------------------------+
//||現在保有しているポジションが存在するシンボルの名前を返します。
//| ポジション。|
//+------------------------------------------------------------------+
string CPositionMT5::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//|ポジションの約定日時を返します。                               |
//+------------------------------------------------------------------+
datetime CPositionMT5::TimeOpen(void)
  {
   return m_time_open;
  }
//+------------------------------------------------------------------+
//|現在のポジションのストップロスのレベルを返します。|
//||ストップロスレベルが設定されていない場合は、0.0を返します。
//+------------------------------------------------------------------+
double CPositionMT5::StopLossValue(void)
  {
   if(!PositionSelect(m_symbol))
      return 0.0;
   return PositionGetDouble(POSITION_SL);
  }
//+------------------------------------------------------------------+
//||ストップロスのレベルを設定します。
//+------------------------------------------------------------------+
bool CPositionMT5::StopLossValue(double sl)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.Buy(0.0, m_symbol, 0.0, sl, TakeProfitValue(), NULL);
  }
//+------------------------------------------------------------------+
//|現在のポジションのストップロスのレベルを返します。|
//||ストップロスレベルが設定されていない場合は、0.0を返します。
//+------------------------------------------------------------------+
double CPositionMT5::TakeProfitValue(void)
  {
   if(!PositionSelect(m_symbol))
      return 0.0;
   return PositionGetDouble(POSITION_TP);
  }
//+------------------------------------------------------------------+
//||ストップロスのレベルを設定します。
//+------------------------------------------------------------------+
bool CPositionMT5::TakeProfitValue(double tp)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.Buy(0.0, m_symbol, 0.0, StopLossValue(), tp, NULL);
  }
//+------------------------------------------------------------------+
//||現在のポジションを閉じ、決済を設定します
//|「コメント」に等しいコメント|
//+------------------------------------------------------------------+
bool CPositionMT5::CloseAtMarket(string comment="")
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.PositionClose(m_symbol);
  }
//+------------------------------------------------------------------+
//|現在のポジションのロットを返します。                                |
//+------------------------------------------------------------------+
double CPositionMT5::Volume(void)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return PositionGetDouble(POSITION_VOLUME);
  }
//+------------------------------------------------------------------+
//|口座通貨でのポジションの現在の利益を返します。 |
//+------------------------------------------------------------------+
double CPositionMT5::Profit(void)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return PositionGetDouble(POSITION_PROFIT);
  }

見てもわかる通り、クラスの主なタスクは、現在の保有ポジションの特定のプロパティを返すことです。決済、テイクプロフィットとストップロスの変更:また、クラスには、現在のポジションを管理するためのいくつかのメソッドがあります。

 

取引ストラテジープロトタイプ - CStrategyクラス

一般的なタスクを実行するモジュールの数です。また、イベントモデル、意思決定モデル、および典型的なEAの行動アルゴリズムを分析しました。CStrategyクラス - EAの単一モジュールに情報を結合する必要があります。これは、様々なタスクを実行します。それらのいくつかは、次のとおりです。

  • CStrategyに基づいている、すべてのストラテジーの取引の単一シーケンスを配置します。
  • Timer, NewTick, BookEvent, NewBarイベントを識別し、カスタムストラテジーに渡します。
  • MT4モデルに基づいて、相場への容易なアクセスを実現します。
  • 取引モードを管理します。
  • オープン・ポジションについて、情報にアクセスし、それらの統計を受け取ります。

ストラテジーは非常に大きいクラスなので、ここでその完全なソースコードを公開することはありません。その代わりに、クラスの最も重要なアルゴリズムに焦点を当てます。検討する最初のものは、そのイベントモデルです。このモデルは既知のものです。さて、イベントを受信し、特定のストラテジーに配信するプロセスを検討する必要があります。CStrategyは、ヒューチャーストラテジーの親クラスです。結果として、適切なイベントの受信する方法に精通しています:

//+------------------------------------------------------------------+
//|レイヤーストラテジーの基本クラス。 |
//+------------------------------------------------------------------+
class CStrategy : public CObject
  {
   //...
   void              OnTick(void);
   void              OnTimer(void);
   void              OnBookEvent(string symbol);
   virtual void      OnTradeTransaction(const MqlTradeTransaction &trans,
                                        const MqlTradeRequest &request,
                                        const MqlTradeResult &result);
   //...
  };

OnTradeTransactionはバーチャルではありません。これはTick,Timer,BookEventが、イベントストラテジーのレベルで直接扱うことができないことを意味します。その代わりに、CStrategyによって処理されます。したがって、TradeTransactionイベントは仮想で、このクラスでは使用されません。

下記の OnTickの内容は、OnTimer OnBookEventです。:

//+------------------------------------------------------------------+
//||システムイベントの際にストラテジーマネージャによって呼び出されます
//|「新しいティック」。 |
//+------------------------------------------------------------------+
void CStrategy::OnTick(void)
  {
   NewTickDetect();
   NewBarsDetect();
  }
//+------------------------------------------------------------------+
//||システムイベントの際にストラテジーマネージャによって呼び出されます
//| 'OnTimer'.                                                       |
//+------------------------------------------------------------------+
void CStrategy::OnTimer(void)
  {
   m_event.symbol=Symbol();
   m_event.type=MARKET_EVENT_TIMER;
   m_event.period=(ENUM_TIMEFRAMES)Period();
   CallSupport(m_event);
   CallInit(m_event);
   NewTickDetect();
   NewBarsDetect();
  }
//+------------------------------------------------------------------+
//||システムイベントの際にストラテジーマネージャによって呼び出されます
//| 'OnBookEvent'.                                                   |
//+------------------------------------------------------------------+
void CStrategy::OnBookEvent(string symbol)
  {
   m_event.symbol=symbol;
   m_event.type=MARKET_EVENT_BOOK_EVENT;
   m_event.period=PERIOD_CURRENT;
   CallSupport(m_event);
   CallInit(m_event);
   NewTickDetect();
   NewBarsDetect();
  }

前述したように、NewTickイベントは、現在のEAが実行されているインストゥルメントに適用されます。NewTickは、複数のシンボルで使う場合は、新しいティックの特別なハンドラを使用して、新しいバーの出現を追跡する必要があります。このタスクは、決済の方法を適切に行います。:NewBarDetectとNewTickDetect。ソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//||新しい足の出現を検出し、適切に生成
//|EAのイベント。 |
//+------------------------------------------------------------------+
void CStrategy::NewBarsDetect(void)
  {
   if(m_bars_detecors.Total()==0)
      AddBarOpenEvent(ExpertSymbol(),Timeframe());
   for(int i=0; i<m_bars_detecors.Total(); i++)
     {
      CBarDetector *bar=m_bars_detecors.At(i);
      if(bar.IsNewBar())
        {
         m_event.period = bar.Timeframe();
         m_event.symbol = bar.Symbol();
         m_event.type=MARKET_EVENT_BAR_OPEN;
         CallSupport(m_event);
         CallInit(m_event);
        }
     }
  }
//+------------------------------------------------------------------+
//|マルチインストゥルメントの新しいティックを検出します。         |
//+------------------------------------------------------------------+
void CStrategy::NewTickDetect(void)
  {
   if(m_ticks_detectors.Total()==0)
      AddTickEvent(ExpertSymbol());
   for(int i=0; i<m_ticks_detectors.Total(); i++)
     {
      CTickDetector *tick=m_ticks_detectors.At(i);
      if(tick.IsNewTick())
        {
         m_event.period=PERIOD_CURRENT;
         m_event.type=MARKET_EVENT_TICK;
         m_event.symbol=tick.Symbol();
         CallSupport(m_event);
         CallInit(m_event);
        }
     }
  }

このメソッドは、実際には新しいティックやバー(上記のCTickDetectorとCBarDetectorクラス)の検出を行います。これらの検出は、最初のEAが実際に取引される金融商品に設定されています。新しいティックやバーが表示された場合、CallSupportとCallInitメソッドがコールされます。

NewBarsDetectとNewTickDetectなどのイベントハンドラの一般的なアルゴリズムは、次のとおりです。

  1. 新しいイベントの発生を検出します。
  2. 適切なイベントIDとその属性の仕様と市場のイベント構造を満たします。
  3. メソッドに渡されたイベントを使用して、仮想のSupportBuyとSupportSellを呼び出し、CallSupportメソッドを呼び出します。
  4. 同様に、そのメソッドに渡されたイベントを使用して、仮想のInitBuyとInitSellを呼び出すCallInitメソッドを呼び出します。

コントロールが特定のストラテジーに渡される方法を理解するため、CallInitとCallSupportメソッドを熟読する必要があります。CallInitのソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//||ポジションを持つロジックのコール
//|これを制限しない状態                        |
//+------------------------------------------------------------------+
void CStrategy::CallInit(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_STOP)return;
   if(m_trade_state == TRADE_WAIT)return;
   if(m_trade_state == TRADE_NO_NEW_ENTRY)return;
   SpyEnvironment();
   if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_BUY_ONLY)
      InitBuy(event);
   if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_SELL_ONLY)
      InitSell(event);
  }

このメソッドは、m_stateモジュール(最初の記事のCTradeStateクラスに記載)から、与えられた新しいポジションの初期化メソッドを呼び出すことが可能であるか否かを判断します。取引状態が取引を禁止している場合は、これらのメソッドは呼び出されません。それ以外の場合は、メソッドの呼び出しを引き起こしたイベントの表示をコールします。

CallSupportは、同様に動作しますが、そのロジックは多少異なります。ソースコードは次のとおりです。

//+------------------------------------------------------------------+
//||ポジションのメンテナンス・ロジックを呼び出します
//|状態はTRADE_WAITと等しくありません。 |
//+------------------------------------------------------------------+
void CStrategy::CallSupport(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_WAIT)return;
   SpyEnvironment();
   for(int i=ActivePositions.Total()-1; i>=0; i--)
     {
      CPosition *pos=ActivePositions.At(i);
      if(pos.ExpertMagic()!=m_expert_magic)continue;
      if(pos.Direction()==POSITION_TYPE_BUY)
         SupportBuy(event,pos);
      else
         SupportSell(event,pos);
      if(m_trade_state==TRADE_STOP && pos.IsActive())
         ExitByStopRegim(pos);
     }
  }

同様に、このメソッドは、EAの現在の取引状況を受信します。ポジション管理ができた場合は、このメソッドは、現在開いているすべてのポジションを介して検索を開始します。これらのポジションはCPositionクラスによって表されています。各ポジションに受信されたアクセスの方法は、現在のEAのマジックナンバーと、そのマジックナンバーを比較します。一致するもの見つかると、適切なポジション管理メソッドが呼び出されます:SupportBuyは買いポジション、SupportSellは売りポジションです。

 

結論

カスタムストラテジーのための豊富な関数を紹介し、それを通してCStrategyクラス、主要なモジュールを検討しました。このクラスから派生したストラテジーは、価格とイベントで動作します。また、ストラテジーはCStrategyクラスから継承された、取引の統一シーケンスを取得します。この場合、別個のモジュールとして開発することができます。このソリューションは、取引アルゴリズムの開発労力を大幅に削減します。

次の記事では「ユニバーサルEA:カスタムストラテジーおよび補助トレードクラス(その3)」は、記述されているアルゴリズムに基づいて、ストラテジー開発のプロセスについて説明します。

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

添付されたファイル |
strategyarticle.zip (100.23 KB)
ユニバーサルEA:カスタムストラテジーと補助トレードクラス(その3) ユニバーサルEA:カスタムストラテジーと補助トレードクラス(その3)
この記事では、ストラテジーの取引エンジンのアルゴリズムを分析していきます。シリーズの3番目の部分は、このアプローチを使用して、特定の取引ストラテジーを開発する方法の詳細な分析があります。特別な注意が補助アルゴリズムに必要です - EAは、従来のインデクサーを使用して、システムとデータへのアクセスをログに記録します(Close[1]、Open[0]など)
エキスパートアドバイザーを使って自分ルールでシグナルをコピーするには? エキスパートアドバイザーを使って自分ルールでシグナルをコピーするには?
シグナルを購読していると、このような状況が起こることがあります。貴方の取引口座はレバレッジが1:100であるのに、プロバイダのレバレッジは1:500かつ最小ロットでトレードを行っていて、貴方の取引残高はほぼ同じ。そしてこの時コピー係数は10%から15%になるという状況です。この記事では、このような場合にどのようにコピー係数を上げたら良いかをお話しします。
MQL5の料理本 - 移動チャネルのプログラミング MQL5の料理本 - 移動チャネルのプログラミング
本稿では、等間隔チャネルシステムのプログラミング法について取り扱っています。等間隔チャネル構築の細部まで本稿で紹介します。チャネル分類について説明したのち、移動チャネルのプログラミング法について提案します。コード実行にはオブジェクト指向型プログラムを使用しています。
ユニバーサルEA:戦略トレードモード(その1) ユニバーサルEA:戦略トレードモード(その1)
EAの開発者は、プログラミングのスキルに関係なく、信頼性の高い取引プロセスを整理するため、同じタスクとアルゴリズムの問題に直面しています。この記事では、これらのタスクの解決に着手し、トレードアイデアを記述するための便利なCStrategyエンジンの可能性を説明します。