English Русский 中文 Español Deutsch Português
ユニバーサルEA:保留注文とサポートヘッジ(その5)

ユニバーサルEA:保留注文とサポートヘッジ(その5)

MetaTrader 5 | 1 8月 2016, 09:14
962 0
Vasiliy Sokolov
Vasiliy Sokolov

目次


イントロダクション

ユニバーサルEAのこのシリーズでは、複雑なロジックを利用して取引ロボットを開発することができ、特別な取引エンジンの関数を提供します。CStrategyクラスのセットは、積極的に改善され続けています。新しいアルゴリズムは、取引をより効率的かつ簡単にするためのエンジンが追加されています。また、さらなるアルゴリズム開発は、記事のディスカッションのトピックで質問をユーザーのフィードバックから、または個人的なメッセージを送信することによって改善されます。最もよくある質問は、予約注文についてでした。CStrategyエンジンは、予約注文を処理する便利なツールがなく、また、予約注文については以前の記事でも説明されていませんでした。この記事では、CStrategyトレーディングエンジンについて大きく紹介します。変更後では、CStrategyは予約注文を操作するための新しいツールがあります。後ほど、詳細について説明します。

また、MT5の新しいバージョンでは、ヘッジオプション付きのアカウントで双方向の取引をサポートしています("メタトレーダーポジションアカウントヘッジの関数」をご覧ください)。CStrategyのコードでは、これらの技術革新をカバーするために、新しいアカウントで正しく動作するように修正されました。このコードへほんのわずかな変更がヘッジサポートとして加えられました。これはつまり、初期で選択されたアプローチが正しかったことを表します。また、このようなヘッジなどの新しいMT5ツールは、CStrategyの新しいバージョンで実装されています。

また、CStrategyは、Ask,Bid,直近の価格のメソッドをサポートしています。これらのメソッドは、この記事の最初の章で説明します。

この記事では、CStrategyとMT5の両方における技術革新と変化に関する多くの情報があります。前のパートで説明されていないトピックを補足しています。読者の興味をひくことを願っています。


現在の価格へのアクセスは、AskとBid、直前のメソッドを確認して下さい。Digits関数のオーバーライド

トレーダーにとって、多くの場合、現在の価格へのアクセス権を持っている必要があります。CStrategyの以前のバージョンでは、このようなデータ・アクセスのための特別なメソッドがありませんでした。代わりに、ユーザが現在の価格をリクエストする際は、標準関数を使用していました。たとえば、価格を見つけるために、次のコードを記述します。

double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
int digits = SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
ask = NormalizeDouble(ask, digits);

SymbolInfoDouble - - 価格を受信するためには1つの関数を必要としますが、受信された値は現在の精度に正規化する必要があります。価格の受信は複数のアクションを必要とします。同じことが、Bid,最新価格についても言えます。

CStrategyを利用するため3つのメソッドがCStrategyに追加されています。:Ask(), Bid() and Last(). 各々は、対応する価格を受信し、現在のシンボルに応じて、それを正規化します。 

//+------------------------------------------------------------------+
//| 価格を返します。 |
//+------------------------------------------------------------------+
double CStrategy::Ask(void)
  {
   double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   ask = NormalizeDouble(ask, digits);
   return ask;
  }
//+------------------------------------------------------------------+
//|Bidを返します。 |
//+------------------------------------------------------------------+
double CStrategy::Bid(void)
  {
   double bid = SymbolInfoDouble(ExpertSymbol(), SYMBOL_BID);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   bid = NormalizeDouble(bid, digits);
   return bid;
  }
//+------------------------------------------------------------------+
//| 最新価格を返します。 |
//+------------------------------------------------------------------+
double CStrategy::Last(void)
  {
   double last = SymbolInfoDouble(ExpertSymbol(), SYMBOL_LAST);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   last = NormalizeDouble(last, digits);
   return last;
  }

これらのメソッドは、CStrategyクラスの新しいバージョンで定義され、直接CStrategyのクラスで利用可能です。ストラテジー開発の例でそれらを使用します。

適切な名前のメソッドを経由して、Ask、Bid、最新価格にアクセスする構成に加えて、CStrategyは、Digits関数をオーバーライドします。この関数は、小数点以下の桁数を返します。この関数のオーバーライドが無意味に見えるかもしれませんが、そんなことはありません。EAのシンボルは、CStrategyを含む実行モジュールが実行されているシンボルと異なる場合があります。この場合、数字システム関数の呼び出しは、間違いを招く可能性があります。実行されているシンボルの桁数を返します。この混乱を避けるために、CStartegyの桁の関数は、同じ名前のメソッドによってオーバーライドされます。この関数への参照は、このメソッドを呼び出します。これは、EAの演算中のシンボルの小数点以下の桁数を返します。ここでのこのメソッドのソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//||小数点以下の桁数を返します。
//|インストゥルメント|
//+------------------------------------------------------------------+
int CStrategy::Digits(void)
  {
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   return digits;
  }

この関数を認識し、このオーバーライドの意味を理解しなければなりません。

 

ヘッジオプションを使用したアカウントのサポート

ヘッジオプションを使用したアカウントのサポートは最近、MT5に追加されました。このアカウントでは、トレーダーは同時に、反対の方向で開いている複数のポジションを持つことができます。CStrategyは、ポジションを持つすべての操作が、SupportBuyとSupportSellで処理されます。CStrategyにリストされているポジションは、これらのメソッドを1つずつ送信します。多くのポジションがあるかどうかは関係ありません。同じシンボルまたは別のシンボルでポジションを開くことができます。これらのポジションを処理し、送信するために使用されます。したがって、ヘッジのサポートを追加するために変化が必要でしょう。まず第1に、ポジションを再構築するメソッドを変更する必要があります。取引環境が変化しているとき(新しい取引が実行されている)、このメソッドは、ポジションのリストを再配置します。使用するモードによって異なり、並べ替えます。アカウントを相殺するために、シンボルのポジションを選択するためのアルゴリズムを使用します。アカウントをヘッジするために、リスト内のポジションのインデックスを使用します。

ここで、ポジションを再構築するメソッドは、次のとおりです。

//+------------------------------------------------------------------+
//|ポジションのリストを並べ替る。|
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
   ActivePositions.Clear();
   for(int i = 0; i < PositionsTotal(); i++)
   {
      string symbol = PositionGetSymbol(i);
      PositionSelect(symbol);
      CPosition* pos = new CPosition();
      ActivePositions.Add(pos);
   }
}

2つのポジション選択アルゴリズムは、ポジションを再構築し、新しいバージョンでは、アカウントの種類に応じて使用します。

//+------------------------------------------------------------------+
//|ポジションのリストを並べ替る。|
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
  {
   ActivePositions.Clear();
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         string symbol=PositionGetSymbol(i);
         PositionSelect(symbol);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
   else
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         ulong ticket=PositionGetTicket(i);
         PositionSelectByTicket(ticket);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
  }

なお、同じポジションクラスCPositionが、ネッティングアカウントに使用されています。ポジションが選択されると、そのプロパティはPositionはgetInteger、GetDoubleとポジション、GetStringメソッドを介してアクセスすることができます。ヘッジまたは正常なポジションが選択されているかどうかは関係ありません。ポジションへのアクセスは、どちらの場合でも、同じです。これが、別のアカウントの同じCPositionのポジションのクラスを使用できる理由です。 

CStrategyクラスの内部で上書きされる他のメソッドはありません。このエンジンに基づくCStrategyは状況に依存するように設計されています。これは、CStrategyはヘッジオプション付きのアカウント上で動作し、1方向に複数のポジションを開いた場合、別々のCPositionクラスとして各ポジションを扱い、管理することを意味します。逆に、アカウント上でポジションを開くことができるならば、同じCPositionのオブジェクトの形でこのポジションを管理します。  

RebuildPositionメソッドの変更に加えて、いくつかのメソッドの内部を変更する必要があります。このクラスは、PositionMT5.mqhファイルにあり、システムの関数呼び出しに基づいたメソッドが含まれています。また、CPositionは、標準的なトレードクラスCTradeを使用しています。ヘッジポジションの特性を利用して、CTradeの最新バージョンが変更されました。例えば、ヘッジポジションは、反対向きのポジションで決済される可能性があります。::PositionCloseByメソッドの呼び出し。変更内容は以下のとおりです。

//+------------------------------------------------------------------+
//|                                                  PositionMT5.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "Logs.mqh"
#include <Trade\Trade.mqh>
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//|CStrategyのアクティブなポジションクラス|
//+------------------------------------------------------------------+
class CPosition : public CObject
  {
   ...
  };
...
//+------------------------------------------------------------------+
//| ポジションのストップロスのレベルを返します。|
//||ストップロスレベルが設定されていない場合は、0.0を返します。
//+------------------------------------------------------------------+
double CPosition::StopLossValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------+
//||ストップロスのレベルを設定します。
//+------------------------------------------------------------------+
bool CPosition::StopLossValue(double sl)
{
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, sl, TakeProfitValue());
}
//+------------------------------------------------------------------+
//| ポジションのストップロスのレベルを返します。|
//||ストップロスレベルが設定されていない場合は、0.0を返します。
//+------------------------------------------------------------------+
double CPosition::TakeProfitValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_TP);
}
//+------------------------------------------------------------------+
//||ストップロスのレベルを設定します。
//+------------------------------------------------------------------+
bool CPosition::TakeProfitValue(double tp)
  {
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, StopLossValue(), tp);
  }
//+------------------------------------------------------------------+
//||現在ポジションを閉じます
//|「コメント」|
//+------------------------------------------------------------------+
bool CPosition::CloseAtMarket(string comment="")
  {
   if(!IsActive())
      return false;
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return m_trade.PositionClose(m_symbol);
   return m_trade.PositionClose(m_id);
  }
//+------------------------------------------------------------------+
//|現在のポジションのボリュームを返します。|
//+------------------------------------------------------------------+
double CPosition::Volume(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_VOLUME);
  }
//+------------------------------------------------------------------+
//|口座通貨でのポジションの現在の利益を返します。 |
//+------------------------------------------------------------------+
double CPosition::Profit(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_PROFIT);
  }
//+------------------------------------------------------------------+
//|ポジションがアクティブである場合にtrueを返します。| falseを返します
//|それ以外の場合|
//+------------------------------------------------------------------+
bool CPosition::IsActive(void)
{
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return PositionSelect(m_symbol);
   else
      return PositionSelectByTicket(m_id);
}
//+------------------------------------------------------------------+


ご覧の通り、これらのメソッドのベースは、isActiveメソッドの呼び出しです。CPositionオブジェクトによって表されるアクティブポジションが存在する場合、メソッドはtrueを返します。このメソッドは、実際には、アカウントの種類に応じて、2つのメソッドのいずれかを使用してポジションを選出します。ネットオプション付きの古典的なアカウントの場合、ポジションはPositionSelect関数を使用して、選択されています。ヘッジオプション付きアカウントでは、ポジションがPositionSelectByTicket関数により、そのチケットに基づいて選択されます。結果は、(真または偽)メソッドを呼び出したプロシージャに戻されます。このメソッドは、ポジションの操作のコンテキストを設定します。CPositionのすべての取引関数がCTradeクラスに基づいているため、取引アルゴリズムを変更する必要はありません。CTradeは双方向のポジションを閉じることができます。  

 

以前のCStrategyのバージョンで、予約注文

予約注文は、多くの取引アルゴリズムにとって、重要な部分です。CStrategy取引エンジンの最初のバージョンのリリース後、予約注文の使用について多くの質問を受けました。この次のセクションでは、保留注文について説明します。

CStrategyエンジンは、もともとは注文の保留に関係なく作成されました。しかし、保留中のオーダーで動作することができないCStrategyクラスに基づいて開発することを意味するものではありません。最初のCStrategyのバージョンでは、MT5のOrdersTotal()およびOrderSelect()関数を使用することによって行うことができます。

例えば、トリガー価格が現在価格より0.25%(buy)以上になるように、それぞれの新しいバーに保留中のストップオーダーを変更します。このアイデアは、レートがバー内で突発的な動き(インパルス)をした場合、このオーダーが満たされます。動きが十分でない場合、注文は、現在のバー内で満たされることはありません。この場合、新たなレベルにオーダーを移動させる必要があります。CStrategyの実装は、インパルスと呼ばれるアルゴリズムを記述したセクションで提供されています。システム名から明らかなように、インパルスアルゴリズムに基づいています。これは、保留中のトリガー時にエントリーが必要です。CStrategyは、ポジションを入力するための特別なオーバーライドされたメソッドのBuyInitとSellInitが含まれています。したがって、予約注文のアルゴリズムは、これらのメソッドに追加する必要があります。CStrategyからの直接サポートがない場合、コードは次のようになります。

//+------------------------------------------------------------------+
//|保留中の操作のロジック。       |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // 要求イベントのハンドル!
   if(positions.open_buy > 0) return;                    // 保有ポジションがある場合、新規エントリーはしない
   int buy_stop_total = 0;
   for(int i = OrdersTotal()-1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(!OrderSelect(ticket))continue;
      ulong magic = OrderGetInteger(ORDER_MAGIC);
      if(magic != ExpertMagic())continue;
      string symbol = OrderGetString(ORDER_SYMBOL);
      if(symbol != ExpertSymbol())continue;
      ENUM_ORDER_TYPE order_type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
      if(order_type == ORDER_TYPE_BUY_STOP)
      {
         buy_stop_total++;
         Trade.OrderModify(ticket, Ask()*0.0025, 0, 0, 0);
      }
   }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

IsTrackEventsメソッドは、受信したイベントが新しいバーの保有ポジションに対応しているか判定します。そして、EAは買い方向のオープンポジションの数をチェックします。少なくとも一つのロング・ポジションがある場合は、EAは新規に買いエントリーすべきではない、というロジックになります。ストラテジーは現在のすべての予約注文をチェックします。予約注文のインデックスをループします。それぞれの注文は、インデックスによって選択され、そのシンボルを分析します。これらの2つのパラメータがEAのパラメータに対応する場合、そのオーダーは現在のEAに属するとされ、オーダーカウンタが1増加されます。そのオーダーは、エントリ価格がそれ以上の現在の価格+0.25%で置き換えられ、変更されます。buy_stop_orderがゼロに等しいことから保留注文が存在しない場合、新たな注文はせずに現在の価格から0.25%の距離に配置されます。

InitBuyには保有ポジションが存在しないことに注意してください。CStrategyは、メソッドで開いたポジションに、このような制限はありません。正確には、EAのロジックはここで追加することができます。しかし、適切なイベント処理のため、保留中または成行注文を介して、保有ポジションと接続する必要があります。 

この例から分かるように、予約注文の操作は、従来のポジションと同様に行われます。主な要件は同じです。買いと売りを分割し、別のメソッドでそれらを記述します。保留中の買い注文はBuyInitで処理されなければなりません。保留中の売り注文がSellInitで処理されなければなりません。予約注文とロジックの残りの部分は、MT4で採用されている方式と同様です:現在のEAに属している注文を選択する取引環境を分析します。 

 

予約注文と相互に運用するためのCPendingOrdersとCOrdersEnvironment

予約注文の包括的かつ簡単な監視のシステム、保留中の作業の標準MT5の関数。しかし、CStrategyは、取引システムの作成を可能にするオブジェクト指向のクラスのセットです。CStrategyで行われるすべてのアクションは、トレードを行うオブジェクトによって順番に実行されます。つまり、オブジェクト指向です。このアプローチにはいくつかの利点があります。ここではいくつか挙げます。

  • カスタムのソースコードの縮小サイズこのような価格の正常化やCopyBufferクラスの関数アレイのような多くのアクションは、「舞台裏」で行われています。したがって、直接MT5システム関数で作業する場合、回避できない結果のチェックや他の中間ステップの追加手順を記述する必要はありません。
  • プラットフォームに依存しない。正式なMQL言語で記述され、提供されるすべてのクラスとメソッドは、任意のプロパティを取得することができます。しかし、このメソッドの実装は、異なるプラットフォーム上では異なるでしょう。しかし、これはユーザーレベルでは重要ではありません。これは、別のプラットフォーム上で動作するようにコンパイルすることができ、理論的には、一つのプラットフォーム用に開発できることを意味します。しかし、この記事では、プラットフォーム独立性については説明しません。
  • 機能性 MQL関数のセットを使用すると、複雑なアルゴリズムと関数を組み合わせることで、基本的な関数を使えます。これらのアルゴリズムが1つのライブラリに含まれている場合、新しい関数の追加は、新しいモジュールが追加されるため、CStartegyのようにアクセスが容易になります。

このアプローチに固執すると、予約注文の操作に便利なオブジェクト指向のメカニズムを提供するようにCStartegyの関数を拡張します。この機構は、2つのクラスで表されます。CPendingOrderCOrdersEnvironment。COrderは整数、注文はGetDoubleとGetStringを介してアクセスすることができ、すべてのプロパティを含む便利なオブジェクトを提供します。COrdersEnvironmentの役割は、後述します。

CPendingOrderオブジェクトがシステムに存在する保留中のオーダーを表しているとします。この保留中のオーダーを削除する場合、オーダーを表すCPendingOrderオブジェクトに何が起こるのでしょうか?オブジェクトが実際の未決済注文の削除後に残っている場合、これは重大なエラーが発生します。EAは、予約注文を見つけるだろうし、誤って予約注文がまだシステムに存在していると「考える」になります。このようなエラーを回避するために、CStrategyオブジェクトで取引環境の同期を保証しなければなりません。言い換えれば、システム内に存在しているオブジェクトのみへのアクセスを保証するメカニズムを作成する必要があります。これはCOrdersEnvironmentクラスがやっていることです。その実装は非常に単純で、予約注文を表すCPendingOrdersオブジェクトへのアクセスを可能にします。

COrdersEnvironmentクラスの基本はGetOrderとTotalメソッドで構成されています。CPendingOrdersは保留中のインデックスを持つ注文に対応するオブジェクトを返します。第2のメソッドは、予約注文数の合計を返します。詳細にこのクラスを見ていきましょう。クラスのソースコードは、次のとおりです。

//+------------------------------------------------------------------+
//|保留中の操作のクラス|
//+------------------------------------------------------------------+
class COrdersEnvironment
{
private:
   CDictionary    m_orders;         //予約注文の合計数
public:
                  COrdersEnvironment(void);
   int            Total(void);
   CPendingOrder* GetOrder(int index);
};
//+------------------------------------------------------------------+
//||EAの現在のシンボルとマジックナンバー
//+------------------------------------------------------------------+
COrdersEnvironment::COrdersEnvironment(void)
{
}
//+------------------------------------------------------------------+
//||予約注文を返します。
//+------------------------------------------------------------------+
CPendingOrder* COrdersEnvironment::GetOrder(int index)
{
   ulong ticket = OrderGetTicket(index);
   if(ticket == 0)
      return NULL;
   if(!m_orders.ContainsKey(ticket))
      return m_orders.GetObjectByKey(ticket);
   if(OrderSelect(ticket))
      return NULL;
   CPendingOrder* order = new CPendingOrder(ticket);
   m_orders.AddObject(ticket, order);
   return order;
}
//+------------------------------------------------------------------+
//||保留中の数を返します。
//+------------------------------------------------------------------+
int COrdersEnvironment::Total(void)
{
   return OrdersTotal();   
}

Totalメソッドは、現在のシステムに存在する予約注文の数を返します。OrdersTotal()関数から受信した値は、決して間違いません。

GetOrderメソッドの動作のために、システム内の予約注文のインデックスを指定する必要があります。Totalメソッドを通じた正確な総数は、システム内の保留中のため、実際のインデックスに正確に対応します。また、GetOrderメソッドは、そのインデックスにより、予約注文の識別子を受け取ります。オーダーが何らかの理由で削除されている場合、注文IDはゼロに等しくなり、NULL定数が要求されたインデックスを持つオーダーが見つからないことを示します。

動的に作成された各オブジェクトは、明示的にdelete演算子によって取り消されます。GetOrderが動的に新しい演算子を使用して、CPendingOrderオブジェクトを作成しているため、これらのオブジェクトも削除する必要があります。作成後、ユーザーがオブジェクトを削除する必要性を排除するために、COrdersEnvironmentオブジェクト内部の辞書コンテナにポジションします。辞書内の要素は、この場合のIDであるユニークなキーでアクセスすることができます。注文がまだ存在している場合、このオーダーを表す以前に作成されたオブジェクトは、コンテナに入れられます。よって、これ以前に作成したオブジェクトがGetOrder関数によって返されます。指定されたIDを持つ最初の呼び出しである場合、新しいCPendingOrderオブジェクトは辞書に追加され、その参照はユーザーに戻されます。

提案されたアプローチから何が得られますか?まず、GetOrderメソッドは本当に既存の保留中のオーダーを表すオブジェクトを返すことが保証されます。システム関数OrderGetTicketにはその役割があります。第2に、オブジェクトは、まだ作成されていない場合にのみ作成されます。これはコンピュータリソースを節約できます。そして第3に、このアルゴリズムは、オブジェクトを削除する必要性からユーザーを解放します。すべてのオブジェクトがディクショナリに格納されているので、COrdersEnvironmentの初期化解除後に自動的に削除されます。 

 

EAコード内の予約注文への参照

前節「以前のCStrategyのバージョンで、予約注文を通じたトレード」で、操作のロジックを書き換えます。CPendingOrderとCOrdersEnvironmentクラスのコードは次のとおりです。

//+------------------------------------------------------------------+
//|短期MAが長期MAを超えているときに買います。       |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // 要求イベントのハンドル!
   if(positions.open_buy > 0) return;                    // 保有ポジションがある場合、新規エントリーはしない
   int buy_stop_total = 0;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
     {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
       {
         buy_stop_total++;
         Order.Modify(Ask() + Ask()*0.0025);
       }
       //オーダーの削除;オーダーオブジェクトの削除の必要性なし
     }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

保留注文オブジェクトは、COrdersEnvironmentクラスです。予約注文を通じた検索システムの関数を使用する場合と同じメソッドを使います。その後、i変数に等しい予約注文インデックスに従ったオーダーオブジェクトを取得します。注文が何らかの理由で受信されていないか、別のEAに属している場合は、新しい注文を検索します。CPendingorderオブジェクトのIsMainメソッドによる注文の数が一致する場合はtrueを返します。

現在の価格+0.25%:オーダー型がORDER_TYPE_BUY_STOPに対応している場合、式によって修正されるべきであることを意味します。予約注文の変更は、Modifyメソッドとそのオーバーライドバージョンを使用することによって行われます。それは価格と他のパラメータを設定することができます。

保留中のオーダーで操作を完了した後、注文オブジェクトを削除する必要がないことに注意してください。そのままオーダーへ参照しておく必要があります。PendingOrdersコンテナはCPendingOrder型のオブジェクトを管理し、ユーザ関数で返されるオブジェクトの除去を必要としません。 

予約注文とbuy_stop_totalカウントがゼロに等しい場合、新しい保留中のオーダーが配置されます。新しい注文は、前のパートで説明したトレード・モジュールを使用して配置します。 

オブジェクト指向のアプローチでは、予約注文はCPendingOrdersオブジェクトの適切なメソッドを介してアクセスされます。唯一の既存の秩序のオブジェクトは、PendingOrdersを保証するので、このアクセス方式はコードを削減します。すなわち、予約注文が本当にシステムに存在するということになります。

 

予約注文を使用するCImpulseEAの取引ロジック 


予約注文の使用を分析し、取引エンジンの可能性を利用した本格的なEAを作成することができます。CStrategyは強い市場の動きの間に基づいて行われますので、 CImpulseと呼ばれます。それぞれの新しいバーのオープニングで、EAは現在のバーから所定の距離を測定し、パーセンテージとして表現されます。EAはBuyStopやSellStopの予約注文を発行し、現在の価格から等距離にストップ注文を販売します。距離は割合として設定されます。バー内でトリガーが発生する場合、価格が市場のインパルスを示す非常に長い距離を移動したことを意味します。注文が実行され、保有ポジションに変わります。

オープンポジションは単純移動平均によって管理されます。移動平均の価格が戻る場合は、ポジションが閉じられます。下のスクリーンショットは、保留中のBuyStopによるロングポジションの一般的なエントリを示しています。


図1。CImpulseCStrategyのロングポジション。
 

上のスクリーンショットでは、バーの始まりに、黒い三角形が付いています。このバー初めに、2つの予約注文がバーオープン価格から0.1%の距離に配置されています。そのうちの1つ - BuyStopオーダー - トリガ。新しいロングポジションがオープンしました。赤い線で表示された移動平均を下回るとすぐに、EAはポジションを閉じました。図1決済は青い三角形として表示されます。

予約注文が1つのバー内でトリガしない場合、新しい現在の価格に基づいて計算されたレベルに移動されます。

このすとらてじいーは、予約注文の処理のいずれか特定の関数を持っています。BuyStopオーダーのレベルは、現在の移動平均よりも低くすることができます。現在の価格が移動平均を下回っているので、この場合、ポジションが約定後後すぐに閉じられます。同じことが、ショート・ポジションについても言えます:SellStop注文のトリガー価格が移動平均レベルより高くすることができます。即時決済を回避するために、BuyInit SellInitメソッドで移動平均レベルのチェックをする必要があります。アルゴリズムは、価格が移動平均以上である場合、BuyStop注文を配置します。同じことがSellStopにも言えます:価格が移動平均を下回っている場合、配置されます。

ヘッジ上の操作 - また、MT5の新関数を使用します。この関数は、ロングとショートポジションの両方をバー内で開くことができます。しかし、ロングとショートを分離するもともとのEAにおけるコードを残すことができます。ポジションの数に関係なく、価格が移動平均を上回ったときにすべてのショートポジションが閉じられます。この場合、アカウントをヘッジするかは問題ではありません。 

まず第1に、「口座開設」で適切なフラグをチェックすることにより、ヘッジオプションが有効か確認する必要があります。

 

図2。ヘッジオプションを持つアカウントを開く 

口座を開設した後、それから取引を進めることができます。まず、CImpulseクラスを記述し、取引アルゴリズムのロジックを実装する必要があります。 

 

CImpulseクラス

さらに、新たなEAのロジックを記述するCImpulseクラスのリストがあります。予約注文と操作については、このクラスは可能な限りシンプルです。ログに接続された手順と同様に、XMLファイルからのCStrategyパラメータを解析する特別なメソッドが含まれていません。

//+------------------------------------------------------------------+
//|                                                      Impulse.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Strategy\Strategy.mqh>
#include <Strategy\Indicators\MovingAverage.mqh>

input double StopPercent = 0.05;
//+------------------------------------------------------------------+
//||アクションが保留中で行われることを定義します
//| オーダー。|
//+------------------------------------------------------------------+
enum ENUM_ORDER_TASK
{
   ORDER_TASK_DELETE,   //予約注文の削除
   ORDER_TASK_MODIFY    //予約注文の修正
};
//+------------------------------------------------------------------+
//| The CImpulse strategy                                            |
//+------------------------------------------------------------------+
class CImpulse : public CStrategy
{
private:
   double            m_percent;        // 予約注文のレベルのパーセント値
   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:
   double            GetPercent(void);
   void              SetPercent(double percent);
   CIndMovingAverage Moving;
};
//+------------------------------------------------------------------+
//||ロングポジションを開くための予約注文
//|ポジション|
//+------------------------------------------------------------------+
void CImpulse::InitBuy(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_buy > 0) return;                    
   int buy_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Ask() + Ask()*(m_percent/100.0);
   if(target < Moving.OutValue(0))                    // The order trigger price must be above the Moving Average
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            buy_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(buy_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.BuyStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//||ショートポジションを開くための作業
//|ポジション|
//+------------------------------------------------------------------+
void CImpulse::InitSell(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_sell > 0) return;                    
   int sell_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Bid() - Bid()*(m_percent/100.0);
   if(target > Moving.OutValue(0))                    // The order trigger price must be below the Moving Average
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_SELL_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            sell_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(sell_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.SellStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//||移動平均に基づいて、ロングポジションを管理
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//||移動平均に基づいてショートポジションを管理
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//|イベントをフィルタリング渡されたイベントがない場合|
//|falseを返しCStrategyによって処理; 処理される場合|
//|trueを返します。                                        |
//+------------------------------------------------------------------+
bool CImpulse::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;
  }
//+------------------------------------------------------------------+
//|シンボルの変化に対応|
//+------------------------------------------------------------------+
void CImpulse::OnSymbolChanged(string new_symbol)
  {
   Moving.Symbol(new_symbol);
  }
//+------------------------------------------------------------------+
//|時間枠の変化に対応|
//+------------------------------------------------------------------+
void CImpulse::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
  {
   Moving.Timeframe(new_tf);
  }
//+------------------------------------------------------------------+
//||ブレイクレベルのパーセントを返します。
//+------------------------------------------------------------------+  
double CImpulse::GetPercent(void)
{
   return m_percent;
}
//+------------------------------------------------------------------+
//||ブレイクレベルのパーセントを設定します。
//+------------------------------------------------------------------+  
void CImpulse::SetPercent(double percent)
{
   m_percent = percent;
}

EAとしてこのCStrategyを設定し、mq5ファイルは、以下で利用可能です:

//+------------------------------------------------------------------+
//|                                                ImpulseExpert.mq5 |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\Impulse.mqh>

CStrategyList Manager;
//+------------------------------------------------------------------+
//|エキスパート初期化関数|
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   CImpulse* impulse = new CImpulse();
   impulse.ExpertMagic(1218);
   impulse.Timeframe(Period());
   impulse.ExpertSymbol(Symbol());
   impulse.ExpertName("Impulse");
   impulse.Moving.MaPeriod(28);                      
   impulse.SetPercent(StopPercent);
   if(!Manager.AddStrategy(impulse))
      delete impulse;
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//|EAのティック関数|
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   Manager.OnTick();
  }
//+------------------------------------------------------------------+


 

CImpulseストラテジーの分析

CStrategyのテストで、予約注文を使用してEA作動する添付のビデオを、参照してください。新しいバーが形成されたときにBuyStop SellStopは実行されます。オーダーは、現在の価格から一定の距離に配置され、チャネルを形成します。予約注文のレベルの移動平均線を下回ると、完全に除去されます。ただし、トリガー価格が移動平均よりも高くなったとき予約注文は再び表示されます。同じルールがSellStop注文に適用されます。


以下のスクリーンショットは、ヘッジ状況が形成された時間を示し、ショートポジションの決済とオープンポジションの約定を意味します。その後、ロングポジションは存在し続け、移動平均の下に移動し、そのロジックによって、閉じられました:

 

図3。ヘッジサポートアカウントのポジション管理。

アカウントが異なる状況でも、同じような状況になるでしょう。

 

図4。ネットアカウントのポジション管理。 

買いは移動中に実行され、このトレードは以前に開かれたショートポジションを閉じました。このように、すべてのオープン・ポジションを閉じました。したがって、次の足でEAは買いオーダーを配置し、その一つが約定しました。代わりに、ヘッジアカウントのポジションが決済されました。

EAのロジックを変更し、並列型にするアルゴリズムは、アカウントの種類に応じて実行されます。それだけで一つのポジションがネットアカウントに存在することは明らかです。新しいポジションの保有ポジションは、すべてのネット・ポジションにストップロスを追加する必要があります。ストップロス値は、反対注文のレベルに等しくなければなりません。したがって、ストップ注文の1のトリガーが現時点で存在している場合、反対ポジションのストップロスのトリガを意味します。このロジックは、ネッティングアカウントでのみ有効になります。これは、BuySupportとSellSupportメソッドに追加する必要があります。ここで変更されたソースコードは次のとおりです。

//+------------------------------------------------------------------+
//||移動平均に基づいて、ロングポジションを管理
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Bid() - Bid()*(m_percent/100.0);
      if(target < Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//||移動平均に基づいてショートポジションを管理
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Ask() + Ask()*(m_percent/100.0);
      if(target > Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}

更新したCStrategyのテスト結果は若干異なります。アカウントのネッティングでは、EAは一箇所のみで動作し、古典的なEAのように動作します:


図5。古典的なネッティング口座に多型EAで管理されるポジション。

この記事での取り付けは、CImpulseストラテジーの最新バージョンが含まれています。   

取引ロジックのすべてのバージョンが正しいことに注意してください。しかしながら、取引ストラテジーの動作は、CStrategy自体に依存します。CStrategyは、ポジションがタイプに応じて操作するインタフェースを提供します。 

 

結論 

CStrategy取引エンジンの新しい関数をレビューしました。新しい関数は、予約注文を持つ新しいアカウントの種類、オブジェクト操作のサポートを操作するための関数の拡張セットが含まれています。

CStrategyの新たなメソッドは、Ask,Bid,直近価格のような値にすばやく簡単にアクセスすることを可能にします。オーバーライドされた数字のメソッドは、必ずシンボル価格の小数点以下の桁数を正しく返します。 

予約注文を使用して、CPendingOrdersとCOrdersEnvironmentクラスは、取引ロジックを簡素化します。EAは、特殊なオブジェクトを介して、予約注文にアクセスすることができます。例えば、オブジェクトのプロパティを変更することによりレベルをトリガするためには、EAはオブジェクトに対応する実際のオーダーの適切なプロパティを変更します。オブジェクトモデルは、高い信頼性を提供します。そのオブジェクトに対応するシステム内の実際の予約注文がない場合には、オブジェクトにアクセスすることは不可能です。予約注文でオーバーライドされるBuyInitとSellInitで行われます。BuyInitは、予約中のオーダーを停止し、リミット注文を操作するために設計されています。SellInitは、SellStopとSellLimitによる予約注文用に設計されています。  

提案されたCStrategyエンジン内では、古典的なアカウントのものと変わりません。ポジションの操作はポジションタイプに依存し、特殊なクラスを介して使用されます。アカウントタイプの操作の違いは、ストラテジーのロジックです。CStrategyは、単一のポジションで動作する場合、そのポジションの管理のロジックを実装しています。ストラテジーが同時に複数のポジションで動作する場合、そのロジックが適切なBuySupportとSellSupportメソッドと連続して複数のポジションに移すことができることを考慮しなければなりません。取引エンジン自体は、任意のトレード・ロジックを実装していません。それが唯一のアカウントに対応するポジションの種類を提供します。

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

添付されたファイル |
MQL5でのエラー処理とロギング MQL5でのエラー処理とロギング
この記事では、ソフトウェアにおける一般的なエラー処理の問題について述べていきます。また、ロギングについて言及し、MQL5のツールによるデータロガーの実装例をデモンストレーションします。
マーケットでの公開前にトレードロボットに行うべき検査 マーケットでの公開前にトレードロボットに行うべき検査
マーケットの全ての製品は、均一な品質基準を確保する為に、公開前に事前の必須検査を受けます。この記事では、開発者が自分のテクニカルインディケータやトレードロボットで犯しがちなミスについてお話しします。また、マーケットへ提出する前の、製品の自己テストの方法もご紹介します。
エリック・ナイマンの『チャネル』インディケータ エリック・ナイマンの『チャネル』インディケータ
この記事では、エリック・L・ナイマン氏の著書『トレーダーの小百科事典』を元に『チャネル』インディケータの作成について述べていきます。このインディケータは、指定した期間で計算したベアとブルの値に基づき、トレンドの方向を表示します。この記事では、サンプルコードと共にインディケータの計算と構築の原理を説明し、インディケータをベースにエキスパートアドバイザを作成し、外部パラメータの最適化について述べていきます。
ユニバーサルEA:グループでの取引とストラテジーのポートフォリオを管理する(その4) ユニバーサルEA:グループでの取引とストラテジーのポートフォリオを管理する(その4)
CStrategyの取引エンジンについての一連の記事の最後のパートでは、XMLファイルからストラテジーをロードする方法を行います。複数の取引アルゴリズムの同時動作を考慮し、単一の実行可能モジュールからのEAを選択する簡単なパネルを提示し、その取引モードを管理します。