MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第9部): MQL4との互換性 - データの準備

Artyom Trishkin | 9 9月, 2019

内容

連載ではすでに、MetaTrader 5およびMetaTrader 4クロスプラットフォームライブラリ用に、以下を行うツールを準備しました。

取引クラスを開発するにあたって、ライブラリはMQL5とMQL4の両方で正しく動作するべきなので、ここでMQL4とライブラリの互換性を実装します。

本稿では、ライブラリの改善を開始してクロスプラットフォームの性質を実装します。

MQL4対MQL5

ライブラリフォルダー全体を適切なMetaTrader 4ディレクトリ(\MQL4\Include\DoEasy)にコピーします。MQL5 EAを含む適切なフォルダーからテストEAを取得し、それらを「*.mq4」拡張子で\MQL4\Experts\TestDoEasyEAディレクトリの記事番号に対応するフォルダーに保存します(この場合は Part09)。

エディターのナビゲータでライブラリディレクトリ(\MQL4\Include\DoEasy)を見つけ、右クリックして[コンパイル]を選択します。


これにより、すべてのライブラリファイルがコンパイルされ、2,000以上のコンパイルエラーが発生します。


発生したエラーを分析すると、それらの大部分が、MQL5特有の定数と列挙に関係していることがわかります。これは、ライブラリで使用される定数についてMQL4に知らせる必要があることを意味します。特定の関数が存在しないなど、他の性質のエラーもあります。つまり、MQL4関数を使用して操作ロジックを実装することになります。

その上、MQL4とMQL5の注文システムは非常に異なります。MQL5で実装されたものとは異なるMQL4特有のイベントハンドラの実装が必要になります。これは、MQL4の履歴注文のリストでは注文に関するデータがはるかに少ない(ならび取引に関するデータがない)ため、端末リストから直接注文や取引のデータを取得できないからです。ここでは、アクティブな注文と過去の注文のリストで発生したイベントを論理的に比較し、比較に基づいて発生したイベントを定義する必要があります。

ライブラリの改善

DoEasyライブラリのルートフォルダーで、新しいToMQL4.mqhインクルードファイルを作成します。ここでは、MQL4に必要なすべての定数と列挙について説明します。MQL4でコンパイルするためにはこのファイルをDefines.mqhファイルの冒頭にインクルードします

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ja/users/artmedia70"
//+------------------------------------------------------------------+
//| ファイルをインクルードする                                          |         |
//+------------------------------------------------------------------+
#ifdef __MQL4__      
#include "ToMQL4.mqh"
#endif               
//+------------------------------------------------------------------+

その後、MQL4ライブラリ全体が、コンパイル中にToMQL4.mqhファイルに書き込まれた内容を確認できるようになります。

NumPad Homeを押すか、一番上までスクロールして、エディターのツールボックスの[エラー]タブにあるエラーリストの最初に移動しましょう。最初のエラーをダブルクリックします。


エディターでDefines.mqhファイルにあるエラー文字列が表示されます。

//+------------------------------------------------------------------+
//| 口座で可能な取引イベントのリスト                                     |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // No trading event
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // 未決注文が出された
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // 未決注文の削除
//--- ENUM_DEAL_TYPE列挙体メンバに一致する列挙体メンバ
//--- (constant order below should not be changed, no constants should be added/deleted)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Charging credit (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // 追加の課金

当然、MQL4は取引とその型については何も知りません。これは修正する必要があります。MQL5リファレンスを開き、DEAL_TYPE_CREDITクエリを使用して約定プロパティのデータを検索します。

ID

説明

種類

DEAL_TICKET

取引チケット。各取引に割り当てられた一意の番号

long

DEAL_ORDER

取引の注文番号

long

DEAL_TIME

取引時刻

datetime

DEAL_TIME_MSC

01.01.1970からのミリ秒単位の取引実行時間

long

DEAL_TYPE

取引タイプ

ENUM_DEAL_TYPE

DEAL_ENTRY

取引の方向 - 市場エントリ、エグジット、反転

ENUM_DEAL_ENTRY

DEAL_MAGIC

取引のマジックナンバー(ORDER_MAGIC)を参照

long

DEAL_REASON

取引実行の理由またはソース

ENUM_DEAL_REASON

DEAL_POSITION_ID

取引によって開閉または変更されたポジションのID。各ポジションには、ポジションの存続期間中に銘柄で実行されたすべての取引に割り当てられる一意のIDがあります。

long


表で一番興味深いのはENUM_DEAL_TYPEです。リンクをクリックして、すべての取引タイプのリストを取得します。

ID

説明

DEAL_TYPE_BUY

買う

DEAL_TYPE_SELL

売る

DEAL_TYPE_BALANCE

残高

DEAL_TYPE_CREDIT

クレジット

DEAL_TYPE_CHARGE

追加の課金

DEAL_TYPE_CORRECTION

修正

DEAL_TYPE_BONUS

ボーナス

DEAL_TYPE_COMMISSION

追加手数料

DEAL_TYPE_COMMISSION_DAILY

手数料(日額)

DEAL_TYPE_COMMISSION_MONTHLY

手数料(月額)

DEAL_TYPE_COMMISSION_AGENT_DAILY

エージェント手数料(日額)

DEAL_TYPE_COMMISSION_AGENT_MONTHLY

エージェント手数料(月額)

DEAL_TYPE_INTEREST

利子

DEAL_TYPE_BUY_CANCELED

キャンセルされた買い取引。以前に実行された買い取引がキャンセルされる場合があります。この場合、以前に実行された取引のタイプ(DEAL_TYPE_BUY)DEAL_TYPE_BUY_CANCELEDに変更され、 利益/損失がゼロにリセットされます。以前に取得した利益/損失は、個別の残高操作を使用して請求/出金されます

DEAL_TYPE_SELL_CANCELED

キャンセルされた売り取引。以前に実行された売り取引がキャンセルされる場合があります。この場合、以前に実行された取引のタイプ(DEAL_TYPE_SELL)DEAL_TYPE_SELL_CANCELEDに変更され、 利益/損失がゼロにリセットされます。以前に取得した利益/損失は、個別の残高操作を使用して請求/出金されます

DEAL_DIVIDEND

配当操作

DEAL_DIVIDEND_FRANKED

非課税配当操作

DEAL_TAX

課税


ENUM_DEAL_TYPE列挙体からの取引タイプをToMQL4.mqhファイルに追加します。

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ja/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
#endif 

ファイルを保存し、すべてのライブラリファイルを再度コンパイルします。エラーが少なくなりました。


再びエラーリストの先頭に移動して、最初のエラーをクリックします。これはENUM_POSITION_TYPEなので、追加しましょう

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ja/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal type                                                   |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
#endif 

コンパイルすると、エラーはさらに少なくなります。リストの最初のエラーに移動し、理由を特定して、次の列挙を追加します

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ja/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
#endif 

次のコンパイル中には、注文タイプが間違っています(ORDER_TYPE_BUY_STOP_LIMIT)。
MQL4にはすでにENUM_ORDER_TYPE列挙があり、新しい定数を追加することはできません。したがって、それらをマクロ置換として追加します。

MQL5では、ENUM_ORDER_TYPE列挙のORDER_TYPE_BUY_STOP_LIMIT定数は6に設定されていますが、MQL4では、そのような注文タイプが存在します。MQL5のORDER_TYPE_SELL_STOP_LIMITのように、この残高操作は7に設定されていますが、MQL4では、この注文タイプはクレジット操作です。

したがって、MQL5でORDER_TYPE_CLOSE_BY注文終結定数を超える値を設定します。 ORDER_TYPE_CLOSE_BY+1およびORDER_TYPE_CLOSE_BY+2です。

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ja/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+
//| Order status                                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATE
  {
   ORDER_STATE_STARTED,
   ORDER_STATE_PLACED,
   ORDER_STATE_CANCELED,
   ORDER_STATE_PARTIAL,
   ORDER_STATE_FILLED,
   ORDER_STATE_REJECTED,
   ORDER_STATE_EXPIRED,
   ORDER_STATE_REQUEST_ADD,
   ORDER_STATE_REQUEST_MODIFY,
   ORDER_STATE_REQUEST_CANCEL
  };
//+------------------------------------------------------------------+
//| Order types                                                      |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8) 
#define ORDER_TYPE_BUY_STOP_LIMIT   (9) 
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
//+------------------------------------------------------------------+
#endif 

ライブラリ全体をコンパイルします。ENUM_ORDER_TYPE列挙型を持つスイッチ演算子で注文タイプの値が使用されるため、StopLimit注文タイプのマクロ置換を実装した後、エラーは、正しい発注価格を返す関数、つまり9と10の値を持たないENUM_ORDER_TYPE列挙を示します。

//+------------------------------------------------------------------+
//| StopLevelに相対した                                               |
//| 正しい発注価格を返す                                               |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| StopLevelに相対した                                               |
//| 正しい発注価格を返す                                               |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch(order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

解決法は簡単です。switch内のorder_type整数型に変換します。

//+------------------------------------------------------------------+
//| StopLevelに相対した                                               |
//| 正しい発注価格を返す                                               |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const double price_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,price_set),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,price_set),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+
//| StopLevelに相対した                                               |
//| 正しい発注価格を返す                                               |
//+------------------------------------------------------------------+
double CorrectPricePending(const string symbol_name,const ENUM_ORDER_TYPE order_type,const int distance_set,const double price=0,const int spread_multiplier=2)
  {
   double pt=SymbolInfoDouble(symbol_name,SYMBOL_POINT),pp=0;
   int lv=StopLevel(symbol_name,spread_multiplier), dg=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
   switch((int)order_type)
     {
      case ORDER_TYPE_BUY_LIMIT        :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      case ORDER_TYPE_BUY_STOP         :  
      case ORDER_TYPE_BUY_STOP_LIMIT   :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_ASK) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_LIMIT       :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmax(pp+lv*pt,pp+distance_set*pt),dg);
      case ORDER_TYPE_SELL_STOP        :  
      case ORDER_TYPE_SELL_STOP_LIMIT  :  pp=(price==0 ?SymbolInfoDouble(symbol_name,SYMBOL_BID) : price); return NormalizeDouble(fmin(pp-lv*pt,pp-distance_set*pt),dg);
      default                          :  Print(DFUN,TextByLanguage("Неправильный тип ордера: ","Invalid order type: "),EnumToString(order_type)); return 0;
     }
  }
//+------------------------------------------------------------------+

コンパイルしましょう。次のエラーはOrder.mqhファイルにあります。MQL4がORDER_FILLING_RETURNORDER_TIME_GTCORDER_REASON_SLORDER_REASON_TPORDER_REASON_EXPERT定数を知らないことが原因です。

//+------------------------------------------------------------------+
//| 実行タイプを返す                                                   |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 注文の有効期限を返す                                               |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 注文の理由またはソース                                              |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_POSITION   : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

ToMQL4.mqhファイルの最後にマクロ置換を追加しましょう(スペースを節約するために、ここでは完全なコードは載せません)。

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
//+------------------------------------------------------------------+
#endif 

もう一回コンパイルすると、HistoryOrderGetTicket()MQL5関数CHistoryCollection::OrderSearch()メソッドのHistoryCollection.mqhファイルに欠落していることがわかります。コード分析によって、ここで条件付きコンパイルディレクティブを適用することが推奨されます。メソッドを補足しましょう。

//+------------------------------------------------------------------+
//| Return the "lost" order's type and ticket                        |
//+------------------------------------------------------------------+
ulong CHistoryCollection::OrderSearch(const int start,ENUM_ORDER_TYPE &order_type)
  {
   ulong order_ticket=0;
#ifdef __MQL5__
   for(int i=start-1;i>=0;i--)
     {
      ulong ticket=::HistoryOrderGetTicket(i);
      if(ticket==0)
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(ticket,ORDER_TYPE);
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#else 
   for(int i=start-1;i>=0;i--)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
         continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      ulong ticket=::OrderTicket();
      if(ticket==0 || type<ORDER_TYPE_BUY_LIMIT || type>ORDER_TYPE_SELL_STOP)
         continue;
      if(this.IsPresentOrderInList(ticket,type))
         continue;
      order_ticket=ticket;
      order_type=type;
     }
#endif    
   return order_ticket;
  }
//+------------------------------------------------------------------+

MQL5用のコードは#ifdef __MQL5__ディレクティブで囲まれています。MQL4用のコードは#else#endifに囲まれています。

次のエラーはCEventクラスコンストラクタにあります。同じ条件付きコンパイルディレクティブを使用してコードを補完します。

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif;
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

口座が「ヘッジ」タイプかどうかを確認する際に、定数欠落によるエラーが発生します。MetaTrader 4ではすべての口座がヘッジであるため、単に直ちにtrueを返します。
また、口座通貨の小数点以下の桁数を受け取ると、2が返されます。MQL4はこの値を取得できないためです。

次にコンパイルすると、CEventsCollection::NewDealEventHedge()メソッドのエラーが浮かびます。これは、MetaTrader 5ヘッジ口座のイベントを受信するメソッドです。これにはMQL4には存在しない取引が使用されます。すべてのメソッドコードを条件付きコンパイルのディレクティブで囲むことにより、メソッドを一時的に無効にします。

ディレクティブをメソッドの先頭と

//+------------------------------------------------------------------+
//| Create a hedging account event                                   |
//+------------------------------------------------------------------+
void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market)
  {
#ifdef __MQL5__
   double ask=::SymbolInfoDouble(deal.Symbol(),SYMBOL_ASK);
   double bid=::SymbolInfoDouble(deal.Symbol(),SYMBOL_BID);
   //--- Market entry

メソッドの最後に挿入します。

#endif 
  }
//+------------------------------------------------------------------+

次に、CEventsCollection::NewDealEventNetto()メソッドでエラーが発生します。これは、ネッティング口座のイベントを作成するメソッドです。解決策は前の場合と同じです。NewDealEventNetto()メソッドコード全体を条件付きコンパイルディレクティブで囲みます。

コンパイルすると、次に発生するのはCEventsCollection::GetListAllDealsInByPosID()メソッドでのDEAL_ENTRY_IN不明な定数エラーです。ToMQL4.mqhファイルに必要な列挙を追加します(条件付きコンパイルを再度使用してコードを無効にもできますが、後でこの列挙が必要になる場合があります)。

//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
enum ENUM_DEAL_TYPE
  {
   DEAL_TYPE_BUY,
   DEAL_TYPE_SELL,
   DEAL_TYPE_BALANCE,
   DEAL_TYPE_CREDIT,
   DEAL_TYPE_CHARGE,
   DEAL_TYPE_CORRECTION,
   DEAL_TYPE_BONUS,
   DEAL_TYPE_COMMISSION,
   DEAL_TYPE_COMMISSION_DAILY,
   DEAL_TYPE_COMMISSION_MONTHLY,
   DEAL_TYPE_COMMISSION_AGENT_DAILY,
   DEAL_TYPE_COMMISSION_AGENT_MONTHLY,
   DEAL_TYPE_INTEREST,
   DEAL_TYPE_BUY_CANCELED,
   DEAL_TYPE_SELL_CANCELED,
   DEAL_DIVIDEND,
   DEAL_DIVIDEND_FRANKED,
   DEAL_TAX
  };
//+------------------------------------------------------------------+
//| Position change method                                           |
//+------------------------------------------------------------------+
enum ENUM_DEAL_ENTRY
  {
   DEAL_ENTRY_IN,
   DEAL_ENTRY_OUT,
   DEAL_ENTRY_INOUT,
   DEAL_ENTRY_OUT_BY
  };
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
enum ENUM_POSITION_TYPE
  {
   POSITION_TYPE_BUY,
   POSITION_TYPE_SELL
  };
//+------------------------------------------------------------------+

次に来るのは、口座が「ヘッジ」タイプであるかを確認するというすでにおなじみのエラーですが、今回のエラーはイベントコレクションクラスコンストラクタにあります。修正しましょう

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_chart_id=::ChartID();
   ::ZeroMemory(this.m_tick);
  }
//+------------------------------------------------------------------+

次に、CEngineクラスコンストラクタを同じく修正します。

//+------------------------------------------------------------------+
//| CEngineコンストラクタ                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_acc_trade_event(TRADE_EVENT_NO_EVENT)
  {
   ::ResetLastError();
   if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
      Print(DFUN,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
  }
//+------------------------------------------------------------------+

準備完了です。これで、ライブラリはエラーなしでコンパイルされます。しかし、これは最初の段階にすぎません。起動できなければなりません。条件付きコンパイルを使用して無効にしたメソッドを、MetaTrader 4で動作するように開発する必要があります。

MQL5では、残高操作は取引です。それらは、過去の注文と取引のリストにあります。MQL4では、残高操作はORDER_TYPE_BALANCE (6)とORDER_TYPE_CREDIT (7)型の注文です。そのため、過去の注文とポジションのリストに保存されるMQL4の残高操作オブジェクトの個別のクラスを作成しました。

HistoryBalance.mqhファイルの\MQL4\Include\DoEasy\Objects\Orders に新しいCHistoryBalance クラスを作成します。基本クラスはCOrderです。

//+------------------------------------------------------------------+
//|                                               HistoryBalance.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| ファイルをインクルードする                                          |                                              |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Historical balance operation                                     |
//+------------------------------------------------------------------+
class CHistoryBalance : public COrder
  {
public:
   //--- コンストラクタ
                     CHistoryBalance(const ulong ticket) : COrder(ORDER_STATUS_BALANCE,ticket) {}
   //--- サポートされる取引プロパティ: (1)実数、(2)整数
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property);
  };
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| その他の場合は「false」を返す                                       |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TICKET      ||
      property==ORDER_PROP_TIME_OPEN   || 
      property==ORDER_PROP_STATUS      ||
      property==ORDER_PROP_TYPE        ||
      property==ORDER_PROP_REASON
     ) return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| その他の場合は「false」を返す                                       |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   return(property==ORDER_PROP_PROFIT ? true : false);
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| その他の場合は「false」を返す                                       |
//+------------------------------------------------------------------+
bool CHistoryBalance::SupportProperty(ENUM_ORDER_PROP_STRING property)
  {
   if(property==ORDER_PROP_SYMBOL || property==ORDER_PROP_EXT_ID)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

このクラスには、新しいものは何も含まれていません。過去の注文のクラスはすべて第2部で確認しました。

バランス操作には、バランス操作とクレジット操作の2種類があります。したがって、それらのタイプには6と7の数値があります。両方のタイプに単一のバランス操作クラスを使用し、「reason」注文プロパティで特定のタイプを明確にします。

ToMQL4.mqhファイルに不足している2つの「理由」を追加します。

//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY         (8)
#define ORDER_TYPE_BUY_STOP_LIMIT   (9)
#define ORDER_TYPE_SELL_STOP_LIMIT  (10)
#define ORDER_FILLING_RETURN        (2)
#define ORDER_TIME_GTC              (0)
#define ORDER_REASON_EXPERT         (3)
#define ORDER_REASON_SL             (4)
#define ORDER_REASON_TP             (5)
#define ORDER_REASON_BALANCE        (6)
#define ORDER_REASON_CREDIT         (7)
//+------------------------------------------------------------------+

抽象順序クラスから新しいクラスが派生しているため、COrderに不足している機能を追加する必要があります。

COrder::OrderPositionID()メソッドで、MQL4ではマジックナンバーを返すかわりに

//+------------------------------------------------------------------+
//| ポジションIDを返す                                                 |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else

チケットを返します(MQL4ポジションの一種のPositionIDは後で実装されます)。

//+------------------------------------------------------------------+
//| ポジションIDを返す                                                 |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else

MQL4で注文ステータスを返すメソッドは、常にENUM_ORDER_STATE列挙からORDER_STATE_FILLEDを返しますが、これはリモートの未決注文には当てはまりません。注文ステータスチェックを実装し、これがリモートの未決注文である場合は、ORDER_STATE_CANCELEDを返します。

//+------------------------------------------------------------------+
//| 注文状態を返す                                                     |
//+------------------------------------------------------------------+
long COrder::OrderState(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_ORDER ? ORDER_STATE_FILLED : ORDER_STATE_CANCELED);
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_STATE); break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_STATE);                 break;
      case ORDER_STATUS_MARKET_POSITION   : 
      case ORDER_STATUS_DEAL              : 
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

2つの新しく追加された「理由」をMQL4の注文理由を返すメソッドに追加します。

//+------------------------------------------------------------------+
//| 注文の理由またはソース                                              |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.TypeOrder()==ORDER_TYPE_BALANCE   ?  ORDER_REASON_BALANCE :
      this.TypeOrder()==ORDER_TYPE_CREDIT    ?  ORDER_REASON_CREDIT  :
      this.OrderCloseByStopLoss()            ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit()          ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0             ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 

ここでの場合、MQL4の未実行ボリュームを返すメソッドは常に注文ロットを返しますが、これはポジションに対しては正しくありません。リモート未決注文の場合はロット注文を返し、ポジションの場合はゼロを返します。

//+------------------------------------------------------------------+
//| 実行されていないボリュームを返す                                     |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return(this.Status()==ORDER_STATUS_HISTORY_PENDING ? ::OrderLots() : 0);
#else 

注文理由の説明を返すメソッドに2つの新しい「理由」の説明を追加します。残高およびクレジット操作の場合は利益を確認します。利益がゼロを超える場合、資金は入金され、そうでない場合は出金されます。

//+------------------------------------------------------------------+
//| Reason description                                               |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      this.Comment()=="cancelled"         ?  TextByLanguage("Отменён","Cancelled")                                      :
      this.Reason()==ORDER_REASON_BALANCE ?  (
                                              this.Profit()>0 ? TextByLanguage("Пополнение баланса","Deposit of funds on the account balance") :
                                              TextByLanguage("Снятие средств с баланса","Withdrawal from the balance")
                                             )                                                                          :
      this.Reason()==ORDER_REASON_CREDIT  ?  (
                                              this.Profit()>0 ? TextByLanguage("Начисление кредитных средств","Received credit funds") :
                                              TextByLanguage("Изъятие кредитных средств","Withdrawal of credit")
                                             )                                                                          :
                                             
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 

このほかに、いくつかの小規模な編集が行われましたが、ここで説明するほど重要ではありません。それらは主にMQL5/MQL4操作ログに表示されるテキストに関連しています。すべての編集は、記事に添付されているライブラリファイルでご覧になれます。

ここで、HistoryCollection.mqhファイルの履歴コレクションクラスを改善しましょう。
まず、新しいクラスをインクルードします

//+------------------------------------------------------------------+
//| ファイルをインクルードする                                          |                                              |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\HistoryOrder.mqh"
#include "..\Objects\Orders\HistoryPending.mqh"
#include "..\Objects\Orders\HistoryDeal.mqh"
#ifdef __MQL4__
#include "..\Objects\Orders\HistoryBalance.mqh"
#endif 
//+------------------------------------------------------------------+

ライブラリのMQL4バージョンのみにCHistoryBalanceクラスが必要なので、このクラスのファイルのインクルードはMQL4用の条件付きコンパイルディレクティブに含まれています。

これで、新しい残高操作クラスができました。開発してコレクションに入れるには、残高捜査とクレジット操作の注文タイプのチェックを追加し、MQL4用にCHistoryCollectionクラスのRefresh()メソッドのコレクションに追加する必要があります。

//+------------------------------------------------------------------+
//| 注文とポジションのリストを更新する                                   |
//+------------------------------------------------------------------+
void CHistoryCollection::Refresh(void)
  {
#ifdef __MQL4__
   int total=::OrdersHistoryTotal(),i=m_index_order;
   for(; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType();
      //--- Closed positions
      if(order_type<ORDER_TYPE_BUY_LIMIT)
        {
         CHistoryOrder *order=new CHistoryOrder(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      //--- Balance/credit operations
      else if(order_type>ORDER_TYPE_SELL_STOP)
        {
         CHistoryBalance *order=new CHistoryBalance(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
      else
        {
         //--- 削除済み未決注文  
         CHistoryPending *order=new CHistoryPending(::OrderTicket());
         if(order==NULL) continue;
         if(!this.m_list_all_orders.InsertSort(order))
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Could not add order to the list"));
            delete order;
           }
        }
     }
//---
   int delta_order=i-m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   this.m_is_trade_event=(this.m_delta_order!=0 ?true : false);
//--- __MQL5__
#else 
注文履歴クラスにいくつかの修正を加えましょう。
//+------------------------------------------------------------------+
//| Return 'true' if an order supports the passed                    |
//| その他の場合は「false」を返す                                       |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(
   #ifdef __MQL5__
      property==ORDER_PROP_PROFIT                  || 
      property==ORDER_PROP_PROFIT_FULL             || 
      property==ORDER_PROP_SWAP                    || 
      property==ORDER_PROP_COMMISSION              ||
      property==ORDER_PROP_PRICE_CLOSE             ||
      (
       property==ORDER_PROP_PRICE_STOP_LIMIT       && 
       (
        this.TypeOrder()<ORDER_TYPE_BUY_STOP_LIMIT || 
        this.TypeOrder()>ORDER_TYPE_SELL_STOP_LIMIT  
       )
      )
   #else
      property==ORDER_PROP_PRICE_STOP_LIMIT        && 
      this.Status()==ORDER_STATUS_HISTORY_ORDER
   #endif 
     ) return false;

   return true;
  }
//+------------------------------------------------------------------+

以前、MQL5ではStopLimit注文価格は操作ログに渡されていませんでした。したがって、チェックを実装しました。チェックされたプロパティがStopLimit注文価格であって注文タイプがStopLimitではない場合、プロパティは使用されません。それ以外の場合、これはStopLimit注文であり、プロパティが必要です。
MQL4では、
StopLimit注文価格はポジションでは使用されません。

これで、MQL4との互換性の第1段階の改善が完了しました。

テスト

テストのために、\MQL5\Experts\TestDoEasy\Part03にあるTestDoEasyPart03_1.mq5 EAを、MQL4 EAのフォルダー(\MQL4\Experts\TestDoEasy\Part09)にTestDoEasyPart09.mq4という名前で保存します。

EAは変更なしでコンパイルされますが、コードを見ると、MQL4にない取引のリストが使用されていることがわかります。

//--- 列挙体
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // 成行注文
   TYPE_ORDER_PENDING,  // 未決注文
   TYPE_ORDER_DEAL      // 取引
  };
//--- 入力パラメータ

//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 履歴を更新する
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- リストから注文を取得する
      COrder* order=list.At(i);
      if(order==NULL) continue;
      //--- これが取引の場合
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
      //--- これが過去の成行注文の場合
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
      //--- これが削除済み未決注文の場合
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

取引を残高操作に置き換えるだけです。この場合、EAで条件付きコンパイルを直接使用します。最終的な製品では、言語別の違いはユーザーに見えるべきではないため、正しくありません。しかし、ここではライブラリの改善結果をテストするだけなので、大したことではありません。

EAコードに小さな変更を追加して、MQL5取引MQL4残高操作</ s1>に置き換えましょう。

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_1.mq4 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ja/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
//--- include
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- 列挙体
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // 成行注文
   TYPE_ORDER_PENDING,  // 未決注文
#ifdef __MQL5__
   TYPE_ORDER_DEAL      // 取引
#else 
   TYPE_ORDER_BALANCE   // Balance/Credit
#endif 
  };
//--- 入力パラメータ
input ENUM_TYPE_ORDERS  InpOrderType   =  TYPE_ORDER_MARKET;   // Show type:
input datetime          InpTimeBegin   =  0;                   // Start date of required range
input datetime          InpTimeEnd     =  END_TIME;            // End date of required range
//--- グローバル変数
CHistoryCollection history;
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 履歴を更新する
   history.Refresh();
//--- get the collection list within the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- リストから注文を取得する
      COrder* order=list.At(i);
      if(order==NULL) continue;
   //--- これが取引の場合
   #ifdef __MQL5__
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
   #else 
   //--- if this is a balance/credit operation
      if(order.Status()==ORDER_STATUS_BALANCE && InpOrderType==TYPE_ORDER_BALANCE)
         order.Print();
   #endif 
   //--- これが過去の成行注文の場合
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
   //--- これが削除済み未決注文の場合
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| エキスパート初期化解除関数                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| エキスパートティック関数                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

EAをコンパイルしてターミナルで起動します(第3部のテストEAはOnInit()ハンドラーでのみ機能するため、起動後または設定でのリストの変更後に必要な履歴コレクションリストが表示されます)。

EAを起動する前に、ターミナルの[アカウント履歴]タブのコンテキストメニューで[すべての履歴]オプションを選択します。MetaTrader4では、アプリケーションで利用可能な履歴の量はタブで選択した履歴サイズに依存するためです。

設定で残高/クレジットが選択され、最初の残高が操作ログに表示されます。


ここで、決済したポジションの検索と表示が正しいかどうかを確認する必要があります。MetaTrader 4アカウントを最近開設したので、取引がありませんでした。売りを開き、StopLossとTakeProfitを設定して、コーヒーを入れに行きました。戻ると、ポジションはストップロスによって決済され、市場が売りポジションの方向に動き始めていました。いつもこんな具合です。:)

しかし、ポジションが決済されたのでテストに使えます。
設定で[Market orders(成行注文)]が選択されます。


次に、削除された未決注文のリストを確認しましょう。いくつかの注文を設定し、その後削除しました。
設定で[Pending orders(指値注文)]が選択されます。


削除された指値注文のリストも表示されます。

次の段階

次の記事では、MQL4で市場ポジションとアクティブな未決注文を処理する機能を実装します。

現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。

目次に戻る

シリーズのこれまでの記事:

第1部: 概念、データ管理
第2部: 過去の注文と取引のコレクション
第3部:注文と取引のコレクション、検索と並び替え
第4部: 取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部: ネッティング勘定イベント
第7部: StopLimit注文アクティブ化イベント。注文イベントと位置変更イベントの機能を準備します。
第8部: 注文とポジションの変更イベント。