クロスプラットフォームEA: オーダーマネージャ

Enrico Lambino | 6 6月, 2017


目次

イントロダクション

以前の記事で説明したように、MQL4 ソースファイルをコピーしても、 MQL5 コンパイラーを使用してコンパイルすることはできません。 両者の最も顕著な相違点の1つは、2つのプラットフォームがトレード操作の実行に影響を与えるということです。 この記事では、COrderManager クラスの作成について説明します。 上記のクラスは、他の補助クラスと同様に、主にEAのトレードの実行だけでなく、EAによってエントリーされたトレードを管理します。


目的

この記事のオーダーマネージャは、次の操作を実行できます。

  1. ロットサイズ計算
  2. ストップロスとテイクプロフィット
  3. エントリに必要なその他のパラメータ (テイクプロフィット、コメント、マジック)
  4. オーダーを送信する前の前提条件
  5. オーダーヒストリーの管理

ロットサイズ計算は、多くの場合、次のトレードに最適なサイズを計算する方法が数多くあります (トレード戦略によっては、オブジェクトメンバに任されます)。 ストップロスとテイクプロフィットのレベルの計算についても同様です。

テイクプロフィット、オーダーコメント、マジックナンバーなどのエントリーするために必要なその他のパラメータには、複雑さがなく、オーダーマネージャオブジェクト自体による処理が最も適しています。

特定の前提条件は、EAによって相場にエントリーする前に必要です。 相場の状況、時間の制約に基づいて、任意の時点でのアクティブなトレードの最大数だけでなく、その全体の有効期限 (の間にオープンするトレードの最大数) と同様に、トレードのシグナルが含まれていますOnInit及びOnDeinit)。 オーダーマネージャの場合、最後の2つの前提条件は、オーダーが最終的に相場に送信される前の状態として含まれます。 EAの操作中に重複するトレードが発生しないように実装されます。 一方、他の前提条件は、オーダーマネージャの外部にある特定のコンポーネントに任せることができます。


基本実装

この記事のシリーズで説明している他のクラスオブジェクトと同様に、MQL4 と MQL5 の間の共通グラウンドは、基本クラスの中で実装する必要があります。 基本クラスの開発では、トレードのリクエストの処理については、次を理解する必要があります:

  1. トレードリクエストの送信方法が異なる
  2. トレードアクションの文書が異なる
  3. MQL5 には、MQL4 にない関数があります。

MQL4 と MQL5 の間で異なるコンポーネントがいくつかあります。 2つのプラットフォームのドキュメントに示されているように、ordersend 関数 (MQL4MQL5) について考えてみます。

(MQL4)

int  OrderSend(
   string   symbol,              //シンボル
   int      cmd,                 //操作
   double   volume,              //ボリューム
   double   price,               //価格
   int      slippage,            //すべり
   double   stoploss,            //ストップロス
   double   takeprofit,          //利確
   string   comment=NULL,        //コメント
   int      magic=0,             //マジックナンバー
   datetime expiration=0,        //保留中のオーダーのテイクプロフィット
   color    arrow_color=clrNONE  //色
       );

(MQL5)

bool  OrderSend(
   MqlTradeRequest&  request,      //クエリ構造
   MqlTradeResult&   result        //答えの構造
       );

MQL4 関数には、より簡単なメソッドがあります。 一方、MQL5 関数は、もう少し複雑ですが、リクエストと結果のデータ (struct) を含む2つのパラメータだけです。 これに関しては、MQL4、特に CExpertTrade と CExpertTradeX クラスに MQL5 標準ライブラリの特定のコンポーネントのインポートに関する以前の記事で主に考察しました。 したがって、オーダーマネージャは、クラスを使用して、トレードリクエストをするときに2つの言語間の互換性を補完します。

もう一つの側面は、トレードの決済またはオーダーの削除は、MT4とMT5で処理されるメソッドです。 保留中のオーダーが削除される方法に大差はありませんが、オーダーメソッドには大きな違いがあります。 MQL4 では、OrderClose関数を呼び出すことによって、決済します。 MT5 では、 PositionClose関数を呼び出したり、現在のポジションと現在のポジションの反対側に同じボリュームを持つトレードリクエストを発行することにより、同じ効果が実現されます。

MT5 では、すべてのトレードアクションが文書化されています。 トレードエントリ、変更、または決済のいずれかにかかわらず、そのアクションは記録を残し、アクションに関するデータはEAがアクセスできます。 MT4の場合、それはありません。 たとえば、チケット id が保留中のオーダーに割り当てられ、決済される前にそのトリガー価格に達して相場のオーダーになった場合でも、そのオーダーの有効期間中に同じ id が使用されることがあります。 ある特定のトレードのプロセスを見るためには、EAおよびジャーナルログファイルを見なければなりません。 さらに、ログファイルは人間によって読み取られることを意図しており、EAが情報にシンプルにアクセスできるようには MQL4ではなっていません。

MQL5 には、MQL4 では利用できない関数がいくつかあります。 この例として、オーダーのフィリングタイプがあります。 MQL5 には、オーダーに対して次のボリュームフィリングオプションがあります。

  • ORDER_FILLING_FOK –リクエストされたボリュームでエントリーできない場合は、オーダーをキャンセルします。
  • ORDER_FILLING_IOC –リクエストされたボリュームでエントリーできない場合は、利用可能な最大ボリュームを使用し、残りのボリュームをキャンセルします。
  • ORDER_FILLING_RETURN –リクエストされたボリュームをエントリーできない場合は、利用可能な最大ボリュームを使用します。 残りのボリュームのオーダーはとどまります。

MT4では、トレードリクエストはエントリーされているかどうか (キャンセル) であり、本質的に ORDER_FILLING_FOK と等価であり、他の2つのオプションは使用できません。 ただし、フィリングポリシーは、リクエストされたボリュームが相場で使用可能なボリュームを超えた場合にのみ実装され、低リスクおよび/低残高の設定では発生しません。 MQL4では特定の取引における最大ロットを取得することができないので、ORDER_FILLING_IOC と ORDER_FILLING_RETURN に類するものを実装するのは困難です。の したがって、MQL4 と MQL5 の間の互換性を確保するために、ORDER_FILLING_FOK は使用できる唯一のフィリングオプションとなります ( MT5 のデフォルトでもあります)。 一方、EAは、ブローカーによって設定された任意のトレード/コントラクトの最大許容ロットである SYMBOL_VOLUME_MAX を超えるトレードリクエストのロットサイズを計算するイベントがあります。 MT5は、自動的にトレードにトレードを分割することによって対処しますが、この関数は、MT4では利用できません 。 したがって、クロスプラットフォームのEAには、事前に (できればトレードボリュームを計算した後) オーダーマネージャを使用して相場参入のトレードのリクエストを送信する前にチェックする方が良いでしょう。

次の図は、オーダーマネージャがトレードのエントリをどのように実装するかを大まかに示しています。

エントリの図

図に示すように、このメソッドは、操作に必要なデータを準備することから始まります。 ポジションがエントリーされ、前提条件が満たされている場合、このメソッドはオーダーのエントリに進みます。 以外の場合は、このプロセスが決済します。 トレードリクエストを送信する前に、必要な値を計算する必要があります。 リクエストが成功すると、結果がチェックされ、COrderの新しいインスタンスが作成され、現在のオーダー/ポジション (m_orders) の一覧に追加されます。

純粋な計算のみを実行するメソッドは、基本クラス内にあります。 2つの言語間で異なる関数を呼び出すメソッドは、言語固有のクラスに拡張します。 ただし、このメソッドでは、操作の実行メソッドにはほとんど違いがありません。 したがって、基本クラスのメソッドは純粋にバーチャルであり、実装は2つのバージョン間で個別に見つけることができます。 基本クラスのメソッドの実装を次のようにします。

COrder* COrderManagerBase::TradeOpen(const string,ENUM_ORDER_TYPE)
  {
   return NULL;
      }

トレードボリュームの計算

前述したように、次のトレードのトレード数量の計算は、オーダーマネージャのクラスメンバになる、別のクラスオブジェクトに任せることをお勧めします。 このメソッドは、MQL5 標準ライブラリのエキスパートライブラリでも使用できます。 トレードのボリュームを計算するメソッドである LotSizeCalculate メソッドのコードを示します。

double COrderManagerBase::LotSizeCalculate(const double price,const ENUM_ORDER_TYPE type,const double stoploss)
  {
   if(CheckPointer(m_moneys))
      return m_moneys.Volume(m_symbol.Name(),0,type,stoploss);
   return m_lotsize;
      }

このメソッドは、CMoneys のインスタンスへのポインタをチェックします。 (COrderのインスタンスのコンテナであるのと同じメソッド) オーダーマネージャが使用する 資金管理オブジェクトのコンテナです。 この資金管理オブジェクトについては、別の記事で説明します。 少なくともこの時点では、サイズの計算を扱う別のコンポーネントがあり、計算されたサイズが有効であることが分かっていれば十分です。 オーダーマネージャに 資金管理インスタンスが提供されなかった場合、オーダーマネージャは、クラスメンバ m_lotsize を通じてデフォルトのサイズを使用します。

ストップロスとテイクプロフィットの計算

ストップロスとテイクプロフィットの計算は、それぞれ StopLossCalculate と TakeProfitCalculate のメソッドによって達成されます。 次のコードスニペットは、各メソッドがオーダーマネージャでどのように実装されるかを示しています。

double COrderManagerBase::StopLossCalculate(const ENUM_ORDER_TYPE type,const double price)
  {
   if(CheckPointer(m_main_stop))
      return m_main_stop.StopLossTicks(type,price);
   return 0;
      }
double COrderManagerBase::TakeProfitCalculate(const ENUM_ORDER_TYPE type,const double price)
  {
   if(CheckPointer(m_main_stop))
      return m_main_stop.TakeProfitTicks(type,price);
   return 0;
      }

ストップレベルの計算は、オーダーマネージャーのメンバーになる別のクラスオブジェクトに任されます (別の記事で説明します)。 stop オブジェクトには、MT4とMT5の個別の実装もあります。 ただし、stop オブジェクトへのポインタがオーダーマネージャに渡されない場合、計算された ストップロス またはテイクプロフィットはデフォルトでゼロ (sl/tp なし) になります。

ポジションを閉じる

次の図は、オーダーマネージャがポジションを決済してオーダーを削除するメソッドを大まかに示しています。

決済の図

図に示すように、このメソッドは、COrderのインスタンスへのポインタが有効かどうかを最初にチェックします。 その後、決済リクエストを処理するために必要なシンボルおよびトレードオブジェクトへの正しいインスタンスの取得に進みます。 その後、その型に応じてオーダーを削除または決済します。 オーダーの決済/削除が成功すると、COrderインスタンスはアクティブなオーダーのリストからオーダーマネージャの履歴オーダーのリストに移動します (アーカイブされます)。 このメソッドは、COrderオブジェクトを閉じたようにマークするフラグも設定します。

設定の検証

オーダーマネージャの設定の検証は、クラスの validate メソッドを呼び出すことによって実現されます。 このメソッドのコードは、次のスニペットに示されています。

bool COrderManagerBase::Validate(void) const
  {
   if(CheckPointer(m_moneys)==POINTER_DYNAMIC)
     {
      if(!m_moneys.Validate())
         return false;
     }
   if(CheckPointer(m_stops)==POINTER_DYNAMIC)
     {
      if(!m_stops.Validate())
         return false;
     }
   return true;
      }

このコードは、MQL5 標準ライブラリ内のクラスでよく見られるValidationSettingsメソッドに似ています。 オブジェクトメンバの validate メソッドを呼び出し、オブジェクトの1つが検証に失敗するたびに false を返します (最終的には、エキスパートの INIT_FAILED 関数が失敗するか、または戻ります)。 このメソッドは、EAの初期化関数の実行中に呼び出されます。

トレード数のカウント

トレードの合計は、オーダーマネージャが今までにエントリーしたトレードの合計数です。 オーダーの合計は、現在のトレードの数を参照します。オーダー履歴の合計は、オーダーマネージャ自身のオーダーの集計に含まれているものを参照します。 よって:

int COrderManagerBase::OrdersTotal(void) const
  {
   return m_orders.Total();
      }
int COrderManagerBase::OrdersHistoryTotal(void) const
  {
   return m_orders_history.Total();
      }
int COrderManagerBase::TradesTotal(void) const
  {
   return m_orders.Total()+m_orders_history.Total()+m_history_count;
      }
両方のケースで、MT4の標準的な概念を使用しています。 たとえば、MQL4 実装では、次のように TradeOpen メソッドの最初の2行が表示されます。
int trades_total =TradesTotal();
  int orders_total = OrdersTotal();

ここでは、OrdersTotal は、MQL4 と MQL5 の両方で検出された OrdersTotal 関数ではなく、クラスのネイティブメソッドを参照していることに注意してください。 代わりにネイティブ関数を使用する場合は、関数の名前の前にスコープ解決演算子を指定して OrdersTotal を呼び出す必要があります。

int orders_total = ::OrdersTotal();

COrderインスタンスのアーカイブ

オーダーマネージャは、トレードの独立した集計を維持するので、MT4とMT5が (両方とも互換性があります) すでにヒストリーに属しているようにCOrderのインスタンスをフラグするメソッドを持っている必要があります。 このオーダーは、アクティブに応じて、corders のインスタンスである m_orders と m_orders_history に格納されます。 結果として、両方のバージョンについて、クロスプラットフォームのEAは、特定のオーダーやトレードに対して相場にあるかどうかを確認する必要があります。

2つのプラットフォームは、相場でエントリーされたトレードを文書化するメソッドで異なるので、オーダーマネージャは、トレードの独立した記録を維持する必要があります。 オーダーの開始時に、COrderインスタンスが作成され、最終的に m_orders に追加されます。 ポジションが決済されるとすぐに、オーダーマネージャはCOrderインスタンスを m_orders_history に移動する必要があります。 両方のバージョンで使用されるクラスの ArchiveOrder メソッドを以下に示します。

bool COrderManagerBase::ArchiveOrder(COrder *order)
  {
   return m_orders_history.Add(order);
      }

MQL4 固有の実装

ポジションを開く

次のコードスニペットは、CorderManagerBase の MQL4-固有クラスの孫の TradeOpen メソッドを示しています。

COrder* COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type)
  {
   int trades_total = TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol = m_symbol_man.Get(symbol);
   if (!CheckPointer(m_symbol))
      return NULL;
   if(!IsPositionAllowed(type))
      return NULL;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      ENUM_ORDER_TYPE ordertype = type;
      double price=PriceCalculate(ordertype);
      double sl=0,tp=0;
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
        {
         sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price);
         tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price);
        }
      double lotsize=LotSizeCalculate(price,type,sl);
      ulong ticket = SendOrder(type,lotsize,price,sl,tp);
      if (ticket>0)
      {
         if (OrderSelect((int)ticket,SELECT_BY_TICKET))
            return m_orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(),(ENUM_ORDER_TYPE)::OrderType(),::OrderLots(),::OrderOpenPrice());
      }            
     }
   return NULL;
      }

この関数は、2つのパラメータ、シンボルまたはインストゥルメントの名前、および開くオーダーの種類を受け取ります。 リクエストの処理に必要な値 (トレード合計、オーダー合計、およびシンボルオブジェクト) を取得することから始まります。

2つの前提条件 (最大オーダーと最大トレード) を満たす上で、このメソッドはストップロス、テイクプロフィットの計算に進み、特殊オブジェクトのメンバーを使用します。 最後に、オーダーを送信し、操作が成功した後、COrderの新しいインスタンスを作成し、アクティブなオーダーの一覧 (オーダーマネージャ内) に格納します。

ポジションを閉じる

次のコードスニペットは、COrderManagerBase の MQL4-固有クラスの子の CloseOrder メソッドを示しています。

bool COrderManager::CloseOrder(COrder *order,const int index=-1)
  {
   bool closed=true;
   if(CheckPointer(order)==POINTER_DYNAMIC)
     {
      if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),order.Symbol())!=0)
         m_symbol=m_symbol_man.Get(order.Symbol());
      if(CheckPointer(m_symbol))
         m_trade=m_trade_man.Get(order.Symbol());
      if(order.Volume()>0)
        {
         if(order.OrderType()==ORDER_TYPE_BUY || order.OrderType()==ORDER_TYPE_SELL)
            closed=m_trade.OrderClose((ulong)order.Ticket());
         else
            closed=m_trade.OrderDelete((ulong)order.Ticket());
        }
      if(closed)
        {
         int idx = index>=0?index:FindOrderIndex(GetPointer(order));
         if(ArchiveOrder(m_orders.Detach(idx)))
           {
            order.Close();
            order.Volume(0);
           }
        }
     }
   return closed;
  }

後で説明しますが、MQL4は、主に1つのマージンモード (ヘッジ) のみを持つため、MQL5よりもはるかに簡単です。 そして、仮にヘッジがブローカーによって無効にされている場合でも、オーダーを閉じるプロセスは同じようになっています。

この関数は、2つのパラメータ、order オブジェクト、およびアクティブなオーダー/ポジションのリストのインデックスを受け取ります。 COrderオブジェクトへのポインタが有効な場合は、CExpertTradeX とCSymbolInfoの正しいインスタンスを取得してオーダーを閉じ、適切な関数を呼び出してトレードターミナルの履歴に配置します。

ポジションが決済したら、COrderオブジェクトを更新する必要があります。 最初に、アクティブなオーダーの一覧から削除され、履歴の一覧の最後に転送されます。 次に、オブジェクトが閉じられたとしてフラグが設定され、ボリュームがゼロになります。

クラスのメソッドの2番目の引数は、省略可能なパラメータ (index) を受け取ります。 オーダー配列内のCOrderインスタンスのインデックスが事前に認識されている場合、リクエストの処理を高速化します。 (通常は、オーダーが頻繁に繰り返される必要があるためです)。 インデックスが不明な場合、メソッドは1つの引数だけで呼び出すことができ、オーダー配列のCOrderインスタンスのポジションを検索するための別のクラスメソッド FindOrderIndex を呼び出します。


MQL5 固有の実装

ポジションを開く

次のコードスニペットは、COrderManagerBase の MQL5-固有クラスの TradeOpen メソッドを示しています。

COrder* COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type)
  {
   double lotsize=0.0,price=0.0;
   int trades_total =TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol=m_symbol_man.Get(symbol);
   if(!IsPositionAllowed(type))
      return NULL;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      price=PriceCalculate(type);
      lotsize=LotSizeCalculate(price,type,m_main_stop==NULL?0:m_main_stop.StopLossCalculate(symbol,type,price));
      if (SendOrder(type,lotsize,price,0,0))
         return m_orders.NewOrder((int)m_trade.ResultOrder(),m_trade.RequestSymbol(),    (int)m_trade.RequestMagic(),m_trade.RequestType(),m_trade.ResultVolume(),m_trade.ResultPrice());
     }      
   return NULL;
      }

ご覧の通り、この実装は MQL4 と大差ありません。 ただし、このコードでは、ストップロスとテイクプロフィットレベルの値を取得する必要はありません。 その理由は、MT5でストップレベルがどのように動作するかは、そのMT4とは異なります。 MQL5での経験があるプログラマは、このライブラリでは、MQL4 のブローカーベースのストップの対応として保留中のオーダーを使用することを知っています。

ポジションを閉じる

次のコードスニペットは、COrderManagerBase の MQL5-固有クラス子孫の CloseOrder メソッドを示しています。

bool COrderManager::CloseOrder(COrder *order,const int index=-1)   {    bool closed=true;    COrderInfo ord;    if(!CheckPointer(order))       return true;    if(order.Volume()<=0)       return true;    if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),order.Symbol())!=0)       m_symbol=m_symbol_man.Get(order.Symbol());    if(CheckPointer(m_symbol))       m_trade=m_trade_man.Get(order.Symbol());    if(ord.Select(order.Ticket()))    {       closed=m_trade.OrderDelete(order.Ticket());    }      else      {       ResetLastError();       if(IsHedging())       {          closed=m_trade.PositionClose(order.Ticket());       }         else         {          if(COrder::IsOrderTypeLong(order.OrderType()))             closed=m_trade.Sell(order.Volume(),0,0,0);          else if(COrder::IsOrderTypeShort(order.OrderType()))             closed=m_trade.Buy(order.Volume(),0,0,0);         }      }    if(closed)      {

      if(ArchiveOrder(m_orders.Detach(index)))         {          order.Close();          order.Volume(0);         }      }    return closed;   }

MQL5 のオーダーまたはポジションを閉じる際には、マージンモード (ネットまたはヘッジ) を考慮する必要があります。 しかし、最初に、閉じられる項目が MQL5 オーダーまたは MQL5 ポジションであるかどうかを特定する必要があります。 OrderSelect と HistoryOrderSelect 関数を使用して実現することはできますが、このメソッドに必要なコードを短くしてプロセスを容易にするために、MQL5 標準ライブラリの COrderInfo クラスを使用することができます。

MQL5 でのオーダーはトレードリクエストの結果として、トレードまたは一連のトレード (MT4の相場オーダーに相当する) になります。 ただし、リクエストが即時ではない場合は、(MT4ではなく、オーダーが保留中である場合) 保留中のオーダーを参照します。 ここで、この項目を決済で区別するために、COrderInfo を使用してアイテムが保留中のオーダーであるかどうかを確認し、保留中の場合は、そのオーダーが削除されます。 COrderInfo によって選択に失敗すれば、約定したオーダーまたはポジションであることが確かになります。 ヘッジモードの場合、ポジションは PositionClose 関数を使用して閉じられます。 それ以外の場合は、ネットモードでは、同等のボリュームと反対のポジションをエントリーしてポジションを中和します。

COrderのインスタンスの作成

オーダーマネージャによるエントリーと決済の方法を見てきました。 以前の記事では、両方のトレーディングプラットフォームと互換性を持たせるために、CExpertTrade クラスをどのように変更できるかについても考察しました。 現在、2つのトレーディングプラットフォームにおいて ストップロスとテイクプロフィットの実装に違いがあり、オーダーマネージャによってかろうじて処理されるだけです。 残りのプロセスは、COrders の NewOrder メソッドで呼び出されるCOrderインスタンスの初期化時に設定されます。 次のコードは、COrdersBase の Init メソッドを示しています。

COrder* COrdersBase::NewOrder(const ulong ticket,const string symbol,const int magic,const ENUM_ORDER_TYPE type,const double volume,const double price)
  {
   COrder *order=new COrder(ticket,symbol,type,volume,price);
   if(CheckPointer(order)==POINTER_DYNAMIC)
      if(InsertSort(GetPointer(order)))
      {  
         order.Magic(magic);
         order.Init(GetPointer(this),m_stops);
         return order;
      }  
   return NULL;
      }

ご覧のとおり、COrderクラスの Init メソッドは、特定のカスタムオブジェクト (CStops) を2番目の引数として受け取ります。 これは、ストップオブジェクト (前に示した m_main_stop オブジェクトなど) のコンテナです。 このクラスオブジェクトは別の記事で紹介します。

ポジションの変更

オーダーマネージャが約定したポジションを修正する方法についてまだ示していません。 他のストップオブジェクト (CStop および CorderStop) 別の記事で議論しましょう。 オブジェクトは、ポジションのストップレベルの更新または変更、およびそれらが属するコーダーオブジェクトの調整を担当します。

MT4では、保留中のオーダーエントリー価格を任意回数で変更できます。 MT5 ではそうではありません。 今回、MQL5 バージョンは制限コンポーネントであるため、MQL5 標準を採用します。 保留中のオーダーを変更するには、既存の保留中のオーダーを削除し、更新されたプロパティを持つ新しいものを作成する必要があります。

例として、この記事シリーズでまでに説明したクラスオブジェクトを使用してEAを実装します。 メタエディタ でエキスパートアドバイザのソースファイルを作成した後、ライブラリへの参照を開始します。

# include「MQLx\Base\OrderManager\OrderManagerBase.mqh」

このステートメントでは、"<" と ">" ではなくクオートを使用していることに注意してください。 EAのソースコードファイルと同じディレクトリにライブラリを配置します。

この EA では、プログラム内でグローバルに宣言する必要がある少なくとも3つのポインタ (COrderManager、CSymbolInfo、および CSymbolManager) をリクエストします。

COrderManager *order_manager;
CSymbolManager *symbol_manager;
CSymbolInfo *symbol_info;

この関数の下では、特に CSymbolInfo のインスタンスに対して、初期化時に割り当てる特定の名前を必要とする3つのポインタを初期化する必要があります。

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

OnDeinit の下で、3つのポインタを削除する必要があるので、メモリリークされません (少なくともトレードプラットフォーム内では):

void OnDeinit(const int reason)
  {
//---
   delete symbol_info;
   delete symbol_manager;
   delete order_manager;
  }

OnTick の下で、実際の戦略を実装する必要があります。 この例では、新しいバーを検出する簡単なメソッドを使用します (チャート上のバーの数をチェックします)。 以前のバーカウントは、静的変数 (またはグローバル変数) に格納することができます。 同じことが、以前のEAが取った前の方向のポジションを格納に使用する変数について言えます。 しかし、チャート上のバーを数えるために使用される関数は、2つのプラットフォーム間で異なるため、この点で実装を分割する必要があります:

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

MQL4 バージョンでは、 Barsという名前の定義済み変数 (関数iBarsの呼び出し) を使用するだけです。 一方、MQL5 バージョンでは、 Bars関数の呼び出しを使用しています。

次のコードスニペットは、エキスパートの実際の動作を実装しています。 前と現在のバー間の不一致が検出された場合、エキスパートは、シンボル (CSymbolInfo) の速度を初期化して、さらなる操作から始めます。 決済するべき前のトレードがあるかどうか調査します。 見つかった場合、EAがポジションを閉じて、前の方向に基づいて、別のエントリの処理に進みます。 このコードは、足が更新されることによって決済します。 

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で同じコードを使用できるように、これまでに行ったコードをヘッダーファイルで移動し、メインソースファイル (MQL4 および MQL5 バージョンの場合) で参照します。 ソースファイル (ターゲットプラットフォームに応じて URtest_ordermanager.mq4 または URtest_ordermanager.mq5) の両方に、メインヘッダーファイルを参照するコードがあります。

#include "test_ordermanager.mqh"

次の表は、MT4でEAを実行した結果と、MT5のヘッジモードを示しており、それぞれのストラテジーテスターレポートです。 簡潔にするために、最初の10トレードだけ記事に載せました。 (完全なレポートは、この記事の最後の zip パッケージにあります)。

MT4:

# Time Type Order Size Price S / L T / P Profit Balance
1 2017.01.02 00:00 buy 1 0.10 1.05102 0.00000 0.00000
2 2017.01.02 01:00 close 1 0.10 1.05172 0.00000 0.00000 7.00 10007.00
3 2017.01.02 01:00 sell 2 0.10 1.05172 0.00000 0.00000
4 2017.01.02 02:00 close 2 0.10 1.05225 0.00000 0.00000 -5.30 10001.70
5 2017.01.02 02:00 buy 3 0.10 1.05225 0.00000 0.00000
6 2017.01.02 03:00 close 3 0.10 1.05192 0.00000 0.00000 -3.30 9998.40
7 2017.01.02 03:00 sell 4 0.10 1.05192 0.00000 0.00000
8 2017.01.02 04:00 close 4 0.10 1.05191 0.00000 0.00000 0.10 9998.50
9 2017.01.02 04:00 buy 5 0.10 1.05191 0.00000 0.00000
10 2017.01.02 05:00 close 5 0.10 1.05151 0.00000 0.00000 -4.00 9994.50
11 2017.01.02 05:00 sell 6 0.10 1.05151 0.00000 0.00000
12 2017.01.02 06:00 close 6 0.10 1.05186 0.00000 0.00000 -3.50 9991.00
13 2017.01.02 06:00 buy 7 0.10 1.05186 0.00000 0.00000
14 2017.01.02 07:00 close 7 0.10 1.05142 0.00000 0.00000 -4.40 9986.60
15 2017.01.02 07:00 sell 8 0.10 1.05142 0.00000 0.00000
16 2017.01.02 08:00 close 8 0.10 1.05110 0.00000 0.00000 3.20 9989.80
17 2017.01.02 08:00 buy 9 0.10 1.05110 0.00000 0.00000
18 2017.01.02 09:00 close 9 0.10 1.05131 0.00000 0.00000 2.10 9991.90
19 2017.01.02 09:00 sell 10 0.10 1.05131 0.00000 0.00000
20 2017.01.02 10:00 close 10 0.10 1.05155 0.00000 0.00000 -2.40 9989.50


mt5 (ネット):

オープンタイム Order Symbol Type Volume Price S / L T / P Time State Comment
2017.01.02 00:00:00 2 EURUSD buy 0.10/0.10 1.05140 2017.01.02 00:00:00 filled
2017.01.02 01:00:00 3 EURUSD sell 0.10/0.10 1.05172 2017.01.02 01:00:00 filled
2017.01.02 01:00:00 4 EURUSD sell 0.10/0.10 1.05172 2017.01.02 01:00:00 filled
2017.01.02 02:00:00 5 EURUSD buy 0.10/0.10 1.05293 2017.01.02 02:00:00 filled
2017.01.02 02:00:00 6 EURUSD buy 0.10/0.10 1.05293 2017.01.02 02:00:00 filled
2017.01.02 03:00:00 7 EURUSD sell 0.10/0.10 1.05192 2017.01.02 03:00:00 filled
2017.01.02 03:00:00 8 EURUSD sell 0.10/0.10 1.05192 2017.01.02 03:00:00 filled
2017.01.02 04:00:00 9 EURUSD buy 0.10/0.10 1.05234 2017.01.02 04:00:00 filled
2017.01.02 04:00:00 10 EURUSD buy 0.10/0.10 1.05234 2017.01.02 04:00:00 filled
2017.01.02 05:00:00 11 EURUSD sell 0.10/0.10 1.05151 2017.01.02 05:00:00 filled
2017.01.02 05:00:00 12 EURUSD sell 0.10/0.10 1.05151 2017.01.02 05:00:00 filled
2017.01.02 06:00:00 13 EURUSD buy 0.10/0.10 1.05230 2017.01.02 06:00:00 filled
2017.01.02 06:00:00 14 EURUSD buy 0.10/0.10 1.05230 2017.01.02 06:00:00 filled
2017.01.02 07:00:00 15 EURUSD sell 0.10/0.10 1.05142 2017.01.02 07:00:00 filled
2017.01.02 07:00:00 16 EURUSD sell 0.10/0.10 1.05142 2017.01.02 07:00:00 filled
2017.01.02 08:00:00 17 EURUSD buy 0.10/0.10 1.05169 2017.01.02 08:00:00 filled
2017.01.02 08:00:00 18 EURUSD buy 0.10/0.10 1.05169 2017.01.02 08:00:00 filled
2017.01.02 09:00:00 19 EURUSD sell 0.10/0.10 1.05131 2017.01.02 09:00:00 filled
2017.01.02 09:00:00 20 EURUSD sell 0.10/0.10 1.05131 2017.01.02 09:00:00 filled
2017.01.02 10:00:00 21 EURUSD buy 0.10/0.10 1.05164 2017.01.02 10:00:00 filled


mt5 (ヘッジ):














オープンタイム Order Symbol Type Volume Price S / L T / P Time State Comment
2017.01.02 00:00:00 2 EURUSD buy 0.10/0.10 1.05140 2017.01.02 00:00:00 filled
2017.01.02 01:00:00 3 EURUSD sell 0.10/0.10 1.05172 2017.01.02 01:00:00 filled
2017.01.02 01:00:00 4 EURUSD sell 0.10/0.10 1.05172 2017.01.02 01:00:00 filled
2017.01.02 02:00:00 5 EURUSD buy 0.10/0.10 1.05293 2017.01.02 02:00:00 filled
2017.01.02 02:00:00 6 EURUSD buy 0.10/0.10 1.05293 2017.01.02 02:00:00 filled
2017.01.02 03:00:00 7 EURUSD sell 0.10/0.10 1.05192 2017.01.02 03:00:00 filled
2017.01.02 03:00:00 8 EURUSD sell 0.10/0.10 1.05192 2017.01.02 03:00:00 filled
2017.01.02 04:00:00 9 EURUSD buy 0.10/0.10 1.05234 2017.01.02 04:00:00 filled
2017.01.02 04:00:00 10 EURUSD buy 0.10/0.10 1.05234 2017.01.02 04:00:00 filled
2017.01.02 05:00:00 11 EURUSD sell 0.10/0.10 1.05151 2017.01.02 05:00:00 filled
2017.01.02 05:00:00 12 EURUSD sell 0.10/0.10 1.05151 2017.01.02 05:00:00 filled
2017.01.02 06:00:00 13 EURUSD buy 0.10/0.10 1.05230 2017.01.02 06:00:00 filled
2017.01.02 06:00:00 14 EURUSD buy 0.10/0.10 1.05230 2017.01.02 06:00:00 filled
2017.01.02 07:00:00 15 EURUSD sell 0.10/0.10 1.05142 2017.01.02 07:00:00 filled
2017.01.02 07:00:00 16 EURUSD sell 0.10/0.10 1.05142 2017.01.02 07:00:00 filled
2017.01.02 08:00:00 17 EURUSD buy 0.10/0.10 1.05169 2017.01.02 08:00:00 filled
2017.01.02 08:00:00 18 EURUSD buy 0.10/0.10 1.05169 2017.01.02 08:00:00 filled
2017.01.02 09:00:00 19 EURUSD sell 0.10/0.10 1.05131 2017.01.02 09:00:00 filled
2017.01.02 09:00:00 20 EURUSD sell 0.10/0.10 1.05131 2017.01.02 09:00:00 filled
2017.01.02 10:00:00 21 EURUSD buy 0.10/0.10 1.05164 2017.01.02 10:00:00 filled


MT5 のヘッジおよびネットモードでは、結果は同一であることに注意してください。 根本的な実装は同じですが、ネットモードでは、ポジションが同じボリュームのトレードをエントリーして中和されますが、ヘッジモードでは、多くのトレーダーがMT4で慣れているものに似ています。 ヘッジモードでは、次のようなメッセージが表示されます。

PE      0       16:19:15.747    Trade   2017.01.02 01:00:00   instant sell 0.10 EURUSD at 1.05172, close #2 (1.05172 / 1.05237 / 1.05172)
GP      0       16:19:15.747    Trades  2017.01.02 01:00:00   deal #3 sell 0.10 EURUSD at 1.05172 done (based on order #3)
DS      0       16:19:15.747    Trade   2017.01.02 01:00:00   deal performed [#3 sell 0.10 EURUSD at 1.05172]

最初の行に "# 2 を閉じる" というステートメントを書き留めます。 ヘッジモードでは、どの特定のトレードが中和されるかを示します (クローズ)。 一方、ネットモードでは、次のようなメッセージのみが表示されます。

PG      0       16:20:51.958    Trade   2017.01.02 01:00:00   instant sell 0.10 EURUSD at 1.05172 (1.05172 / 1.05237 / 1.05172)
MQ      0       16:20:51.958    Trades  2017.01.02 01:00:00   deal #3 sell 0.10 EURUSD at 1.05172 done (based on order #3)
KN      0       16:20:51.958    Trade   2017.01.02 01:00:00   deal performed [#3 sell 0.10 EURUSD at 1.05172]

このモードでは、新しいオーダーによるエントリなのか、既存のポジションを中和する行為が行われたのか、明らかではありません。

構造の概要

COrderManager クラスは、この記事シリーズで説明する最も複雑なクラスオブジェクトの1つです。 最終的なオーダーマネージャがオブジェクトメンバとどのようになるかを可視化するため、次の図を考えてみます。

オーダーマネージャの構造の概要

簡単な言葉で表すと、オーダーマネージャは、オーダーマネージャ (COrder) によってエントリーされたオーダーのコンテナとして (現在および履歴) の2つのインスタンスが含まれます。 各オーダーのストップレベルに対応することができ、各レベルの独自の後続のメソッドを持つことができます。 ほとんどのEAは、このような複雑さは必要ないですが、複数のサポートとレジスタンスレベルを扱う際、この構造は便利になります。 クラスメンバーについては、今後の記事で説明します。

結論

この記事では、トレードオペレーションの管理を担当する COrderManager クラスについて説明しました。 COrderManager クラスは、MQL4 と MQL5 で扱うことができるように設計されているので、トレーダーやプログラマがEAをコーディングする際、メインソースまたはヘッダーファイルに記述するし、クロスプラットフォームの互換性を確保することができます。