MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第1部)概念、データ管理および最初の結果

Artyom Trishkin | 7 5月, 2019

内容

はじめに

膨大な数の取引戦略やMetaTrader 5およびMetaTrader 4ターミナル用アプリケーションの開発の注文、およびMetaTrader用のスクリプトや指標、ロボットに関するさまざまなWebサイトを分析するうちに、私は、このすべての多様性のほとんどが、異なるプログラムで定期的に現れる同じ基本的な機能、行動、および価値観に基づいているという結論に達しました。

実際、どのようなプログラムでも、そのロジックは多くの同一のアクションに分割することができます。これらのアクションの結果は、アプリケーションのロジックを構築するために使用されます。これはMQL4/MQL5フォーラムに寄せられた質問の統一性によって繰り返し確認されています。異なるユーザが、解決するアルゴリズムとタスクに関して本質的に同じ質問をしています。

これらすべてを考慮して、私は必要なデータを要求し取得するための組み込み関数を備えた大きなライブラリを開発することにしました。提案されたライブラリのデータセットを使用するには、発問法を使ってライブラリが収集してデータベースに保存する膨大な量の完全に異なるデータを(異なる組み合わせパラメータと並び替えパラメータで)取得するだけです。

概念

データ型はどのようなものでも、同じプロパティを持つ一連のオブジェクトとして表すことができます。
例えば、時系列は、長い順序付きリストとして表すことが可能で、その各後続セルは、時系列内の同様のオブジェクトに属する他のすべてのセットと同じ型のプロパティのセットを持つオブジェクトを格納します。そのようなオブジェクトのデータはMqlRates構造体で表されます。

以下は価格、数量、スプレッドの情報を格納するための構造体です。

struct MqlRates 
  { 
   datetime time;         // 期間開始時
   double   open;         // 始値
   double   high;         // 期間の最高値
   double   low;          // 期間の最低値
   double   close;        // 終値
   long     tick_volume;  // ティックボリューム
   int      spread;       // スプレッド
   long     real_volume;  // 取引量
  };

ティックデータのセットは、MqlTick構造体で表されるティックが固定されたプロパティのセットを持つオブジェクトである順序付きリストとして表すこともできます。

ラストプライスを銘柄で格納するための構造体は、現在の価格に関する最も必要なデータをタイムリーに取得するように設計されています。

struct MqlTick 
  { 
   datetime     time;          // ラストプライスの更新時刻
   double       bid;           // 現在の買呼値
   double       ask;           // 現在の売呼値
   double       last;          // 最後の約定の現行価格(ラスト)
   ulong        volume;        // 現在のラストプライスの数量
   long         time_msc;      // ミリ秒単位でのラストプライス更新時刻
   uint         flags;         // ティックフラグ
   double       volume_real;   // 現在のラストプライスの数量(高精度)
  };

プログラムロジックの分析と準備に必要なその他のデータも、単純なオブジェクトリストとして整理されています。

1つのリスト内のすべてのオブジェクトは、それが注文のリスト、取引、または未決注文のいずれであるかにかかわらず、この型のオブジェクトに固有の同じ型のデータを持ちます。特定のオブジェクトごとに、データの格納、並び替え、および表示に必要最小限の機能を備えたクラスを開発します。

ライブラリデータ構造体

すでに説明したように、ライブラリはオブジェクトリストで構成され、そのオブジェクトでサポートされている任意のカスタム条件またはプロパティによって任意のリスト項目を選択する機能を提供します。ライブラリは、保管および取り扱いに必要なデータを独自に収集します。人間の介入は必要ありません。ユーザは自分のライブラリクエリの結果のみを適用します。

最も単純なトピックから始めて、既存の機能とデータに新しい機能とデータを徐々に追加することから、ライブラリを開発するすべてのステップを説明します。ライブラリは「ライブ」モードで開発されます。必要な編集と追加は各記事で実装されます。私は、このスタイルのプレゼンテーションは読者を開発に巻き込むので最も有用であると思います。

ライブラリデータの最小構造は、必要なデータのプロパティを記述するための一連の異なるオブジェクトです。データコレクションは、対応するオブジェクトを格納するリストです。

リストを配置するためには、標準ライブラリのデータコレクションからのCOBjectクラスインスタンスとその派生クラスへのポインタの動的配列のクラスを使用します。そのようなリストに格納するにはCObject標準ライブラリの基本クラスのオブジェクトが必要なので、オブジェクトの各クラスをCObject基本クラスから継承します。

初期実装

まず、MetaTrader 5ターミナルのヘッジアカウント用のライブラリを開発します。最低限の機能を準備した後、その動作をMetaTrader 4に合わせて調整します。次に、口座履歴、現在の市場ポジション、および未決注文の処理を実装した後、MetaTrader 5ネッティング口座で作業する機能を追加します。最後に、ライブラリにさまざまな関数を追加します。

アカウント履歴から始めましょう。多くの戦略は、過去の取引結果、決済方法(ストップロス、テイクプロフィット)、価格など、過去の取引の結果を何らかの方法で適用します。また、最終日の結果は現在の作業の開始点として使用できます。取引戦略の種類は限りなく、私たちの役目はすべての多様性に迅速なアクセスを提供することです。

まず、過去の注文、取引、成行注文、ポジションのコレクションを扱うための用語を定義しましょう。MetaTrader 4の注文システムはMetaTrader 5の注文システムとは異なります。MetaTrader 4には成行注文と未決注文がありますが、MetaTrader 5では、注文は基本的に取引を生成する取引要求(成行注文)である一方、取引はポジションを生成します。その上、未決注文があります。言い換えれば、注文、取引、ポジションの3つのオブジェクトがあります。名前や定義に煩わされないようにするために、注文、取引、ポジション別にデータを格納するためのクラスを単純に抽象注文クラスと呼びましょう。さらに、私達のリスト(記事の冒頭で述べた歴史注文のコレクション)では、すべてが型(注文、取引など)によって分類されます。

注文抽象クラスは、注文または取引チケット、ならびに注文または取引のパラメータおよびそのステータスに関するデータ全体を含むことです。ステータスは、オブジェクトが正確に何であるか(注文、取引またはポジション)を示します。

<terminal data folder>\MQL5\Includeで、ライブラリファイルを格納するためのDoEasyフォルダを作成します。この段階で、DoEasyフォルダーにはさらに2つのディレクトリが必要になります。 Objectsフォルダにはオブジェクトクラス、Collectionsにはデータコレクション(オブジェクトリスト)が格納されます。
ターミナルデータディレクトリを見つけるには、[ファイル] -> [データフォルダを開く](Ctrl + Shift + D)の順に進みます。

これで、最初のクラス(抽象注文クラス)を作成できます。Objectsフォルダに新しいクラスを作成して、COrder (1)と名付けます。標準ライブラリのCObject (2)クラスを必ず基本クラスに設定してください。この場合、新しいオブジェクトクラスはCObjectクラスから継承され、それを標準ライブラリのCArrayObjオブジェクトのリストに入れることができます。


[完了]をクリックすると、新しいOrder.mqh (3) ファイルがライブラリディレクトリのObjectsフォルダに表示されます。 今のところ、これは単なるクラスワークピースです。

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ja/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

コードをコンパイルしようとすると、5つのエラーが発生します。これらは、COrderクラスの派生元であるCObjectクラスが存在しないことを示しています。CObjectクラスファイルをリストに含めてもう一度コンパイルすれば、エラーは発生しません。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

次の注文、取引またはポジションが選択されると、クラスのオブジェクトは注文、取引またはポジションによってループ内に作成されます。オブジェクトのフィールドをすぐに初期化するために、プライベートのコンストラクタを作成します。オブジェクトの種類(ステータス)とチケットは、後で識別するために渡されます。しかし、最初に、新しいDefines.mqhインクルードファイルをプロジェクトルートフォルダに配置しましょう。このフォルダには、ライブラリに必要なすべての列挙型、マクロ置換、定数、および構造体が格納されています。

現在、注文状況を記述する列挙と注文、取引またはポジションのすべてのパラメータを記述する列挙が必要です。注文パラメータにはintegerrealstringの3つの列挙体があります。

//+------------------------------------------------------------------+
//|                                                      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"
//+------------------------------------------------------------------+
//| 抽象注文型(ステータス)                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // 現在の未決注文
   ORDER_STATUS_MARKET_ACTIVE,                              // アクティブな成行注文
   ORDER_STATUS_HISTORY_ORDER,                              // 履歴成行注文
   ORDER_STATUS_HISTORY_PENDING,                            // 削除された未決注文
   ORDER_STATUS_BALANCE,                                    // 残高操作
   ORDER_STATUS_CREDIT,                                     // クレジット操作
   ORDER_STATUS_DEAL,                                       // 取引
   ORDER_STATUS_UNKNOWN                                     // 未知のステータス
  };
//+------------------------------------------------------------------+
//| 注文、取引、ポジションの整数型プロパティ                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // 注文チケット
   ORDER_PROP_MAGIC,                                        // 注文マジック
   ORDER_PROP_TIME_OPEN,                                    // 開始時間
   ORDER_PROP_TIME_CLOSE,                                   // 決済時間
   ORDER_PROP_TIME_EXP,                                     // 注文有効期限(未決注文用)
   ORDER_PROP_TYPE,                                         // 注文と取引タイプ
   ORDER_PROP_STATUS,                                       // 注文ステータス(ENUM_ORDER_STATUS列挙体から)
   ORDER_PROP_REASON,                                       // 取引/注文/ポジション理由またはソース
   ORDER_PROP_POSITION_ID,                                  // ポジションID
   ORDER_PROP_POSITION_BY_ID,                               // 反対側のポジションID
   ORDER_PROP_DEAL_ORDER,                                   // 取引が実行された注文
   ORDER_PROP_DEAL_ENTRY,                                   // 取引の方向 – IN、OUT、IN/OUT
   ORDER_PROP_TIME_OPEN_MSC,                                // 開始時間(ミリ秒)
   ORDER_PROP_TIME_CLOSE_MSC,                               // 決済時間(ミリ秒)(実行または削除の時間 - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_UPDATE,                                  // ポジション変更時間(秒)
   ORDER_PROP_TIME_UPDATE_MSC,                              // ポジション変更時間(ミリ秒)
   ORDER_PROP_TICKET_FROM,                                  // 親注文チケット
   ORDER_PROP_TICKET_TO,                                    // 派生注文チケット
   ORDER_PROP_PROFIT_PT,                                    // 利益(ポイント)
   ORDER_PROP_CLOSE_BY_SL,                                  // ストップロスによる決済のフラグ
   ORDER_PROP_CLOSE_BY_TP,                                  // テイクプロフィットによる決済のフラグ
   ORDER_PROP_DIRECTION,                                    // 方向(買または売)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // 整数型プロパティの総数
//+------------------------------------------------------------------+
//| 注文、取引、ポジションの実数型プロパティ                            |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // 始値(MQL5取引価格)
   ORDER_PROP_PRICE_CLOSE,                                  // 終値
   ORDER_PROP_PROFIT,                                       // 利益
   ORDER_PROP_COMMISSION,                                   // 手数料
   ORDER_PROP_SWAP,                                         // スワップ
   ORDER_PROP_VOLUME,                                       // ボリューム
   ORDER_PROP_VOLUME_CURRENT,                               // 実行されていないボリューム
   ORDER_PROP_SL,                                           // ストップロス価格
   ORDER_PROP_TP,                                           // テイクプロフィット価格
   ORDER_PROP_PROFIT_FULL,                                  // 利益+手数料+スワップ
   ORDER_PROP_PRICE_STOP_LIMIT,                             // ストップリミット注文が発動した際のリミット注文価格
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // 実数型プロパティの総数
//+------------------------------------------------------------------+
//| 注文、取引、ポジションの文字列型プロパティ                          |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // 注文銘柄
   ORDER_PROP_COMMENT,                                      // 注文コメント
   ORDER_PROP_EXT_ID                                        // 外部取引システムの注文ID
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // 文字列型プロパティの総数
//+------------------------------------------------------------------+

親注文チケット、派生注文チケット、利益(ポイント)、ストップロスまたはテイクプロフィットによる決済のプロパティ、手数料およびスワップを考慮した方向および全利益のプロパティが追加されました。これらのデータは取引戦略ロジックでよく使用されるため、抽象注文フィールドに格納するだけでなく、カスタムプログラムで更新してすぐに受信する必要があります。

すべての注文プロパティ(整数、実数、文字列)をまとめるには、各プロパティタイプの3つの列挙のそれぞれに含まれるパラメータの数を含むマクロ置換を作成します。
整数型プロパティの列挙型の番号付けはゼロから始まりますが、他のプロパティの列挙型の番号付けは前のプロパティの総数から始まります。したがって、必要なプロパティのインデックスは常に、要求されたプロパティの数とこの列挙の初期プロパティの数の差として取得できます。

Defines.mqhファイルを作成した後は、COrderクラスの現在のファイルに接続してprivateセクションに注文チケットを格納するためのクラスメンバ変数と整数実数文字列型注文プロパティを格納するための3つの配列を作成します。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // 選択された注文/取引チケット(MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // 整数型プロパティ
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // 実数型プロパティ
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // 文字列型プロパティ
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Defines.mqhファイルをインクルードするには、CObjectをインクルードするときに使用した山括弧(<>)を適用するのではなく、ファイルへの相対パスを引用符で囲みます。これは、ライブラリを他のディレクトリに転送するときに、ファイル間の接続が失われず、常に現在のディレクトリに対するDefines.mqhファイルの場所を参照するようにするためです。

それでは、同じprivateセクションに2つのメソッドを作成しましょう。1つ目はプロパティ配列内の必要なプロパティの正確な位置を返します。2つ目は対応する保護されたセクション内にprotectedコンストラクタを返します。空の注文オブジェクトを作成するためのデフォルトのコンストラクタをプロパティを初期化せずにそのままにして、デストラクタを削除します(デストラクタは必要ありません。コンパイル時に自動的に作成されます)。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象注文クラス                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 選択された注文/取引チケット(MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数プロパティ
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 実数プロパティ
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 文字列プロパティ
   
   //--- 実数プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                  }
   //--- 文字列プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- デフォルトのコンストラクタ
                     COrder(void){;}
protected:                                                                     
   //--- 保護されたパラメトリックコンストラクタ                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

これまでのところ、保護されたコンストラクタは何もしません。これは、オブジェクトの作成時に、そのチケットがクラスコンストラクタに渡される注文のすべてのプロパティをすぐに初期化するために使用されます。注文はすでに選択されているので、選択された注文の必要なプロパティを取得してオブジェクトプロパティの配列に書き込むことができます。これは注文データを受信して返すためのメソッドを作成した少し後で行います。

これはクロスプラットフォームのライブラリなので、注文のプロパティを取得するためのメソッドを別に作成するほうが便利です。保護されたセクションに、選択した注文の整数実数文字列プロパティを受け取るためのメソッドの説明を追加します。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象注文クラス                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 選択された注文/取引チケット(MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数プロパティ
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 実数プロパティ
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 文字列プロパティ
   
   //--- 実数プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- 文字列プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- デフォルトのコンストラクタ
                     COrder(void){;}
protected:
   //--- 保護されたパラメトリックコンストラクタ
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- 選択した注文の整数プロパティをそのパラメータから取得して返す
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- 選択した注文の実数プロパティをそのパラメータから取得して返す: (1) 始値、(2)終値、(3)利益
   //---  (4)手数料、(5)スワップ、(6)ボリューム、(7)実行されていないボリューム、(8)ストップロス価格、(9)テイクプロフィット価格、(10)ストップリミット注文価格
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- 選択した注文の文字列プロパティをパラメータから取得して返す: (1)銘柄、(2)コメント、(3)取引所でのID
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| Closedパラメトリックコンストラクタ                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

現在、プロパティを受け取るためのメソッドのみが宣言されています。クラスはエラーなしでコンパイルされますが、それらはまだ実装されていません。注文プロパティ配列は、先ほど追加したメソッドを使用してクラスコンストラクタで書き入れられます。すべての準備が整ったら、これらの配列によって、要求に応じてプログラムで任意のプロパティを取得できるようになります。

指定したプロパティで仮想メソッドを比較するオブジェクトは、標準ライブラリのCObjectクラスで宣言されています。ただし、このメソッドはサブクラスに実装する必要があります。したがって、抽象注文クラスに、その任意のプロパティに従ってCOrderオブジェクトを比較するためのメソッド、および注文プロパティにアクセスするためのいくつかのパブリックメソッド注文オブジェクトの特定のプロパティをサポートするためのフラグを返す仮想メソッドを追加しましょう。これらのメソッドは、COrderクラスの下位オブジェクトに実装されています。これは、後でいずれかのプロパティでコレクションリストから注文を選択するために必要になります。デフォルトでは、これらのメソッドのいずれかが下位クラスに実装されていない場合、そのプロパティに対する注文のサポートを示すフラグが返されます。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象注文クラス                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 選択された注文/取引チケット(MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数プロパティ
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 実数プロパティ
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 文字列プロパティ
   
   //--- 実数プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- 文字列プロパティが実際に配置されている配列インデックスを返す
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- デフォルトのコンストラクタ
                     COrder(void){;}
protected:
   //--- 保護されたパラメトリックコンストラクタ
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- 選択した注文の整数プロパティをそのパラメータから取得して返す
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- 選択した注文の実数プロパティをそのパラメータから取得して返す: (1) 始値、(2)終値、(3)利益
   //---  (4)手数料、(5)スワップ、(6)ボリューム、(7)実行されていないボリューム、(8)ストップロス価格、(9)テイクプロフィット価格、(10)ストップリミット注文価格
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- 選択した注文の文字列プロパティをパラメータから取得して返す: (1)銘柄、(2)コメント、(3)取引所でのID
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- プロパティ配列から(1)整数、(2)実数、(3)文字列の注文プロパティを返す
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }
   
   //--- プロパティをサポートしている注文のフラグを返す
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }
   
   //--- COrderオブジェクトをすべての可能なプロパティで比較する
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| Closedパラメトリックコンストラクタ                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

指定されたプロパティで2つの注文を比較するためのメソッドを実装します。

//+------------------------------------------------------------------+
//| COrderオブジェクトをすべての可能なプロパティで比較する                |
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- 2つの注文の整数プロパティを比較する
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 2つの注文の実数プロパティを比較する
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 2つの注文の文字列プロパティを比較する
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

このメソッドは、そのプロパティが特定の与えられた値と比較されなければならない注文オブジェクトへのポインタ、ならび、注文プロパティの列挙体からの自体を渡します。
システムは、注文の値が比較された値より大きい場合には1、比較された値より小さい場合は-1、その他の場合には0を返します。比較が行われる注文のリストは、比較対象のプロパティであらかじ並びかえられている必要があります

それでは、注文プロパティを受け取り、それらをプロパティ配列に書き込むためのメソッドを実装しましょう。これらは、privateセクションで以前に宣言されたメソッドです。注文プロパティを受け取るメソッドはクロスプラットフォームなので、例としてEAのマジックナンバーを受け取りで分析しましょう。

//+------------------------------------------------------------------+
//| マジックナンバーを返す                                              |
//+------------------------------------------------------------------+
long COrder::OrderMagicNumber() const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_MAGIC);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_MAGIC);                 break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_MAGIC);   break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_MAGIC); break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

このコードがMQL4用の場合、マジックナンバーはOrderMagicNumber() MQL5関数によって返されます。その他の場合、注文状態を確認します。扱っているものに応じて、ポジション、注文、または取引マジックナンバーのいずれかを返します。

強調表示された注文/取引/ポジションの特性を読み書きする残りのメソッドも同様に行われます。これらは自分自身で分析してください。

注文/取引/ポジションの整数プロパティを取得するためのメソッド:
//+------------------------------------------------------------------+
//| チケットを返す                                               |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 親注文チケットを返す                                   |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| 子注文チケットを返す                                   |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| ポジションIDを返す                                               |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 反対側のポジションIDを返す                                 |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 開始時間(ミリ秒)を返す                                 |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 終了時間(ミリ秒)を返す                                |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#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_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 型を返す                                                  |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 方向別に型を返す                                     |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| 実行タイプを返す                                 |
//+------------------------------------------------------------------+
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_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_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_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      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 
  }
//+------------------------------------------------------------------+
//| 取引が実行された注文                        |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 取引の方向 – IN, OUT, IN/OUT                                   |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| ストップロスによる決済のフラグを返す                |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ?this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ?this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| テイクプロフィットによる決済のフラグを返す                |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ?this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ?this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| 開始時間を返す                                                 |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 終了時間を返す                                                |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 有効期限を返す                                          |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| ポジション変更時間(秒)                                  |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| ポジション変更時間(ミリ秒)                             |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

注文/取引/ポジションの実数プロパティを取得するためのメソッド:

//+------------------------------------------------------------------+
//| 始値を返す                                                  |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 終値を返す                                               |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 利益を返す                                                   |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 手数料を返す                                                |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| スワップを返す                                                      |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| ボリュームを返す                                                    |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 実行されていないボリュームを返す                                         |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| ストップロス価格を返す                                            |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| テイクプロフィット価格を返す                                          |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| リミット注文価格を返す                                         |
//| (ストップリミット注文の発動時)                                |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

注文/取引/ポジションの文字列プロパティを取得するためのメソッド:

//+------------------------------------------------------------------+
//| 銘柄を返す                                                   |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| コメントを返す                                                  |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 取引所によって使用されるIDを返す                                   |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

注文データからプロパティを受け取るためのプライベートメソッドを宣言して実装しました。

次に、チケットがコンストラクタに渡された注文のすべてのプロパティを保存するメソッドを書き込んで保護されたクラスコンストラクタを実装します。

保護されたクラスコンストラクタの実装:

//+------------------------------------------------------------------+
//| Closedパラメトリックコンストラクタ                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- 整数プロパティを保存する
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- 実数プロパティを保存する
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- 文字列プロパティを保存する
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- 追加の整数プロパティを保存する
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- 追加の実数プロパティを保存する
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

履歴または市場の注文/取引/ポジションをループで渡す、または新しい注文/取引/ポジションを選択すると、COrderクラスから派生した新しいオブジェクトが作成されます。クラスコンストラクタは注文ステータスとチケットを特徴とし、注文チケットのみを受け取るクラスコンストラクタ内で呼び出されます。次に、上記のメソッドを使用して、COrderクラスコンストラクタで単に注文プロパティの配列が書き入れられます。

したがって、それぞれの新しい注文/取引/ポジションはそれぞれ固有のプロパティを持つことになります。そのようなリストを扱うことがライブラリの主な活動である間、それらのすべてはリストに格納されることになります。リストは注文/取引/ポジションプロパティのいずれかで並び替えできます。その上、選択されたリストから新しいリストを生成することができます。

この段階で、COrder抽象注文クラスの基本機能が実装されました。これは、さまざまな種類の注文、取引、およびポジションを格納するための基本クラスです。注文、取引、およびポジションの種類によって分けられたオブジェクトを作成するための他のすべてのクラスは、このクラスから派生します。

データへのアクセスを簡単にし、プログラム開発を容易にするためにライブラリが作成されます。
現在の状態では、GetProperty(XXX)抽象注文のプロパティにアクセスするための3つのパブリックメソッドがあります。これらは使用できますが、特定の注文プロパティを記述する列挙のメンバ名を覚えている必要があるのであまり便利ではありません。したがって、必要なデータを取得するためのパブリックメソッドをさらにいくつか追加します。これらのメソッドには適切な名前が付けられているため、特定のメソッドを使用してどのプロパティを取得できるかがすぐにわかります。

カスタムプログラムでこれらのメソッドを使用して、選択した注文、取引、またはポジションのプロパティを読み取ることができます。
   //--- (1)チケット、(2)親注文チケット、(3)派生注文チケット、(4)マジックナンバー、(5)注文理由、(6)ポジションID
   //--- (7)反対のポジションID、(8) ストップロスによる決済のフラグ、(9)テイクプロフィットによる決済のフラグ、(10)開始時間、(11)終了時間、
   //--- (12)開始時間(ミリ秒)、(13)終了時間(ミリ秒)、(14)有効期限、(15)タイプ、(16)状態、(17)方向を返す
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- (1)始値、(2)終値、(3)利益、(4)手数料、(5)スワップ、(6)ボリューム、 
   //--- (7)実行されていないボリューム、(8)ストップロス価格、(9)テイクプロフィット価格、(10)ストップリミット注文価格を返す
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- (1)銘柄、(2)コメント、(3)取引所でのIDを返す
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- 完全な注文利益を取得する
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- 注文利益(ポイント)を取得する
   int               ProfitInPoints(void) const;

ポイント単位の利益を受信するメソッドの実装:

//+------------------------------------------------------------------+
//| 注文利益(ポイント)を返す                                    |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

そして、注文オブジェクトのいくつかのプロパティを便利な方法でオンデマンドで表示できるように、パブリックメソッドを追加しましょう。

   //--- 注文の(1)整数、(2)実数、(3)文字列プロパティの説明を取得する
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- 注文状態名を返す
   string            StatusDescription(void) const;
   //--- 注文またはポジションの名前を返す
   string            TypeDescription(void) const;
   //--- 取引方向名を返す
   string            DealEntryDescription(void) const;
   //--- 注文またはポジションの方向を返す
   string            DirectionDescription(void) const;
   //--- 注文プロパティの説明を操作ログに送信する(full_prop=true  - すべてのプロパティ、false-サポートされているプロパティのみ)
   void              Print(const bool full_prop=false);

これらのメソッドを実装する前に、ライブラリとそれに基づくプログラムにはさまざまなサービス関数が必要であるという別の問題を解決しましょう。例えば、この場合、ミリ秒単位で時間を表示する関数と、2つの言語のいずれかでメッセージを受信する関数が必要です。メッセージの言語はターミナルの言語に依存します。

ライブラリのルートディレクトリに新しいインクルードファイルを作成して


DELibと名付けます。これは、ライブラリクラス自体とライブラリベースのプログラムの両方で使用できるサービス関数のライブラリファイルになります。


[完了]をクリックしてテンプレートファイルを作成します。

//+------------------------------------------------------------------+
//|                                                        DELib.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"
//+------------------------------------------------------------------+
//| define                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLLのインポート                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5のインポート                                                   |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Defines.mqhファイルを含めて、必要に応じてテンプレートを変更します。

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // mql4に必要
//+------------------------------------------------------------------+
//| ファイルをインクルードする                                                    |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| サービス関数                                                |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

Defines.mqhをこのファイルにインクルードしたので、(Defines.mqhではなく)この新しいファイルをCOrderクラスファイルにインクルードして、両方をライブラリで使用できるようにすることができます。これは、2つではなく1つの文字列で行います。
Order.mqhファイルのincludeディレクティブを置き換えます。

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| 抽象注文クラス                                             |
//+------------------------------------------------------------------+

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"
//+------------------------------------------------------------------+
//| マクロ置換                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"
//+------------------------------------------------------------------+

したがって、ターミナル言語が英語ではない場合、ユーザは表示されるメッセージに母国語を設定できます。しかし、これを達成するためには、ここに入力する全てのメッセージをユーザによって要求された言語に置き換える必要があるでしょう。

DELib.mqhファイルに、2つの言語のいずれかでメッセージを返すための関数を追加します。

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // mql4に必要
//+------------------------------------------------------------------+
//| ファイルをインクルードする                                                    |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| サービス関数                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 2つの言語のいずれかでテキストを返す                         |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+

この関数はターミナルの言語を確認し、それがCOUNTRY_LANGマクロ置換で指定された言語と一致する場合は、最初の関数パラメータで渡されたテキストが表示されます。それ以外の場合は、2番目の関数パラメータに含まれているテキスト(英語)が表示されます。

ミリ秒で時間を表示する関数も追加しましょう。

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // mql4に必要
//+------------------------------------------------------------------+
//| ファイルをインクルードする                                                     |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| サービス関数                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 2つの言語のいずれかでテキストを返す                           |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| 時間をミリ秒で返す                                     |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

ここでは、すべてが簡潔で、ミリ秒単位で設定された時間が関数に渡されます。秒単位で時間を計算するには、関数に渡される値を1,000で割る必要があります。ミリ秒を計算するには、この除算の残りを取ります。受け取った値はすべて文字列としてフォーマットされ、呼び出し側プログラムに返されます。

銘柄ロットの小数桁数を取得する必要がある場合もあるかもしれません。この関数をサービス関数のファイルに入力しましょう。

//+------------------------------------------------------------------+
//| 銘柄ロットの小数桁数を返す              |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

サービス関数に加えて、操作ログにメッセージを表示するために注文の理由、方向、取引の種類を返す3つのメソッドが必要になります。COrderクラスの保護されたセクションに3つのメソッドを追加します。

   //--- (1)理由、(2)方向、(3)取引の種類を返す
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

以下が実装です。

//+------------------------------------------------------------------+
//| 理由                                                           |
//+------------------------------------------------------------------+
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")   :
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE        : 
         res=
           (
            reason==POSITION_REASON_CLIENT   ?  TextByLanguage("Позиция открыта из десктопного терминала","Position opened from desktop terminal") :
            reason==POSITION_REASON_MOBILE   ?  TextByLanguage("Позиция открыта из мобильного приложения","Position opened from mobile app") :
            reason==POSITION_REASON_WEB      ?  TextByLanguage("Позиция открыта из веб-платформы","Position opened from web platform") :
            reason==POSITION_REASON_EXPERT   ?  TextByLanguage("Позиция открыта из советника или скрипта","Position opened from EA or script") : ""
           );
         break;
      case ORDER_STATUS_MARKET_PENDING       :
      case ORDER_STATUS_HISTORY_PENDING      : 
      case ORDER_STATUS_HISTORY_ORDER        : 
         res=
           (
            reason==ORDER_REASON_CLIENT      ?  TextByLanguage("Ордер выставлен из десктопного терминала","Order set from desktop terminal") :
            reason==ORDER_REASON_MOBILE      ?  TextByLanguage("Ордер выставлен из мобильного приложения","Order set from mobile app") :
            reason==ORDER_REASON_WEB         ?  TextByLanguage("Ордер выставлен из веб-платформы","Oder set from web platform") :
            reason==ORDER_REASON_EXPERT      ?  TextByLanguage("Ордер выставлен советником или скриптом","Order set from EA or script") :
            reason==ORDER_REASON_SL          ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==ORDER_REASON_TP          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==ORDER_REASON_SO          ?  TextByLanguage("Ордер выставлен в результате наступления Stop Out","Due to Stop Out") : ""
           );
         break;
      case ORDER_STATUS_DEAL                 : 
         res=
           (
            reason==DEAL_REASON_CLIENT       ?  TextByLanguage("Сделка проведена из десктопного терминала","Deal carried out from desktop terminal") :
            reason==DEAL_REASON_MOBILE       ?  TextByLanguage("Сделка проведена из мобильного приложения","Deal carried out from mobile app") :
            reason==DEAL_REASON_WEB          ?  TextByLanguage("Сделка проведена из веб-платформы","Deal carried out from web platform") :
            reason==DEAL_REASON_EXPERT       ?  TextByLanguage("Сделка проведена из советника или скрипта","Deal carried out from EA or script") :
            reason==DEAL_REASON_SL           ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==DEAL_REASON_TP           ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==DEAL_REASON_SO           ?  TextByLanguage("Сделка проведена в результате наступления Stop Out","Due to Stop Out") :
            reason==DEAL_REASON_ROLLOVER     ?  TextByLanguage("Сделка проведена по причине переноса позиции","Due to position rollover") :
            reason==DEAL_REASON_VMARGIN      ?  TextByLanguage("Сделка проведена по причине начисления/списания вариационной маржи","Due to variation margin") :
            reason==DEAL_REASON_SPLIT        ?  TextByLanguage("Сделка проведена по причине сплита (понижения цены) инструмента","Due to split") : ""
           );
         break;
      default                                : res="";   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

使用されているのがMQL4MQL5であるかが確認され、入力で渡された注文理由が注文状態に従って検証され、その説明が返されます。

//+------------------------------------------------------------------+
//| 取引方向の説明                                       |
//+------------------------------------------------------------------+
string COrder::GetEntryDescription(const long deal_entry) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_entry==DEAL_ENTRY_IN     ?  TextByLanguage("Вход в рынок","Entry to the market") :
            deal_entry==DEAL_ENTRY_OUT    ?  TextByLanguage("Выход из рынка","Out from the market") :
            deal_entry==DEAL_ENTRY_INOUT  ?  TextByLanguage("Разворот","Reversal") :
            deal_entry==DEAL_ENTRY_OUT_BY ?  TextByLanguage("Закрытие встречной позицией","Closing by opposite position") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

使用されているのがMQL4MQL5であるかが確認され、入力で渡された取引方向が注文状態に従って検証され、その説明が返されます。

//+------------------------------------------------------------------+
//| 取引の種類の名を返す                                            |
//+------------------------------------------------------------------+
string COrder::GetTypeDealDescription(const long deal_type) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_type==DEAL_TYPE_BUY                      ?  TextByLanguage("Сделка на покупку","Buy deal") :
            deal_type==DEAL_TYPE_SELL                     ?  TextByLanguage("Сделка на продажу","Sell deal") :
            deal_type==DEAL_TYPE_BALANCE                  ?  TextByLanguage("Начисление баланса","Balance accrual") :
            deal_type==DEAL_TYPE_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit accrual") :
            deal_type==DEAL_TYPE_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Extra charges") :
            deal_type==DEAL_TYPE_CORRECTION               ?  TextByLanguage("Корректирующая запись","Corrective entry") :
            deal_type==DEAL_TYPE_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonuses") :
            deal_type==DEAL_TYPE_COMMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
            deal_type==DEAL_TYPE_COMMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Commission accrued at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Commission accrued at the end of a month") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Agency commission charged at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Agency commission charged at the end of a month") :
            deal_type==DEAL_TYPE_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Accrued interest on free funds") :
            deal_type==DEAL_TYPE_BUY_CANCELED             ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
            deal_type==DEAL_TYPE_SELL_CANCELED            ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
            deal_type==DEAL_DIVIDEND                      ?  TextByLanguage("Начисление дивиденда","Accrued dividends") :
            deal_type==DEAL_DIVIDEND_FRANKED              ?  TextByLanguage("Начисление франкированного дивиденда","Accrual of franked dividend") :
            deal_type==DEAL_TAX                           ?  TextByLanguage("Начисление налога","Tax accrual") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }

使用されているのがMQL4MQL5であるかが確認され、入力で渡された取引の種類が注文状態に従って検証され、その説明が返されます。

注文のプロパティを記述するメソッドを実装します。

//+------------------------------------------------------------------+
//| 注文の整数プロパティの説明を返す                |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- 一般的なプロパティ
      property==ORDER_PROP_MAGIC             ?  TextByLanguage("Магик","Magic")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  TextByLanguage("Тикет","Ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  TextByLanguage("Тикет родительского ордера","Ticket of parent order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  TextByLanguage("Время открытия","Open time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  TextByLanguage("Время закрытия","Close time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  TextByLanguage("Дата экспирации","Expiration date")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Не задана",": Not set") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE              ?  TextByLanguage("Тип","Type")+": "+this.TypeDescription()                   :
      property==ORDER_PROP_DIRECTION         ?  TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  TextByLanguage("Причина","Reason")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  TextByLanguage("Идентификатор позиции","Position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER        ?  TextByLanguage("Сделка на основании ордера","Deal by order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal direction")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN_MSC     ?  TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  TextByLanguage("Время изменения позиции","Position change time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0")
         )  :
      property==ORDER_PROP_TIME_UPDATE_MSC   ?  TextByLanguage("Время изменения позиции в милисекундах","Position change time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? (string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property)) : "0")
         )  :
   //--- 追加のプロパティ
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  TextByLanguage("Прибыль в пунктах","Profit in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 注文の実数プロパティの説明を返す                      |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- 一般的なプロパティ
      property==ORDER_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Close price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Open price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  TextByLanguage("Цена StopLoss","StopLoss price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  TextByLanguage("Прибыль","Profit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  TextByLanguage("Комиссия","Comission")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  TextByLanguage("Своп","Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  TextByLanguage("Объём","Volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  TextByLanguage("Невыполненный объём","Unfulfilled volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ? 
         TextByLanguage("Цена постановки Limit ордера при активации StopLimit ордера","Price of placing Limit order when StopLimit order activated")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- 追加のプロパティ
      property==ORDER_PROP_PROFIT_FULL       ?  TextByLanguage("Прибыль+комиссия+своп","Profit+Comission+Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 注文の文字列プロパティの説明を返す                |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property)
  {
   return
     (
      property==ORDER_PROP_SYMBOL         ?  TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""            :
      property==ORDER_PROP_COMMENT        ?  TextByLanguage("Комментарий","Comment")+
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") :
      property==ORDER_PROP_EXT_ID         ?  TextByLanguage("Идентификатор на бирже","ID on exchange")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")):
      ""
     );
  }
//+------------------------------------------------------------------+

注文状況の名前、注文またはポジションの名前、取引方向の名前、注文/ポジション方向タイプの説明、操作ログの注文プロパティの便利な表示方法を返すためのメソッドを実装します。

まず、操作ログに関数/メソッド名を簡単に表示するために、Defines.mqhファイルにさらにマクロ置換を追加します。

//+------------------------------------------------------------------+
//| マクロ置換                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // 国の言語
#define DFUN           (__FUNCTION__+": ")   // 「関数の説明」
//+------------------------------------------------------------------+

文字列を完全に記述するのではなく、単にDFUNと記述するだけで、コンパイラによって記述がマクロで設定された文字列に変更されます。

//+------------------------------------------------------------------+
//| 注文状態名を返す                                     |
//+------------------------------------------------------------------+
string COrder::StatusDescription(void) const
  {
   ENUM_ORDER_STATUS status=this.Status();
   ENUM_ORDER_TYPE   type=(ENUM_ORDER_TYPE)this.TypeOrder();
   return
     (
      status==ORDER_STATUS_BALANCE        ?  TextByLanguage("Балансная операция","Balance operation") :
      status==ORDER_STATUS_CREDIT         ?  TextByLanguage("Кредитная операция","Credits operation") :
      status==ORDER_STATUS_HISTORY_ORDER  || status==ORDER_STATUS_HISTORY_PENDING                     ?  
      (
       type>ORDER_TYPE_SELL ? TextByLanguage("Отложенный ордер","Pending order")                      :
       TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell"))
      )                                                                                               :
      status==ORDER_STATUS_DEAL           ?  TextByLanguage("Сделка","Deal")                          :
      status==ORDER_STATUS_MARKET_ACTIVE  ?  TextByLanguage("Позиция","Active position")              :
      status==ORDER_STATUS_MARKET_PENDING ?  TextByLanguage("Установленный отложенный ордер","Active pending order") :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 注文またはポジションの名前を返す                                    |
//+------------------------------------------------------------------+
string COrder::TypeDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.GetTypeDealDescription(this.TypeOrder());
   else switch(this.TypeOrder())
     {
      case ORDER_TYPE_BUY              :  return "Buy";
      case ORDER_TYPE_BUY_LIMIT        :  return "Buy Limit";
      case ORDER_TYPE_BUY_STOP         :  return "Buy Stop";
      case ORDER_TYPE_SELL             :  return "Sell";
      case ORDER_TYPE_SELL_LIMIT       :  return "Sell Limit";
      case ORDER_TYPE_SELL_STOP        :  return "Sell Stop";
      #ifdef __MQL4__
      case ORDER_TYPE_BALANCE          :  return TextByLanguage("Балансовая операция","Balance operation");
      case ORDER_TYPE_CREDIT           :  return TextByLanguage("Кредитная операция","Credit operation");
      #else 
      case ORDER_TYPE_BUY_STOP_LIMIT   :  return "Buy Stop Limit";
      case ORDER_TYPE_SELL_STOP_LIMIT  :  return "Sell Stop Limit";
      #endif 
      default                          :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| 取引方向名を返す                                       |
//+------------------------------------------------------------------+
string COrder::DealEntryDescription(void) const
  {
   return(this.Status()==ORDER_STATUS_DEAL ? this.GetEntryDescription(this.GetProperty(ORDER_PROP_DEAL_ENTRY)) : "");
  }
//+------------------------------------------------------------------+
//| 注文またはポジションの方向の種類を返す                            |
//+------------------------------------------------------------------+
string COrder::DirectionDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.TypeDescription();
   switch(this.TypeByDirection())
     {
      case ORDER_TYPE_BUY  :  return "Buy";
      case ORDER_TYPE_SELL :  return "Sell";
      default              :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| 操作ログに注文プロパティを送信する                             |
//+------------------------------------------------------------------+
void COrder::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров: \"","Beginning of the parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=ORDER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

COrder抽象注文クラスのすべてのメソッドが実装されたので、この段階で本当に何があるかを見ることができます。

COrder抽象注文テスト

COrderベースオブジェクトがどのように作成され、データで埋められるかをテストしてみましょう。さらに、それに基づいてオブジェクトを種類別に開発し、それらをコレクションに格納します。

Expertsディレクトリまたはサブディレクトリに新しいフォルダを作成し、TestDoEasyと名付けます。次に、新しく作成したフォルダの中に別のフォルダPart01を作成します。テストEAのファイルはここに配置します。

エディタナビゲータでPart01を右クリックして、ポップアップメニューから[New File(新規ファイル)]を選択するか、Part01フォルダを強調表示した状態でCtrl+Nを押します。[Expert Advisor (template)]オプションがすでに選択された状態でMQLウィザードウィンドウが開きます。[次へ]をクリックして新しいTestDoEasyPart01ファイルの名前を名前入力フィールドにすでに指定されたファイルパスに追加します。ウィザード操作が終了するまで[次へ]をクリックします。テストのために追加のイベントハンドラは必要ないので、すべてのチェックボックスを空白のままでも大丈夫です。[完了]をクリックして新しいEAテンプレートを作成します。
//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        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"
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| エキスパート初期化解除関数                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| エキスパートティック関数                                  |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

テストは非常に簡単なので、作り直す必要はありません。そのため、標準ライブラリからCOrder抽象注文のクラスとCArrayObjを含み、 オブジェクトリストを作成します

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        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\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- グローバル変数
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| エキスパート初期化関数                                  |
//+------------------------------------------------------------------+

次に、OnInit()でソート済みリストフラグをオブジェクトリストに設定し、2つのループですべての履歴取引注文を書き入れます。

次に、ループ内で、いっぱいになったリストの各オブジェクトのデータをターミナルのエキスパート操作ログで表示します。

//+------------------------------------------------------------------+
//| エキスパート初期化関数                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get history of deals and orders"));
      return INIT_FAILED;
     }
//--- 取引
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- 注文
   int total_orders=HistoryOrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- 操作ログで表示する
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

EAをコンパイルしようとすると、3つのエラーが発生します。


エラーは、クラスの保護されたメソッドには外部からアクセスできないことを示しています。結局のところ、COrderコンストラクタはクラスの保護されたセクションに作成しました。型ごとのオブジェクトを開発するための基礎としてクラスを使う場合、オープンコンストラクタがあることになるのですべてがうまくいくでしょう。それでは、テストを実行するために、コンストラクタをCOrderクラスのpublicセクションに移動しましょう。


これで、すべてがエラーなしでコンパイルされ、取引口座の履歴内のすべての注文と取引に関するデータがターミナル操作ログに表示されます。


サポートされていないものも含め、各注文/取引のすべてのプロパティが表示されます。


この注文で特定のプロパティをサポートするためのフラグを返すメソッドは仮想(virtual)として開発されているため、派生クラスで再定義されています。これらの派生クラスは、操作ログ内のデータを表示するために使用されます。その場合、すべてが正しく表示されるはずです。注文でサポートされていないプロパティがある場合、それは操作ログに表示されません。COrderクラスのPrint(const bool full_prop=false)メソッドが操作ログ内でのサポートされていないプロパティの表示を無効にするためのデフォルトのフラグを持ちますが、クラスのSupportProperty()仮想メソッドはすべてのプロパティに対して単に「true」を返します。

次の段階

最初の(そして最も小さい)部分の準備が整いました。履歴注文と取引ならび市場注文とポジションを収集するための基本オブジェクトを開発しました。これまでのところ実用的な価値はありませんが、これは始まりにすぎません。このただ1つの基本オブジェクトは、注文システムでデータを保存および表示するシステムの要となります。次のステップは、同じ原則を使用して他の必要なオブジェクトとコレクションを開発することです。また、絶えず必要とされるデータの収集を自動化するつもりです。


次の部分では、この抽象注文に基づいていくつかのクラスを開発します。これらは特定の注文、取引、ポジションの種類を記述するものです。最初のコレクション(履歴注文と取引のコレクション)、そしてライブラリの基本と主な目的であるエンジンも開発されます。ライブラリ開発記述の以降の部分では、MQL4でライブラリをコンパイルを試みてエラーを取り除きます。

以下は、最初の部分で説明したすべてのファイルを含むアーカイブです。ダウンロードして詳細に分析してください。質問、コメント、提案をコメント欄に残してください。

ライブラリは記事を書くのと並行して開発されています。したがって、記事ごとにライブラリのメソッドと変数に必要な修正が行われます。したがって、添付ファイルの内容と記事の説明文に多少の矛盾があるかもしれません。例えば、コメントが違ったり、列挙体のメンバの順番が違うかもしれませんが、添付の例のパフォーマンスには影響がありません。

目次に戻る