MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第12部): 「口座」オブジェクトクラスと口座オブジェクトのコレクション

Artyom Trishkin | 23 9月, 2019

内容

ライブラリの取引クラスを開発する前に、追加の取引関連クラスを作成する必要があります。具体的には、取引口座と取引銘柄のデータが必要です。本稿では、口座オブジェクトに専念します。

口座データは取引中に変更される可能性があるため、口座オブジェクトを準備してから口座オブジェクトの収集を行います。次に、口座追跡イベントを実装して、レバレッジ、残高、利益/損失、エクイティ、口座の制限の変化を検出できるようにします。


口座オブジェクト

取引口座のボリュームは、以前に作成され過去の記事で説明したオブジェクトと同じです。このオブジェクトと以前に検討されたオブジェクトとの唯一の違いは、これは、後続オブジェクトが特定のオブジェクトステータスを指定する抽象オブジェクトではないということです。口座オブジェクトは、すべての口座プロパティを備えた独立したオブジェクトです。このようなオブジェクトは口座オブジェクトコレクションに追加されるため、さまざまなパラメータで異なる口座のデータを比較できるようになります。

通常どおり、クラスの操作に必要な口座オブジェクトプロパティのすべての列挙の開発から始めます。
エディターのヘルプで口座プロパティを開きます。

AccountInfoInteger()関数では

ENUM_ACCOUNT_INFO_INTEGER

ID

説明

Property type

ACCOUNT_LOGIN

Account number

long

ACCOUNT_TRADE_MODE

Trading account type

ENUM_ACCOUNT_TRADE_MODE

ACCOUNT_LEVERAGE

Leverage

long

ACCOUNT_LIMIT_ORDERS

Maximum allowed number of active pending orders

int

ACCOUNT_MARGIN_SO_MODE

Mode of setting the minimum available margin level

ENUM_ACCOUNT_STOPOUT_MODE

ACCOUNT_TRADE_ALLOWED

Trading permission of the current account

bool

ACCOUNT_TRADE_EXPERT

Trading permission of an EA

bool

ACCOUNT_MARGIN_MODE

Margin calculation mode

ENUM_ACCOUNT_MARGIN_MODE

ACCOUNT_CURRENCY_DIGITS

Number of digits for an account currency necessary for accurate display of trading results

int


AccountInfoDouble()関数では

ENUM_ACCOUNT_INFO_DOUBLE

ID

説明

Property type

ACCOUNT_BALANCE

Account balance in a deposit currency

double

ACCOUNT_CREDIT

Credit in a deposit currency

double

ACCOUNT_PROFIT

Current profit on an account in the account currency

double

ACCOUNT_EQUITY

Equity on an account in the deposit currency

double

ACCOUNT_MARGIN

Reserved margin on an account in the deposit currency

double

ACCOUNT_MARGIN_FREE

Free funds available for opening a position on an account in the deposit currency

double

ACCOUNT_MARGIN_LEVEL

Margin level on an account in %

double

ACCOUNT_MARGIN_SO_CALL

Margin level, at which a deposit to an account is required (Margin Call). Depending on ACCOUNT_MARGIN_SO_MODE, the property is set either in % or deposit currency

double

ACCOUNT_MARGIN_SO_SO

Margin level, at which the most loss-making position is closed (Stop Out). Depending on ACCOUNT_MARGIN_SO_MODE, the property is set either in % or deposit currency

double

ACCOUNT_MARGIN_INITIAL

Funds reserved on an account to ensure a guarantee amount for all pending orders

double

ACCOUNT_MARGIN_MAINTENANCE

Funds reserved on an account to ensure a minimum amount for all open positions

double

ACCOUNT_ASSETS

Current assets on an account

double

ACCOUNT_LIABILITIES

Current liabilities on an account

double

ACCOUNT_COMMISSION_BLOCKED

Current sum of blocked commissions on an account

double


AccountInfoString()関数では

ENUM_ACCOUNT_INFO_STRING

ID

説明

Property type

ACCOUNT_NAME

Client name

string

ACCOUNT_SERVER

Trade server name

string

ACCOUNT_CURRENCY

Deposit currency

string

ACCOUNT_COMPANY

Name of a company serving an account

string


口座オブジェクトはこれらすべてのプロパティを備えており、それらはクラスコンストラクタで設定されます。

ライブラリのDefines.mqhファイルで、上記に表示された口座プロパティの表に対応する口座オブジェクトのintegerrealstringプロパティを追加します。
口座イベントを使用するための列挙は既に作成されているため、以前に作成した口座イベントを使用するためのデータの前に口座プロパティデータを配置するのが妥当です。

//+------------------------------------------------------------------+
//| Data for working with accounts                                   |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Account integer properties                                       |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_INTEGER
  {
   ACCOUNT_PROP_LOGIN,                                      // Account number
   ACCOUNT_PROP_TRADE_MODE,                                 // Trading account type
   ACCOUNT_PROP_LEVERAGE,                                   // Provided leverage
   ACCOUNT_PROP_LIMIT_ORDERS,                               // Maximum allowed number of active pending orders
   ACCOUNT_PROP_MARGIN_SO_MODE,                             // Mode of setting the minimum available margin level
   ACCOUNT_PROP_TRADE_ALLOWED,                              // Permission to trade for the current account from the server side
   ACCOUNT_PROP_TRADE_EXPERT,                               // Permission to trade for an EA from the server side
   ACCOUNT_PROP_MARGIN_MODE,                                // Margin calculation mode
   ACCOUNT_PROP_CURRENCY_DIGITS                             // Number of digits for an account currency necessary for accurate display of trading results
  };
#define ACCOUNT_PROP_INTEGER_TOTAL    (9)                   // Total number of account's integer properties
#define ACCOUNT_PROP_INTEGER_SKIP     (0)                   // Number of account's integer properties not used in sorting
//+------------------------------------------------------------------+
//| Account real properties                                          |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_DOUBLE
  {
   ACCOUNT_PROP_BALANCE = ACCOUNT_PROP_INTEGER_TOTAL,       // Account balance in a deposit currency
   ACCOUNT_PROP_CREDIT,                                     // Credit in a deposit currency
   ACCOUNT_PROP_PROFIT,                                     // Current profit on an account in the account currency
   ACCOUNT_PROP_EQUITY,                                     // Equity on an account in the deposit currency
   ACCOUNT_PROP_MARGIN,                                     // Reserved margin on an account in a deposit currency
   ACCOUNT_PROP_MARGIN_FREE,                                // Free funds available for opening a position in a deposit currency
   ACCOUNT_PROP_MARGIN_LEVEL,                               // Margin level on an account in %
   ACCOUNT_PROP_MARGIN_SO_CALL,                             // Margin level, at which a deposit to an account is required (Margin Call)
   ACCOUNT_PROP_MARGIN_SO_SO,                               // Margin level, at which the most loss-making position is closed (Stop Out)
   ACCOUNT_PROP_MARGIN_INITIAL,                             // Funds reserved on an account to ensure a guarantee amount for all pending orders 
   ACCOUNT_PROP_MARGIN_MAINTENANCE,                         // Funds reserved on an account to ensure a minimum amount for all open positions
   ACCOUNT_PROP_ASSETS,                                     // Current assets on an account
   ACCOUNT_PROP_LIABILITIES,                                // Current liabilities on an account
   ACCOUNT_PROP_COMMISSION_BLOCKED                          // Current sum of blocked commissions on an account
  };
#define ACCOUNT_PROP_DOUBLE_TOTAL     (14)                  // Total number of account's real properties
#define ACCOUNT_PROP_DOUBLE_SKIP      (0)                   // Number of account's real properties not used in sorting
//+------------------------------------------------------------------+
//| Account string properties                                        |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_STRING
  {
   ACCOUNT_PROP_NAME = (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL), // Client name
   ACCOUNT_PROP_SERVER,                                     // Trade server name
   ACCOUNT_PROP_CURRENCY,                                   // Deposit currency
   ACCOUNT_PROP_COMPANY                                     // Name of a company serving an account
  };
#define ACCOUNT_PROP_STRING_TOTAL     (4)                   // Total number of account's string properties
#define ACCOUNT_PROP_STRING_SKIP      (0)                   // Number of account string properties not used in sorting
//+------------------------------------------------------------------+
//| Possible account sorting criteria                                |
//+------------------------------------------------------------------+
#define FIRST_ACC_DBL_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP)
#define FIRST_ACC_STR_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP+ACCOUNT_PROP_DOUBLE_TOTAL-ACCOUNT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_ACCOUNT_MODE
  {
   SORT_BY_ACCOUNT_LOGIN               =  0,                      // Sort by account number
   SORT_BY_ACCOUNT_TRADE_MODE          =  1,                      // Sort by trading account type
   SORT_BY_ACCOUNT_LEVERAGE            =  2,                      // Sort by leverage
   SORT_BY_ACCOUNT_LIMIT_ORDERS        =  3,                      // Sort by maximum acceptable number of existing pending orders
   SORT_BY_ACCOUNT_MARGIN_SO_MODE      =  4,                      // Sort by mode for setting the minimum acceptable margin level
   SORT_BY_ACCOUNT_TRADE_ALLOWED       =  5,                      // Sort by permission to trade for the current account
   SORT_BY_ACCOUNT_TRADE_EXPERT        =  6,                      // Sort by permission to trade for an EA
   SORT_BY_ACCOUNT_MARGIN_MODE         =  7,                      // Sort by margin calculation mode
   SORT_BY_ACCOUNT_CURRENCY_DIGITS     =  8,                      // Sort by number of digits for an account currency
    
   SORT_BY_ACCOUNT_BALANCE             =  FIRST_ACC_DBL_PROP,     // Sort by an account balance in the deposit currency
   SORT_BY_ACCOUNT_CREDIT              =  FIRST_ACC_DBL_PROP+1,   // Sort by credit in a deposit currency
   SORT_BY_ACCOUNT_PROFIT              =  FIRST_ACC_DBL_PROP+2,   // Sort by the current profit on an account in the deposit currency
   SORT_BY_ACCOUNT_EQUITY              =  FIRST_ACC_DBL_PROP+3,   // Sort by an account equity in the deposit currency
   SORT_BY_ACCOUNT_MARGIN              =  FIRST_ACC_DBL_PROP+4,   // Served by an account reserved margin in the deposit currency
   SORT_BY_ACCOUNT_MARGIN_FREE         =  FIRST_ACC_DBL_PROP+5,   // Sort by account free funds available for opening a position in the deposit currency
   SORT_BY_ACCOUNT_MARGIN_LEVEL        =  FIRST_ACC_DBL_PROP+6,   // Sort by account margin level in %
   SORT_BY_ACCOUNT_MARGIN_SO_CALL      =  FIRST_ACC_DBL_PROP+7,   // Sort by margin level requiring depositing funds to an account (Margin Call)
   SORT_BY_ACCOUNT_MARGIN_SO_SO        =  FIRST_ACC_DBL_PROP+8,   // Sort by margin level, at which the most loss-making position is closed (Stop Out)
   SORT_BY_ACCOUNT_MARGIN_INITIAL      =  FIRST_ACC_DBL_PROP+9,   // Sort by funds reserved on an account to ensure a guarantee amount for all pending orders 
   SORT_BY_ACCOUNT_MARGIN_MAINTENANCE  =  FIRST_ACC_DBL_PROP+10,  // Sort by funds reserved on an account to ensure a minimum amount for all open positions
   SORT_BY_ACCOUNT_ASSETS              =  FIRST_ACC_DBL_PROP+11,  // Sort by the amount of the current assets on an account
   SORT_BY_ACCOUNT_LIABILITIES         =  FIRST_ACC_DBL_PROP+12,  // Sort by the current liabilities on an account
   SORT_BY_ACCOUNT_COMMISSION_BLOCKED  =  FIRST_ACC_DBL_PROP+13,  // Sort by the current amount of blocked commissions on an account

   SORT_BY_ACCOUNT_NAME                =  FIRST_ACC_STR_PROP,     // Sort by a client name
   SORT_BY_ACCOUNT_SERVER              =  FIRST_ACC_STR_PROP+1,   // Sort by a trade server name
   SORT_BY_ACCOUNT_CURRENCY            =  FIRST_ACC_STR_PROP+2,   // Sort by a deposit currency
   SORT_BY_ACCOUNT_COMPANY             =  FIRST_ACC_STR_PROP+3    // Sort by a name of a company serving an account
  };
//+------------------------------------------------------------------+
//| Data for working with account events                             |
//+------------------------------------------------------------------+

ここにあるものはすべて連載の記事からおなじみなので、次のタイプのオブジェクトプロパティのために初期列挙定数のアドレスを正確に計算するために渡されるプロパティ数を指定するための列挙の配置、未使用のオブジェクトプロパティの設定、マクロ置換の設定などについて説明することには意味がありません。これらはすべて、これまでの記事、つまり、第6部の「ネッティングアカウントでのイベント処理の実装」セクションで説明されています。

ここでは、「証拠金計算モード」についてのみ説明します。
MQL4にはENUM_ACCOUNT_MARGIN_MODE列挙がないため、MQL4でコンパイルできるようにToMQL4.mqhファイルの終わりに追加しましょう。

//+------------------------------------------------------------------+
//| Margin calculation mode                                          |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_MARGIN_MODE
  {
   ACCOUNT_MARGIN_MODE_RETAIL_NETTING,
   ACCOUNT_MARGIN_MODE_EXCHANGE,
   ACCOUNT_MARGIN_MODE_RETAIL_HEDGING
  };
//+------------------------------------------------------------------+

すべてのデータが準備されたので、口座オブジェクトを作成できます。

\MQL5\Include\DoEasy\Objects\ライブラリフォルダにAccountsサブフォルダを作成して、Account.mqhファイルに新しいCAccountクラスを導入します。

新しく作成されたクラスファイルに、必要なすべてのメソッドの宣言を追加します
これらのメソッドのほとんどは、ライブラリオブジェクトに対して既に「標準的」なものですが、ちょっとした注意点があります。このクラスでは子孫の存在が示唆されていないため、オブジェクトのステータスを受け入れて設定するprotectedクラスコンストラクタがないということです。したがって、口座オブジェクトには「ステータス」プロパティがなく、そのコンストラクタは引数を受け入れません。同時に、将来のクラスの子孫のために、特定のプロパティをサポートするオブジェクトのフラグを返す仮想メソッドを残しておきます。

//+------------------------------------------------------------------+
//|                                                      Account.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 "..\..\Services\DELib.mqh"で置き換えます。
//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CObject
  {
private:
   long              m_long_prop[ACCOUNT_PROP_INTEGER_TOTAL];           // Integer properties
   double            m_double_prop[ACCOUNT_PROP_DOUBLE_TOTAL];          // Real properties
   string            m_string_prop[ACCOUNT_PROP_STRING_TOTAL];          // String properties

//--- Return the index of the array the account's (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_ACCOUNT_PROP_DOUBLE property) const { return(int)property-ACCOUNT_PROP_INTEGER_TOTAL;                          }
   int               IndexProp(ENUM_ACCOUNT_PROP_STRING property) const { return(int)property-ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_DOUBLE_TOTAL;}
public:
//--- コンストラクタ                   
                     CAccount(void);
protected:
   
public:
//--- Set (1) integer, (2) real and (3) string properties of an account
   void              SetProperty(ENUM_ACCOUNT_PROP_INTEGER property,long value)        { this.m_long_prop[property]=value;                                  }
   void              SetProperty(ENUM_ACCOUNT_PROP_DOUBLE property,double value)       { this.m_double_prop[this.IndexProp(property)]=value;                }
   void              SetProperty(ENUM_ACCOUNT_PROP_STRING property,string value)       { this.m_string_prop[this.IndexProp(property)]=value;                }
//--- Return (1) integer, (2) real and (3) string properties of an account from the property array
   long              GetProperty(ENUM_ACCOUNT_PROP_INTEGER property)             const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_ACCOUNT_PROP_DOUBLE property)              const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_ACCOUNT_PROP_STRING property)              const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of calculating MarginCall and StopOut levels in %
   bool              IsPercentsForSOLevels(void)                                 const { return this.MarginSOMode()==ACCOUNT_STOPOUT_MODE_PERCENT;          }
//--- Return the flag of supporting the property by the account object
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_INTEGER property)               { return true; }
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_DOUBLE property)                { return true; }
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_STRING property)                { return true; }

//--- Compare CAccount objects by all possible properties (for sorting the lists by a specified account object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CAccount objects by account properties (to search for equal account objects)
   bool              IsEqual(CAccount* compared_account) const;
//+------------------------------------------------------------------+
//| Methods of a simplified access to the account object properties  |
//+------------------------------------------------------------------+
//--- Return the account's integer properties
   ENUM_ACCOUNT_TRADE_MODE    TradeMode(void)                     const { return (ENUM_ACCOUNT_TRADE_MODE)this.GetProperty(ACCOUNT_PROP_TRADE_MODE);        }
   ENUM_ACCOUNT_STOPOUT_MODE  MarginSOMode(void)                  const { return (ENUM_ACCOUNT_STOPOUT_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_SO_MODE);  }
   ENUM_ACCOUNT_MARGIN_MODE   MarginMode(void)                    const { return (ENUM_ACCOUNT_MARGIN_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_MODE);      }
   long              Login(void)                                  const { return this.GetProperty(ACCOUNT_PROP_LOGIN);                                      }
   long              Leverage(void)                               const { return this.GetProperty(ACCOUNT_PROP_LEVERAGE);                                   }
   long              LimitOrders(void)                            const { return this.GetProperty(ACCOUNT_PROP_LIMIT_ORDERS);                               }
   long              TradeAllowed(void)                           const { return this.GetProperty(ACCOUNT_PROP_TRADE_ALLOWED);                              }
   long              TradeExpert(void)                            const { return this.GetProperty(ACCOUNT_PROP_TRADE_EXPERT);                               }
   long              CurrencyDigits(void)                         const { return this.GetProperty(ACCOUNT_PROP_CURRENCY_DIGITS);                            }
   
//--- Return the account's real properties
   double            Balance(void)                                const { return this.GetProperty(ACCOUNT_PROP_BALANCE);                                    }
   double            Credit(void)                                 const { return this.GetProperty(ACCOUNT_PROP_CREDIT);                                     }
   double            Profit(void)                                 const { return this.GetProperty(ACCOUNT_PROP_PROFIT);                                     }
   double            Equity(void)                                 const { return this.GetProperty(ACCOUNT_PROP_EQUITY);                                     }
   double            Margin(void)                                 const { return this.GetProperty(ACCOUNT_PROP_MARGIN);                                     }
   double            MarginFree(void)                             const { return this.GetProperty(ACCOUNT_PROP_MARGIN_FREE);                                }
   double            MarginLevel(void)                            const { return this.GetProperty(ACCOUNT_PROP_MARGIN_LEVEL);                               }
   double            MarginSOCall(void)                           const { return this.GetProperty(ACCOUNT_PROP_MARGIN_SO_CALL);                             }
   double            MarginSOSO(void)                             const { return this.GetProperty(ACCOUNT_PROP_MARGIN_SO_SO);                               }
   double            MarginInitial(void)                          const { return this.GetProperty(ACCOUNT_PROP_MARGIN_INITIAL);                             }
   double            MarginMaintenance(void)                      const { return this.GetProperty(ACCOUNT_PROP_MARGIN_MAINTENANCE);                         }
   double            Assets(void)                                 const { return this.GetProperty(ACCOUNT_PROP_ASSETS);                                     }
   double            Liabilities(void)                            const { return this.GetProperty(ACCOUNT_PROP_LIABILITIES);                                }
   double            ComissionBlocked(void)                       const { return this.GetProperty(ACCOUNT_PROP_COMMISSION_BLOCKED);                         }
   
//--- Return the account's string properties
   string            Name(void)                                   const { return this.GetProperty(ACCOUNT_PROP_NAME);                                       }
   string            Server(void)                                 const { return this.GetProperty(ACCOUNT_PROP_SERVER);                                     }
   string            Currency(void)                               const { return this.GetProperty(ACCOUNT_PROP_CURRENCY);                                   }
   string            Company(void)                                const { return this.GetProperty(ACCOUNT_PROP_COMPANY);                                    }

//+------------------------------------------------------------------+
//| Descriptions of the account object properties                    |
//+------------------------------------------------------------------+
//--- Return the description of the account's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_STRING property);
//--- Return a name of a trading account type
   string            TradeModeDescription(void)    const;
//--- Return the description of the mode for setting the minimum available margin level
   string            MarginSOModeDescription(void) const;
//--- Return the description of the margin calculation mode
   string            MarginModeDescription(void)   const;
//--- Display the description of the account properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display a short account description in the journal
   void              PrintShort(void);
//---
  };
//+------------------------------------------------------------------+

コンストラクタをクラス本体の外側で実装します。

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
//--- 整数型プロパティを保存する
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = ::AccountInfoInteger(ACCOUNT_LOGIN);
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = ::AccountInfoInteger(ACCOUNT_TRADE_MODE);
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = ::AccountInfoInteger(ACCOUNT_LEVERAGE);
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ;
   
//--- 実数型プロパティを保存する
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- 文字列プロパティを保存する
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);
   
  }
//+-------------------------------------------------------------------+

ここではすべてが明確です。適切な口座プロパティは、AccountInfo関数を使用して各オブジェクトプロパティに割り当てられています。
MQL4に存在しない2つのプロパティについては、条件付きコンパイルのディレクティブが使用されます。MQL5では「margin calculation mode」プロパティと「number of decimal places for an account currency」プロパティは選択される一方、MQL4では前者には単に ACCOUNT_MARGIN_MODE_RETAIL_HEDGING(ヘッジ勘定)をENUM_ACCOUNT_MARGIN_MODE列挙から返し、後者には小数点以下2桁を返します。

コレクションリスト内の口座オブジェクトを検索および並び替えするためのメソッドを実装しましょう
このメソッドはライブラリオブジェクトで以前に説明された物と同じなので、ここではコードだけを提示します。

//+-------------------------------------------------------------------+
//|Compare CAccount objects by all possible properties                |
//+-------------------------------------------------------------------+
int CAccount::Compare(const CObject *node,const int mode=0) const
  {
   const CAccount *account_compared=node;
//--- compare integer properties of two accounts
   if(mode<ACCOUNT_PROP_INTEGER_TOTAL)
     {
      long value_compared=account_compared.GetProperty((ENUM_ACCOUNT_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ACCOUNT_PROP_INTEGER)mode);
      return(value_current>value_compared ?1 : value_current<value_compared ?-1 : 0);
     }
//--- comparing real properties of two accounts
   else if(mode<ACCOUNT_PROP_DOUBLE_TOTAL+ACCOUNT_PROP_INTEGER_TOTAL)
     {
      double value_compared=account_compared.GetProperty((ENUM_ACCOUNT_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ACCOUNT_PROP_DOUBLE)mode);
      return(value_current>value_compared ?1 : value_current<value_compared ?-1 : 0);
     }
//--- comparing string properties of two accounts
   else if(mode<ACCOUNT_PROP_DOUBLE_TOTAL+ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_STRING_TOTAL)
     {
      string value_compared=account_compared.GetProperty((ENUM_ACCOUNT_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ACCOUNT_PROP_STRING)mode);
      return(value_current>value_compared ?1 : value_current<value_compared ?-1 : 0);

     }
   return 0;
  }
//+------------------------------------------------------------------+

2つの口座オブジェクトを比較するには、不変のプロパティを比較して、これらのオブジェクトが異なる口座に属しているかどうかを判断する必要があります。口座番号(ログイン)、ユーザー名、会社名は正確な識別のために提供されるもので、これらは、口座オブジェクト比較メソッドで比較される2つの口座に対して確認されるプロパティです。

//+------------------------------------------------------------------+
//| Compare CAccount objects by account properties                   |
//+------------------------------------------------------------------+
bool CAccount::IsEqual(CAccount *compared_account) const
  {
   if(this.GetProperty(ACCOUNT_PROP_COMPANY)!=compared_account.GetProperty(ACCOUNT_PROP_COMPANY) ||
      this.GetProperty(ACCOUNT_PROP_LOGIN)!=compared_account.GetProperty(ACCOUNT_PROP_LOGIN)     ||
      this.GetProperty(ACCOUNT_PROP_NAME)!=compared_account.GetProperty(ACCOUNT_PROP_NAME)
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

比較されているオブジェクトへのポインタがメソッドに渡されて、両方のオブジェクトの3つのプロパティ( 会社名口座番号クライアント名)が確認されます。プロパティが1つでも等しくない場合、オブジェクトは異なる口座に属するため、falseを返します。プロパティが3つとも等しい場合、オブジェクトは等しいので、trueを返します

他のクラスメソッドは「サービス」メソッドで、必要な情報はすべてコードに含まれているのでここでは説明しません。連載ではすでに同様のメソッドが分析されています。

//+------------------------------------------------------------------+
//| Display account properties in the journal                        |
//+------------------------------------------------------------------+
void CAccount::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров аккаунта","Beginning of Account parameter list")," ==================");
   int beg=0, end=ACCOUNT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ACCOUNT_PROP_INTEGER prop=(ENUM_ACCOUNT_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ACCOUNT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ACCOUNT_PROP_DOUBLE prop=(ENUM_ACCOUNT_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ACCOUNT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ACCOUNT_PROP_STRING prop=(ENUM_ACCOUNT_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров аккаунта","End of Account parameter list")," ==================\n");
  }
//+------------------------------------------------------------------+
//| Display the brief account description in the journal             |
//+------------------------------------------------------------------+
void CAccount::PrintShort(void)
  {
   string mode=(this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? ", Hedge" : this.MarginMode()==ACCOUNT_MARGIN_MODE_EXCHANGE ? ", Exhange" : "");
   string names=TextByLanguage("Счёт ","Account ")+(string)this.Login()+": "+this.Name()+" ("+this.Company()+" ";
   string values=DoubleToString(this.Balance(),(int)this.CurrencyDigits())+" "+this.Currency()+", 1:"+(string)+this.Leverage()+mode+", "+this.TradeModeDescription()+")";
   ::Print(names,values);
  }
//+------------------------------------------------------------------+
//| Display a description of an account integer property             |
//+------------------------------------------------------------------+
string CAccount::GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property)
  {
   return
     (
      property==ACCOUNT_PROP_LOGIN           ?  TextByLanguage("Номер счёта","Account number")+": "+(string)this.GetProperty(property)                                                    :
      property==ACCOUNT_PROP_TRADE_MODE      ?  TextByLanguage("Тип торгового счета","Account trade mode")+": "+this.TradeModeDescription()                                               :
      property==ACCOUNT_PROP_LEVERAGE        ?  TextByLanguage("Размер предоставленного плеча","Account leverage")+": "+(string)this.GetProperty(property) :
      property==ACCOUNT_PROP_LIMIT_ORDERS    ?  TextByLanguage("Максимально допустимое количество действующих отложенных ордеров","Maximum allowed number of active pending orders")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      property==ACCOUNT_PROP_MARGIN_SO_MODE  ?  TextByLanguage("Режим задания минимально допустимого уровня залоговых средств","Mode for setting the minimal allowed margin")+": "+
                                                   this.MarginSOModeDescription()                                                          :
      property==ACCOUNT_PROP_TRADE_ALLOWED   ?  TextByLanguage("Разрешенность торговли для текущего счета","Allowed trade for the current account")+": "+
                                                   (this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))                                                 :
      property==ACCOUNT_PROP_TRADE_EXPERT    ?  TextByLanguage("Разрешенность торговли для эксперта","Allowed trade for an Expert Advisor")+": "+
                                                   (this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))                                                 :
      property==ACCOUNT_PROP_MARGIN_MODE     ?  TextByLanguage("Режим расчета маржи","Margin calculation mode")+": "+
                                                   this.MarginModeDescription()                                                         :
      property==ACCOUNT_PROP_CURRENCY_DIGITS ?  TextByLanguage("Количество знаков после запятой для валюты счета","Number of decimal places in account currency")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return a description of an account real property                 |
//+------------------------------------------------------------------+
string CAccount::GetPropertyDescription(ENUM_ACCOUNT_PROP_DOUBLE property)
  {
   return
     (
      property==ACCOUNT_PROP_BALANCE         ?  TextByLanguage("Баланс счета","Account balance")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())+" "+this.Currency()                                            :
      property==ACCOUNT_PROP_CREDIT          ?  TextByLanguage("Предоставленный кредит","Account credit")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())+" "+this.Currency()                                            :
      property==ACCOUNT_PROP_PROFIT          ?  TextByLanguage("Текущая прибыль на счете","Current profit of an account")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())+" "+this.Currency()                                            :
      property==ACCOUNT_PROP_EQUITY          ?  TextByLanguage("Собственные средства на счете","Account equity")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())+" "+this.Currency()                                            :
      property==ACCOUNT_PROP_MARGIN          ?  
         TextByLanguage("Зарезервированные залоговые средства на счете","Account margin used in deposit currency")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())+" "+this.Currency()                                            :
      property==ACCOUNT_PROP_MARGIN_FREE     ?  
         TextByLanguage("Свободные средства на счете, доступные для открытия позиции","Account free margin")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      property==ACCOUNT_PROP_MARGIN_LEVEL    ?  TextByLanguage("Уровень залоговых средств на счете в процентах","Account margin level in percentage")+": "+
                                                   ::DoubleToString(this.GetProperty(property),1)+"%"                                                                                     :
      property==ACCOUNT_PROP_MARGIN_SO_CALL  ?  TextByLanguage("Уровень залоговых средств для наступления Margin Call","Margin call level")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(this.IsPercentsForSOLevels() ? 1 : (int)this.CurrencyDigits()))+
                                                   (this.IsPercentsForSOLevels() ? "%" : this.Currency())                                                                                 :
      property==ACCOUNT_PROP_MARGIN_SO_SO    ?  TextByLanguage("Уровень залоговых средств для наступления Stop Out","Margin stop out level")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(this.IsPercentsForSOLevels() ? 1 : (int)this.CurrencyDigits()))+
                                                   (this.IsPercentsForSOLevels() ? "%" : this.Currency())                                                                                 :
      property==ACCOUNT_PROP_MARGIN_INITIAL  ? 
         TextByLanguage("Зарезервированные средства для обеспечения гарантийной суммы по всем отложенным ордерам","Amount reserved on account to cover margin of all pending orders ")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      property==ACCOUNT_PROP_MARGIN_MAINTENANCE  ? 
         TextByLanguage("Зарезервированные средства для обеспечения минимальной суммы по всем открытым позициям","Min equity reserved on account to cover min amount of all open positions")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      property==ACCOUNT_PROP_ASSETS          ?  TextByLanguage("Текущий размер активов на счёте","Current account assets")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      property==ACCOUNT_PROP_LIABILITIES     ?  TextByLanguage("Текущий размер обязательств на счёте","Current liabilities on account")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      property==ACCOUNT_PROP_COMMISSION_BLOCKED ?  
         TextByLanguage("Сумма заблокированных комиссий по счёту","Currently blocked commission amount on account")+": "+
                                                   ::DoubleToString(this.GetProperty(property),(int)this.CurrencyDigits())                                                                :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return a description of an account string property               |
//+------------------------------------------------------------------+
string CAccount::GetPropertyDescription(ENUM_ACCOUNT_PROP_STRING property)
  {
   return
     (
      property==ACCOUNT_PROP_NAME      ?  TextByLanguage("Имя клиента","Client name")+": \""+this.GetProperty(property)+"\""                                                     :
      property==ACCOUNT_PROP_SERVER    ?  TextByLanguage("Имя торгового сервера","Trade server name")+": \""+this.GetProperty(property)+"\""                                     :
      property==ACCOUNT_PROP_CURRENCY  ?  TextByLanguage("Валюта депозита","Account currency")+": \""+this.GetProperty(property)+"\""                                            :
      property==ACCOUNT_PROP_COMPANY   ?  TextByLanguage("Имя компании, обслуживающей счет","Name of company that serves account")+": \""+this.GetProperty(property)+"\""  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return a trading account type name                               |
//+------------------------------------------------------------------+
string CAccount::TradeModeDescription(void) const
  {
   return
     (
      this.TradeMode()==ACCOUNT_TRADE_MODE_DEMO    ? TextByLanguage("Демонстрационный счёт","Demo account") :
      this.TradeMode()==ACCOUNT_TRADE_MODE_CONTEST ? TextByLanguage("Конкурсный счёт","Contest account")    :
      this.TradeMode()==ACCOUNT_TRADE_MODE_REAL    ? TextByLanguage("Реальный счёт","Real account")         :
      TextByLanguage("Неизвестный тип счёта","Unknown account type")
     );
  }
//+------------------------------------------------------------------+
//| Return a description of a mode for setting                       |
//| minimum available margin level                                   |
//+------------------------------------------------------------------+
string CAccount::MarginSOModeDescription(void) const
  {
   return
     (
      this.MarginSOMode()==ACCOUNT_STOPOUT_MODE_PERCENT  ? TextByLanguage("Уровень задается в процентах","Account stop out mode in percentage") : 
      TextByLanguage("Уровень задается в деньгах","Account stop out mode in money")
     );
  }
//+------------------------------------------------------------------+
//| Return a description of a margin calculation mode                |
//+------------------------------------------------------------------+
string CAccount::MarginModeDescription(void) const
  {
   return
     (
      this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_NETTING ? TextByLanguage("Внебиржевой рынок в режиме \"Неттинг\"","Netting mode") :
      this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? TextByLanguage("Внебиржевой рынок в режиме \"Хеджинг\"","Hedging mode") :
      TextByLanguage("Биржевой рынок","Exchange markets mode")
     );
  }
//+------------------------------------------------------------------+

口座クラスの完全なコードは、本稿に添付されているファイルでご覧ください。クラスをテストしましょう。

口座オブジェクトのテスト

クラスが口座データを正しく受信するかどうかを確認するには、クラスファイルを一時的にライブラリのメインオブジェクト(CEngine class)にインクルードします。

//+------------------------------------------------------------------+
//|                                                       Engine.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 "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Services\TimerCounter.mqh"
#include "Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| ライブラリ基本クラス                                               |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {

口座オブジェクトクラスファイルをインクルードすると、プログラムはオブジェクトを表示できるようになります。

クラスをテストする目的で、前の記事のEA(\MQL5\Experts\TestDoEasy\Part11\TestDoEasyPart11.mq5)を\MQL5\Experts\TestDoEasy\Part12TestDoEasyPart12_1.mq5として保存します。

口座オブジェクトを含めてテストするには、EAのOnInit()ハンドラーに文字列を追加します(確認は初期化中に実行されます)。

//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal, 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;

//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Fast check of the account object
   CAccount* acc=new CAccount();
   if(acc!=NULL)                
     {                          
      acc.PrintShort();         
      acc.Print();              
      delete acc;               
     }                          
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

ここで口座オブジェクトを作成して、成功したら、簡潔な口座データ口座パラメータの完全なリストを後に操作ログで表示します。完了したら口座オブジェクトを削除します

任意の銘柄チャートでEAを起動して、[エキスパート]ログを表示します。


口座データが正しく表示されています。

口座オブジェクトのコレクション

口座を変更するとすべてのEAが再初期化されるため、最初にデストラクタが呼び出され、次にクラスコンストラクタが呼び出されて、口座が変更される前に存在していた以前の口座オブジェクトは失われます。口座コレクションを維持するには、端末が以前に接続した口座のデータを記憶する必要があります。これを行うには、現在の口座オブジェクトのデータを口座コレクションクラスデストラクタのファイルに保存し、クラスコンストラクタのファイルからデータをダウンロードします。したがって、口座コレクションには、ライブラリに基づくプログラム操作中に端末が接続したすべての口座のデータが入力されます。ファイルはすべてのクライアント端末の共通フォルダに保存されるため、ライブラリベースのプログラムがその端末で動作する場合、起動された各端末はPC上の端末が接続されているすべての口座を参照します。
新しい口座コレクションクラスでは、既存のすべての口座のデータをさまざまなパラメータで比較できます。

口座オブジェクトをファイルに保存するには、CAccountクラスのファイルにデータを保存するメソッドを作成する必要があります。
作成されたオブジェクトはすべて、標準ライブラリの基本オブジェクトであるCObjectから継承されています。このクラスにはファイルにオブジェクトを保存し読み取る仮想メソッドもあります。

//+------------------------------------------------------------------+
//| Class CObject.                                                   |
//| Purpose: Base class for storing elements.                        |
//+------------------------------------------------------------------+
class CObject
  {
private:
   CObject          *m_prev;               // previous item of list
   CObject          *m_next;               // next item of list

public:
                     CObject(void): m_prev(NULL),m_next(NULL)            {                 }
                    ~CObject(void)                                       {                 }
   //--- methods to access protected data
   CObject          *Prev(void)                                    const { return(m_prev); }
   void              Prev(CObject *node)                                 { m_prev=node;    }
   CObject          *Next(void)                                    const { return(m_next); }
   void              Next(CObject *node)                                 { m_next=node;    }
   //--- methods for working with files
   virtual bool      Save(const int file_handle)                         { return(true);   }
   virtual bool      Load(const int file_handle)                         { return(true);   }
   //--- オブジェクト特定法
   virtual int       Type(void)                                    const { return(0);      }
   //--- method of comparing the objects
   virtual int       Compare(const CObject *node,const int mode=0) const { return(0);      }
  };
//+------------------------------------------------------------------+

このメソッドは何もしません。必要となる子孫クラス(CAccountクラス)でメソッドを再定義する必要があります。

すべての口座オブジェクトプロパティをファイルに保存するには、単純な構造体を使用してそれをファイルに保存します。ただし、オブジェクトフィールドには行が含まれているため、これはPOD構造ではありません。したがって、一定サイズの構造体フィールドのuchar配列に保存する場合、オブジェクトのすべての文字列プロパティを変換する必要があります。この場合、FileWriteArray()関数を使用して、口座オブジェクトプロパティのすべてのデータを構造体としてファイルに保存できます。

ライブラリファイルと一定サイズのuchar配列を格納するディレクトリを作成するには、Defines.mqhファイルにマクロ置換を作成します。

#define DIRECTORY                   ("DoEasy\\")               // Library directory for placing class object folders
#define UCHAR_ARRAY_SIZE            (64)                       // Size of uchar arrays for storing string properties

コメント行の長さは64文字に制限されているため、この値に合わせて配列サイズが作成されます。さらに、注文オブジェクトをファイルに保存する必要があり、64シンボル未満の長さが不適切であることが判明する場合があります。口座の文字列プロパティには、より長い文字列が割り当てられていることがあります。口座を提供している会社の名前を保存するのにサイズが不十分であることがテストで示された場合は、いつでもサイズを増やすことができます。

CAccountクラスのprivateセクションに、口座オブジェクトプロパティを保存するための構造体ファイルを使用するためのクラスメンバ変数を作成します。

//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CObject
  {
private:
   struct SData
     {
      //--- Account integer properties
      long           login;                        // ACCOUNT_LOGIN (Account number)
      int            trade_mode;                   // ACCOUNT_TRADE_MODE (Trading account type)
      long           leverage;                     // ACCOUNT_LEVERAGE (Leverage)
      int            limit_orders;                 // ACCOUNT_LIMIT_ORDERS (Maximum allowed number of active pending orders)
      int            margin_so_mode;               // ACCOUNT_MARGIN_SO_MODE (Mode of setting the minimum available margin level)
      bool           trade_allowed;                // ACCOUNT_TRADE_ALLOWED (Permission to trade for the current account from the server side)
      bool           trade_expert;                 // ACCOUNT_TRADE_EXPERT (Permission to trade for an EA from the server side)
      int            margin_mode;                  // ACCOUNT_MARGIN_MODE (Margin calculation mode)
      int            currency_digits;              // ACCOUNT_CURRENCY_DIGITS (Number of digits for an account currency)
      //--- Account real properties
      double         balance;                      // ACCOUNT_BALANCE (Account balance in the deposit currency)
      double         credit;                       // ACCOUNT_CREDIT (Credit in the deposit currency)
      double         profit;                       // ACCOUNT_PROFIT (Current profit on an account in the deposit currency)
      double         equity;                       // ACCOUNT_EQUITY (Equity on an account in the deposit currency)
      double         margin;                       // ACCOUNT_MARGIN (Reserved margin on an account in the deposit currency)
      double         margin_free;                  // ACCOUNT_MARGIN_FREE (Free funds available for opening a position in the deposit currency)
      double         margin_level;                 // ACCOUNT_MARGIN_LEVEL (Margin level on an account in %)
      double         margin_so_call;               // ACCOUNT_MARGIN_SO_CALL (Margin Call level)
      double         margin_so_so;                 // ACCOUNT_MARGIN_SO_SO (StopOut level)
      double         margin_initial;               // ACCOUNT_MARGIN_INITIAL (Funds reserved on an account to ensure a guarantee amount for all pending orders)
      double         margin_maintenance;           // ACCOUNT_MARGIN_MAINTENANCE (Funds reserved on an account to ensure a minimum amount for all open positions)
      double         assets;                       // ACCOUNT_ASSETS (Current assets on an account)
      double         liabilities;                  // ACCOUNT_LIABILITIES (Current liabilities on an account)
      double         comission_blocked;            // ACCOUNT_COMMISSION_BLOCKED (Current sum of blocked commissions on an account)
      //--- Account string properties
      uchar          name[UCHAR_ARRAY_SIZE];       // ACCOUNT_NAME (Client name)
      uchar          server[UCHAR_ARRAY_SIZE];     // ACCOUNT_SERVER (Trade server name)
      uchar          currency[UCHAR_ARRAY_SIZE];   // ACCOUNT_CURRENCY (Deposit currency)
      uchar          company[UCHAR_ARRAY_SIZE];    // ACCOUNT_COMPANY (Name of a company serving an account)
     };
   SData             m_struct_obj;                                      // Account object structure
   uchar             m_uchar_array[];                                   // uchar array of the account object structure

   
//--- Object properties
   long              m_long_prop[ACCOUNT_PROP_INTEGER_TOTAL];           // Integer properties
   double            m_double_prop[ACCOUNT_PROP_DOUBLE_TOTAL];          // Real properties
   string            m_string_prop[ACCOUNT_PROP_STRING_TOTAL];          // String properties

//--- Return the array of the index the account (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_ACCOUNT_PROP_DOUBLE property) const { return(int)property-ACCOUNT_PROP_INTEGER_TOTAL;                                   }
   int               IndexProp(ENUM_ACCOUNT_PROP_STRING property) const { return(int)property-ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_DOUBLE_TOTAL;         }
protected:
//--- Create (1) the account object structure and (2) the account object from the structure
   bool              ObjectToStruct(void);
   void              StructToObject(void);
public:
コードによると、CAccountクラスのprotectedセクションではさらに2つのメソッドが宣言されています。これらは、オブジェクトプロパティフィールドから構造体を作成するためのメソッドおよび反対に口座オブジェクトを構造体から作成するためのメソッドです。

1番目のメソッドは口座オブジェクトをファイルに書き込むために使用され、2番目のメソッドはファイルから読み取るために使用されます。

メソッドをクラス本体の外側で実装しましょう。

//+------------------------------------------------------------------+
//| Create the account object structure                              |
//+------------------------------------------------------------------+
bool CAccount::ObjectToStruct(void)
  {
//--- Save the integer properties
   this.m_struct_obj.login=this.Login();
   this.m_struct_obj.trade_mode=this.TradeMode();
   this.m_struct_obj.leverage=this.Leverage();
   this.m_struct_obj.limit_orders=(int)this.LimitOrders();
   this.m_struct_obj.margin_so_mode=this.MarginSOMode();
   this.m_struct_obj.trade_allowed=this.TradeAllowed();
   this.m_struct_obj.trade_expert=this.TradeExpert();
   this.m_struct_obj.margin_mode=this.MarginMode();
   this.m_struct_obj.currency_digits=(int)this.CurrencyDigits();
//--- Save the real properties
   this.m_struct_obj.balance=this.Balance();
   this.m_struct_obj.credit=this.Credit();
   this.m_struct_obj.profit=this.Profit();
   this.m_struct_obj.equity=this.Equity();
   this.m_struct_obj.margin=this.Margin();
   this.m_struct_obj.margin_free=this.MarginFree();
   this.m_struct_obj.margin_level=this.MarginLevel();
   this.m_struct_obj.margin_so_call=this.MarginSOCall();
   this.m_struct_obj.margin_so_so=this.MarginSOSO();
   this.m_struct_obj.margin_initial=this.MarginInitial();
   this.m_struct_obj.margin_maintenance=this.MarginMaintenance();
   this.m_struct_obj.assets=this.Assets();
   this.m_struct_obj.liabilities=this.Liabilities();
   this.m_struct_obj.comission_blocked=this.ComissionBlocked();
//--- Save the string properties
   ::StringToCharArray(this.Name(),this.m_struct_obj.name);
   ::StringToCharArray(this.Server(),this.m_struct_obj.server);
   ::StringToCharArray(this.Currency(),this.m_struct_obj.currency);
   ::StringToCharArray(this.Company(),this.m_struct_obj.company);
   //--- Saving the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      ::Print(DFUN,TextByLanguage("Не удалось сохранить структуру объекта в uchar-массив, ошибка ","Failed to save object structure to uchar array, error "),(string)::GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

コードからわかるように、すべてのintegerおよびrealオブジェクトプロパティは、同じ名前の構造体フィールドに保存されます。文字列プロパティを保存するには、文字列をuchar配列に変換して、適切な構造体フィールドに保存します。
オブジェクトのプロパティを保存すると、構造体全体がuchar配列に保存され、ファイルに保存されます。

//+------------------------------------------------------------------+
//| Create the account object from the structure                     |
//+------------------------------------------------------------------+
void CAccount::StructToObject(void)
  {
//--- 整数型プロパティを保存する
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = this.m_struct_obj.login;
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = this.m_struct_obj.trade_mode;
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = this.m_struct_obj.leverage;
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = this.m_struct_obj.limit_orders;
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = this.m_struct_obj.margin_so_mode;
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = this.m_struct_obj.trade_allowed;
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = this.m_struct_obj.trade_expert;
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = this.m_struct_obj.margin_mode;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = this.m_struct_obj.currency_digits;
//--- 実数型プロパティを保存する
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = this.m_struct_obj.balance;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = this.m_struct_obj.credit;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = this.m_struct_obj.profit;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = this.m_struct_obj.equity;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = this.m_struct_obj.margin;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = this.m_struct_obj.margin_free;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = this.m_struct_obj.margin_level;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = this.m_struct_obj.margin_so_call;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = this.m_struct_obj.margin_so_so;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = this.m_struct_obj.margin_initial;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=this.m_struct_obj.margin_maintenance;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = this.m_struct_obj.assets;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = this.m_struct_obj.liabilities;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=this.m_struct_obj.comission_blocked;
//--- 文字列プロパティを保存する
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::CharArrayToString(this.m_struct_obj.name);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::CharArrayToString(this.m_struct_obj.server);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::CharArrayToString(this.m_struct_obj.currency);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::CharArrayToString(this.m_struct_obj.company);
  }
//+------------------------------------------------------------------+

構造体フィールドを口座オブジェクトプロパティに逆変換するメソッドは、上記で説明した1番目のメソッドとほぼ同じです。
ここでは、オブジェクトの口座文字列プロパティは、構造体のuchar配列を文字列に変換することによって取得されます。

これで、オブジェクトをファイルに保存したりファイルに/からアップロード/ダウンロードしたりする仮想メソッドを作成する準備が整いました。

CAccountクラスのpublicセクションで、Save()およびLoad()仮想メソッドを宣言します。

public:
//--- コンストラクタ
                     CAccount(void);
//--- Set (1) integer, (2) real and (3) string account properties
   void              SetProperty(ENUM_ACCOUNT_PROP_INTEGER property,long value)        { this.m_long_prop[property]=value;                                  }
   void              SetProperty(ENUM_ACCOUNT_PROP_DOUBLE property,double value)       { this.m_double_prop[this.IndexProp(property)]=value;                }
   void              SetProperty(ENUM_ACCOUNT_PROP_STRING property,string value)       { this.m_string_prop[this.IndexProp(property)]=value;                }
//--- Return (1) integer, (2) real and (3) string account properties from the properties array
   long              GetProperty(ENUM_ACCOUNT_PROP_INTEGER property)             const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_ACCOUNT_PROP_DOUBLE property)              const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_ACCOUNT_PROP_STRING property)              const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the MarginCall and StopOut levels calculation in %
   bool              IsPercentsForSOLevels(void)                                 const { return this.MarginSOMode()==ACCOUNT_STOPOUT_MODE_PERCENT;          }
//--- プロパティをサポートしている注文のフラグを返す
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_INTEGER property)               { return true; }
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_DOUBLE property)                { return true; }
   virtual bool      SupportProperty(ENUM_ACCOUNT_PROP_STRING property)                { return true; }

//--- Compare CAccount objects with one another by all possible properties (for sorting the lists by a specified account object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CAccount objects by account properties (to search for equal account objects)
   bool              IsEqual(CAccount* compared_account) const;
//--- (1) Save the account object to the file, (2), download the account object from the file
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
//+------------------------------------------------------------------+
//| Methods of simplified access to the account object properties    |
//+------------------------------------------------------------------+

口座オブジェクトをファイルに保存するメソッドとファイルからダウンロードするメソッドを書きましょう。

//+------------------------------------------------------------------+
//| Save the account object to the file                              |
//+------------------------------------------------------------------+
bool CAccount::Save(const int file_handle)
  {
   if(!this.ObjectToStruct())
     {
      Print(DFUN,TextByLanguage("Не удалось создать структуру объекта.","Could not create object structure"));
      return false;
     }
   if(::FileWriteArray(file_handle,this.m_uchar_array)==0)
     {
      Print(DFUN,TextByLanguage("Не удалось записать uchar-массив в файл.","Could not write uchar array to file"));
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

ここで

以下は、ファイルからオブジェクトデータをダウンロードするメソッドです。

//+------------------------------------------------------------------+
//| Download the account object from the file                        |
//+------------------------------------------------------------------+
bool CAccount::Load(const int file_handle)
  {
   if(::FileReadArray(file_handle,this.m_uchar_array)==0)
     {
      Print(DFUN,TextByLanguage("Не удалось загрузить uchar-массив из файла.","Could not load uchar array from file"));
      return false;
     }
   if(!::CharArrayToStruct(this.m_struct_obj,this.m_uchar_array))
     {
      Print(DFUN,TextByLanguage("Не удалось создать структуру объекта из uchar-массива.","Could not create object structure from uchar array"));
      return false;
     }
   this.StructToObject();
   return true;
  }
//+------------------------------------------------------------------+

ここで

  • 以前に読み取り用に開かれたファイルのハンドルがメソッドに渡されます
  • ファイルデータをuchar配列にアップロードします
  • 配列データをPOD構造に保存します
  • POD構造データをオブジェクトフィールドに書き込みます
ファイルとの間でデータをアップロード/ダウンロードするための口座オブジェクトを改善しました。

口座コレクションの操作は以下です。実行プログラムを起動すると、現在の口座が確認されます。現在の口座のデータを表す口座オブジェクトが作成され、口座コレクションリストに配置されます。次に、以前に保存した口座のファイルを含むフォルダを表示します。ファイルが含まれている場合は、現在の口座との一貫性が維持されているかどうかを1つずつ確認し、口座コレクションリストに配置します。リストを作成した後、タイマーで現在の口座のステータスを確認し、変更が発生した場合は記録します。
一部の変更については、イベントを作成してプログラムに送信し、口座パラメータの変更を管理します。たとえば、レバレッジの突然の変更は、ユーザとユーザプログラムへの遅れなしの通知を要する、非常に明らかで不快なイベントです。

新しいコレクションリストを作成するだけでなく、タイマーで作業する必要があるため、 Defines.mqhtimerパラメータでマクロ置換を作成してIDをリストします。また、注文、取引、ポジションのコレクションタイマーに対して以前に作成したマクロ置換の名前を必ず変更してください(名前に「ORD」を追加して、異なるコレクションタイマーに属するマクロ置換を区別します)。口座データの更新の一時停止を1秒に設定します。変更を追跡し、システムの負荷を減らすにはこれで十分だと思います。

//+------------------------------------------------------------------+
//| マクロ置換                                                        |
//+------------------------------------------------------------------+
//--- Describe the function with the error line number
#define DFUN_ERR_LINE               (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                        (__FUNCTION__+": ")        // "Function description"
#define COUNTRY_LANG                ("Russian")                // Country language
#define END_TIME                    (D'31.12.3000 23:59:59')   // End date for requesting account history data
#define TIMER_FREQUENCY             (16)                       // Minimal frequency of the library timer in milliseconds
//--- Parameters of orders and deals collection timer
#define COLLECTION_ORD_PAUSE        (250)                      // Orders and deals collection timer pause in milliseconds
#define COLLECTION_ORD_COUNTER_STEP (16)                       // Increment of the orders and deals collection timer counter
#define COLLECTION_ORD_COUNTER_ID   (1)                        // Orders and deals collection timer counter ID
//--- Parameters of the account collection timer
#define COLLECTION_ACC_PAUSE        (1000)                     // Account collection timer pause in milliseconds
#define COLLECTION_ACC_COUNTER_STEP (16)                       // Account timer counter increment
#define COLLECTION_ACC_COUNTER_ID   (2)                        // Account timer counter ID
//--- Collection list IDs
#define COLLECTION_HISTORY_ID       (0x7778+1)                 // Historical collection list ID
#define COLLECTION_MARKET_ID        (0x7778+2)                 // Market collection list ID
#define COLLECTION_EVENTS_ID        (0x7778+3)                 // Event collection list ID
#define COLLECTION_ACCOUNT_ID       (0x7778+4)                 // Account collection list ID
//--- Data parameters for file operations
#define DIRECTORY                   ("DoEasy\\")               // Library directory for storing object folders
#define UCHAR_ARRAY_SIZE            (64)                       // Size of uchar arrays for storing string properties

CEngineクラスで、COLLECTION_PAUSE、COLLECTION_COUNTER_STEP、COLLECTION_COUNTER_IDを適切なマクロ置換名で置き換えます(COLLECTION_ORD_PAUSE、COLLECTION_ORD_COUNTER_STEP、COLLECTION_ORD_COUNTER_ID)。

口座コレクションを作成するため、これは複数の口座オブジェクトのプロパティを比較する機能を意味します。これを行うには、CSelectクラスの口座コレクションに選択メソッドと並べ替えメソッドを追加して、条件に適合するオブジェクトを選択します。このクラスは第3部で説明されています。

\MQL5\Include\DoEasy\Servicesライブラリのサービスクラスフォルダで選択されたSelect.mqhファイルを開き、ファイルを口座クラスに接続し、口座オブジェクトを使用するための新しいメソッドを追加します。

//+------------------------------------------------------------------+
//|                                                       Select.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 <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| Storage list                                                     |
//+------------------------------------------------------------------+
CArrayObj   ListStorage; // Storage object for storing sorted collection lists
//+------------------------------------------------------------------+
//| 基準を満たすオブジェクトを並び替えるクラス                            |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- 2つの値を比較するメソッド
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
//+------------------------------------------------------------------+
//| Methods of working with orders                                   |
//+------------------------------------------------------------------+
   //--- 指定された基準を満たす(1)整数、(2)実数、(3)文字列プロパティのうちの1つを持つ注文のリストを返す
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the order index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
   //--- Return the order index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with events                                   |
//+------------------------------------------------------------------+
   //--- Return the list of events with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with accounts                                 |
//+------------------------------------------------------------------+
   //--- Return the list of accounts with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
  };
//+------------------------------------------------------------------+

宣言されたメソッドをクラス本体の外側に実装します。

//+------------------------------------------------------------------+
//| Methods of working with account lists                            |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of accounts with one integer                     |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CAccount *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of accounts with one real                        |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CAccount *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of accounts with one string                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CAccount *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CAccount *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CAccount *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CAccount *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CAccount *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CAccount *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CAccount *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMin(CArrayObj* list_source,ENUM_ACCOUNT_PROP_INTEGER property)
  {
   int index=0;
   CAccount* min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CAccount* obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMin(CArrayObj* list_source,ENUM_ACCOUNT_PROP_DOUBLE property)
  {
   int index=0;
   CAccount* min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CAccount* obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the account index in the list                             |
//| リストでの注文のインデックスを返す                                   |
//+------------------------------------------------------------------+
int CSelect::FindAccountMin(CArrayObj* list_source,ENUM_ACCOUNT_PROP_STRING property)
  {
   int index=0;
   CAccount* min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CAccount* obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+

メソッドの動作は第3部で考察されたので、ここでは説明しません。必要に応じて、第3部をこちらでご覧ください。

口座コレクションクラスのワークを作成しましょう。

MQL5\Include\DoEasy\Collections\ライブラリファイルで、新しいAccountsCollection.mqhクラスファイルを作成して、 必要なクラスファイルをインクルードしてすぐに標準メソッドを書き込みます。

//+------------------------------------------------------------------+
//|                                           AccountsCollection.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 "ListObj.mqh"                    
#include "..\Services\Select.mqh"         
#include "..\Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {
private:
   CListObj          m_list_accounts;                 // List of account objects
public:
//--- Return the full event collection list "as is"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_accounts;                                            }
//--- 比較された基準を満たす選択された(1)整数、(2)実数、(3)文字列プロパティののリストを返す
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);   }
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);   }
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);   }
//--- コンストラクタ
                     CAccountsCollection();

  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CAccountsCollection::CAccountsCollection(void)
  {
   this.m_list_accounts.Clear();
   this.m_list_accounts.Sort(SORT_BY_ACCOUNT_LOGIN);
   this.m_list_accounts.Type(COLLECTION_ACCOUNT_ID);
  }
//+------------------------------------------------------------------+

この短いクラスコンストラクタのコードは、口座オブジェクトを格納するリストを準備するために使用されます。

  • リストがクリアされます 
  • リストが口座番号で並び替えられるように設定されます
  • 口座コレクションリストIDがリストに割り当てられます

口座コレクションクラスの作業は次のようにまとめることができます。プログラムが銘柄チャートに接続されると、1つの口座の現在のデータにアクセスして、そのプロパティの変更を追跡し、変更に対応することができます。残りの口座は、プログラム内でのみ「追跡」できます。追跡できるのは、新しい口座への接続時の最後の状態です。したがって、追跡できるのは現在の口座の変更のみですが、口座接続リストには、これまで接続したすべての口座オブジェクトが含まれます。さらに、すべての口座データは任意のプロパティで比較できます。

重要な口座プロパティを追跡するには、「ハッシュコントロール」を使用して、現在の時点でのすべての口座プロパティの合計を前の確認で取得した合計と比較します。合計が異なる場合はすぐに、具体的に何が変更されたかを確認し、適切な変更フラグを設定します。次に、口座イベント(重要な口座プロパティの変更)を追跡する場合、フラグはすべての追跡されたプロパティを確認し、変更されたプロパティに関するイベントをプログラムに送信する必要があることを通知します。

必要な変数とクラスメソッドをすべて追加して、後で分析しましょう。

//+------------------------------------------------------------------+
//|                                           AccountsCollection.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 "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| Account collection                                               |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {
private:
   struct MqlDataAccount
     {
      double         hash_sum;               // Account data hash sum
      //--- Account integer properties
      long           login;                  // ACCOUNT_LOGIN (Account number)
      long           leverage;               // ACCOUNT_LEVERAGE (Leverage)
      int            limit_orders;           // ACCOUNT_LIMIT_ORDERS (Maximum allowed number of active pending orders)
      bool           trade_allowed;          // ACCOUNT_TRADE_ALLOWED (Permission to trade for the current account from the server side)
      bool           trade_expert;           // ACCOUNT_TRADE_EXPERT (Permission to trade for an EA from the server side)
      //--- Account real properties
      double         balance;                // ACCOUNT_BALANCE (Account balance in a deposit currency)
      double         credit;                 // ACCOUNT_CREDIT (Credit in a deposit currency)
      double         profit;                 // ACCOUNT_PROFIT (Current profit on an account in the account currency)
      double         equity;                 // ACCOUNT_EQUITY (Equity on an account in the deposit currency)
      double         margin;                 // ACCOUNT_MARGIN (Reserved margin on an account in a deposit currency)
      double         margin_free;            // ACCOUNT_MARGIN_FREE (Free funds available for opening a position in a deposit currency)
      double         margin_level;           // ACCOUNT_MARGIN_LEVEL (Margin level on an account in %)
      double         margin_so_call;         // ACCOUNT_MARGIN_SO_CALL (Margin Call)
      double         margin_so_so;           // ACCOUNT_MARGIN_SO_SO (Stop Out)
      double         margin_initial;         // ACCOUNT_MARGIN_INITIAL (Funds reserved on an account to ensure a guarantee amount for all pending orders)
      double         margin_maintenance;     // ACCOUNT_MARGIN_MAINTENANCE (Funds reserved on an account to ensure a minimum amount for all open positions)
      double         assets;                 // ACCOUNT_ASSETS (Current assets on an account)
      double         liabilities;            // ACCOUNT_LIABILITIES (Current liabilities on an account)
      double         comission_blocked;      // ACCOUNT_COMMISSION_BLOCKED (Current sum of blocked commissions on an account)
     };
   MqlDataAccount    m_struct_curr_account;  // Account current data
   MqlDataAccount    m_struct_prev_account;  // Account previous data
   
   CListObj          m_list_accounts;                                   // Account object list
   string            m_folder_name;                                     // Name of a folder account objects are stored in
   int               m_index_current;                                   // Index of an account object featuring the current account data
   bool              m_is_account_event;                                // Event flag in the account data

//--- Write the current account data to the account object properties
  void               SetAccountsParams(CAccount* account);
//--- Save the current data status values of the current account as previous ones
   void              SavePrevValues(void)                                                                   { this.m_struct_prev_account=this.m_struct_curr_account;                }
//--- Check the account object presence in the collection list
   bool              IsPresent(CAccount* account);
//--- Find and return the account object index with the current account data
   int               Index(void);
public:
//--- Return the full account collection list "as is"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_accounts;                                         }
//--- 比較された基準を満たす選択された(1)整数、(2)実数、(3)文字列プロパティののリストを返す
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
//--- Return the (1) current account object index, (2) occurred event flag in the account data
   int               IndexCurrentAccount(void)                                                        const { return this.m_index_current;                                          }
   bool              IsAccountEvent(void)                                                             const { return this.m_is_account_event;                                       }

//--- Constructor, destructor
                     CAccountsCollection();
                    ~CAccountsCollection();
//--- Add the account object to the list
   bool              AddToList(CAccount* account);
//--- (1) Save account objects from the list to the files
//--- (2) Save account objects from the files to the list
   bool              SaveObjects(void);
   bool              LoadObjects(void);
//--- Update the current account data
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

クラスのprivateセクションには、重要な口座プロパティを格納するためのMqlDataAccount構造体があります。追跡されたすべての口座オブジェクトプロパティが保存されます。構造体型を持つ変数には、現在の口座データを保存するものと以前のデータを保存するものの2つがあります。構造体内で変更されない唯一のプロパティは、口座番号を保存するloginです。このフィールド値は、最初の起動を定義するために使用されます。「login」フィールドにゼロが含まれている場合、これが最初の起動であり、後続の比較のために、現在の口座のステータスを前のステータスとして保存する必要があります。構造体のhashフィールドで、すべての構造体フィールドの値の合計を設定し、「前の」口座ステータスの構造体で設定された値と比較します。これら2つのフィールドの値の不一致が検出された場合、口座オブジェクトプロパティの変更が検出されたと見なされます。

口座コレクションリストは異なる口座(ライブラリベースのプログラム操作中に接続したすべての口座と現在の口座)のデータを格納するためのものであるため、ファイルから読み取ることでリストに保存された口座データを追跡することはできませんが、リスト内の口座オブジェクトの正確なインデックスを知る必要があります。これが、追跡する必要がある現在の口座オブジェクトです。このインデックスは、口座オブジェクトを取得し、タイマーでプロパティのステータスを確認するために使用されます。また、口座オブジェクトプロパティを変更するフラグとして使用されるクラスメンバ変数およびクラスオブジェクトを格納するライブラリディレクトリ内のフォルダのアドレスを示す変数があります。

同じprivateセクションには4つのメソッドがあります。その実装を見てみましょう。

以下は、現在の口座のデータを口座オブジェクトプロパティに書き込むメソッドです。

//+------------------------------------------------------------------+
//| Write the current account data to the account object properties  |
//+------------------------------------------------------------------+
void CAccountsCollection::SetAccountsParams(CAccount *account)
  {
   if(account==NULL)
      return;
//--- Account number
   this.m_struct_curr_account.login=account.Login();
//--- Leverage
   account.SetProperty(ACCOUNT_PROP_LEVERAGE,::AccountInfoInteger(ACCOUNT_LEVERAGE));
   this.m_struct_curr_account.leverage=account.Leverage();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.leverage;
//--- Maximum allowed number of active pending orders
   account.SetProperty(ACCOUNT_PROP_LIMIT_ORDERS,::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS));
   this.m_struct_curr_account.limit_orders=(int)account.LimitOrders();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.limit_orders;
//--- Permission to trade for the current account from the server side
   account.SetProperty(ACCOUNT_PROP_TRADE_ALLOWED,::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED));
   this.m_struct_curr_account.trade_allowed=account.TradeAllowed();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.trade_allowed;
//--- Permission to trade for an EA from the server side
   account.SetProperty(ACCOUNT_PROP_TRADE_EXPERT,::AccountInfoInteger(ACCOUNT_TRADE_EXPERT));
   this.m_struct_curr_account.trade_expert=account.TradeExpert();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.trade_expert;
//--- Account balance in a deposit currency
   account.SetProperty(ACCOUNT_PROP_BALANCE,::AccountInfoDouble(ACCOUNT_BALANCE));
   this.m_struct_curr_account.balance=account.Balance();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.balance;
//--- Credit in a deposit currency
   account.SetProperty(ACCOUNT_PROP_CREDIT,::AccountInfoDouble(ACCOUNT_CREDIT));
   this.m_struct_curr_account.credit=account.Credit();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.credit;
//--- Current profit on an account in the account currency
   account.SetProperty(ACCOUNT_PROP_PROFIT,::AccountInfoDouble(ACCOUNT_PROFIT));
   this.m_struct_curr_account.profit=account.Profit();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.profit;
//--- Equity on an account in the deposit currency
   account.SetProperty(ACCOUNT_PROP_EQUITY,::AccountInfoDouble(ACCOUNT_EQUITY));
   this.m_struct_curr_account.equity=account.Equity();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.equity;
//--- Reserved margin on an account in a deposit currency
   account.SetProperty(ACCOUNT_PROP_MARGIN,::AccountInfoDouble(ACCOUNT_MARGIN));
   this.m_struct_curr_account.margin=account.Margin();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin;
//--- Free funds available for opening a position in a deposit currency
   account.SetProperty(ACCOUNT_PROP_MARGIN_FREE,::AccountInfoDouble(ACCOUNT_MARGIN_FREE));
   this.m_struct_curr_account.margin_free=account.MarginFree();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_free;
//--- Margin level on an account in %
   account.SetProperty(ACCOUNT_PROP_MARGIN_LEVEL,::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
   this.m_struct_curr_account.margin_level=account.MarginLevel();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_level;
//--- Margin Call level
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_CALL,::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
   this.m_struct_curr_account.margin_so_call=account.MarginSOCall();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_so_call;
//--- StopOut level
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_SO,::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
   this.m_struct_curr_account.margin_so_so=account.MarginSOSO();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_so_so;
//--- Funds reserved on an account to ensure a guarantee amount for all pending orders
   account.SetProperty(ACCOUNT_PROP_MARGIN_INITIAL,::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL));
   this.m_struct_curr_account.margin_initial=account.MarginInitial();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_initial;
//--- Funds reserved on an account to ensure a minimum amount for all open positions
   account.SetProperty(ACCOUNT_PROP_MARGIN_MAINTENANCE,::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE));
   this.m_struct_curr_account.margin_maintenance=account.MarginMaintenance();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.margin_maintenance;
//--- Current assets on an account
   account.SetProperty(ACCOUNT_PROP_ASSETS,::AccountInfoDouble(ACCOUNT_ASSETS));
   this.m_struct_curr_account.assets=account.Assets();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.assets;
//--- Current liabilities on an account
   account.SetProperty(ACCOUNT_PROP_LIABILITIES,::AccountInfoDouble(ACCOUNT_LIABILITIES));
   this.m_struct_curr_account.liabilities=account.Liabilities();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.liabilities;
//--- Current sum of blocked commissions on an account
   account.SetProperty(ACCOUNT_PROP_COMMISSION_BLOCKED,::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED));
   this.m_struct_curr_account.comission_blocked=account.ComissionBlocked();
   this.m_struct_curr_account.hash_sum+=(double)this.m_struct_curr_account.comission_blocked;
  }
//+------------------------------------------------------------------+

更新を例としてレバレッジを見てみましょう。
メソッドは口座オブジェクトへのポインタを受け取り現在の口座データが口座オブジェクトのフィールド口座の現在のステータスを表す構造体のフィールドに追加されます。次に、取得した各プロパティの値がハッシュに追加されます。

現在の口座ステータスの構造体を以前のステータスの構造体に保存するSavePrevValues()メソッドは、単に現在のステータスの構造体を以前のステータス構造体にコピーします。

以下は、コレクションリスト内の口座オブジェクトの存在を確認するメソッドです。

//+------------------------------------------------------------------+
//| Check the presence of the account object in the collection list  |
//+------------------------------------------------------------------+
bool CAccountsCollection::IsPresent(CAccount *account)
  {
   int total=this.m_list_accounts.Total();
   if(total==0)
      return false;
   for(int i=0;i<total;i++)
     {
      CAccount* check=this.m_list_accounts.At(i);
      if(check==NULL)
         continue;
      if(check.IsEqual(account))
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

このメソッドは、コレクションリストでデータが見つかるべき口座オブジェクトへのポインタを受け取ります。検索は、口座オブジェクトクラスを作成するときに前述したIsEqual()メソッドによって、クライアント名と会社名だけでなく口座番号を使用して実行されます。
口座オブジェクトリスト(ループ内)を使用してリストからオブジェクトを取得し、そのデータをメソッドに渡されたオブジェクトのデータと比較します
データが一致した場合はtrueを返します

その他の場合、ループの完了時に等しいオブジェクトが見つからなければfalseを返します

以下は、リスト内の現在の口座データを含む口座オブジェクトのインデックスを返すメソッドです。

//+------------------------------------------------------------------+
//| Return the account object index with the current account data    |
//+------------------------------------------------------------------+
int CAccountsCollection::Index(void)
  {
   int total=this.m_list_accounts.Total();
   if(total==0)
      return WRONG_VALUE;
   for(int i=0;i<total;i++)
     {
      CAccount* account=this.m_list_accounts.At(i);
      if(account==NULL)
         continue;
      if(account.Login()==::AccountInfoInteger(ACCOUNT_LOGIN)    &&
         account.Company()==::AccountInfoString(ACCOUNT_COMPANY) &&
         account.Name()==::AccountInfoString(ACCOUNT_NAME)         
        ) return i;                                                
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

口座オブジェクトリスト(ループ内)を使用してオブジェクトを獲得し、口座データ(ログイン、クライアント名、会社名)をプログラムが起動されている口座データと比較します。一致した場合は、ループインデックスが返されます。ループの完了時に、現在の口座データを持つオブジェクトが見つかっていない場合には-1が返されます

次のメソッドがpublicクラスセクションに追加されます。

現在の口座のデータを持つ口座オブジェクトのインデックスを格納する変数の値を返すメソッド口座プロパティが変更されたかどうかのフラグを返すメソッド。また、クラスデストラクタ(リスト内のすべての口座をファイルに保存する)、口座オブジェクトをコレクションリストに追加するメソッドオブジェクトをファイルに保存したりファイルへ/からアップロード/ダウンロードしたりするメソッド現在の口座オブジェクトの現在の口座データを更新するメソッドもあります。

public:
//--- Return the full account collection list "as is"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_accounts;                                         }
//--- 比較された基準を満たす選択された(1)整数、(2)実数、(3)文字列プロパティののリストを返す
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
//--- Return the (1) current object account index, (2) flag of an occurred event in the account data
   int               IndexCurrentAccount(void)                                                        const { return this.m_index_current;                                          }
   bool              IsAccountEvent(void)                                                             const { return this.m_is_account_event;                                       }

//--- Constructor, destructor
                     CAccountsCollection();
                    ~CAccountsCollection();
//--- Add the account object to the list
   bool              AddToList(CAccount* account);
//--- (1) Save account objects from the list to the files
//--- (2) Save account objects from the files to the list
   bool              SaveObjects(void);
   bool              LoadObjects(void);
//--- Update the current account data
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

これらのメソッドについて考えてみましょう。

ライブラリファイルはFiles\DoEasy\ターミナルフォルダに保存されます。このフォルダ―には各クラスのフォルダがあります(クラスがファイルを保存する必要がある場合)。口座オブジェクトを格納するフォルダの名前を設定するためのm_folder_nameクラスメンバ変数もあって、口座プロパティの変更の発生をフラグする変数とともに、クラスコンストラクタの初期化リストですぐに初期化されます。

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CAccountsCollection::CAccountsCollection(void) : m_folder_name(DIRECTORY+"Accounts"),m_is_account_event(false)
  {
   this.m_list_accounts.Clear();
   this.m_list_accounts.Sort(SORT_BY_ACCOUNT_LOGIN);
   this.m_list_accounts.Type(COLLECTION_ACCOUNT_ID);
   ::ZeroMemory(this.m_struct_prev_account);

//--- Create the folder for storing account files
   ::ResetLastError();
   if(!::FolderCreate(this.m_folder_name,FILE_COMMON))
      Print(DFUN,TextByLanguage("Не удалось создать папку хранения файлов. Ошибка ","Could not create file storage folder. Error "),::GetLastError());
//--- Create the current account object and add it to the list
   CAccount* account=new CAccount();
   if(account!=NULL)
     {
      if(!this.AddToList(account))
        {
         Print(DFUN_ERR_LINE,TextByLanguage("Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию.","Error. Failed to add current account object to collection list."));
         delete account;
        }
      else
         account.PrintShort();
     }
   else
      Print(DFUN,TextByLanguage("Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта.","Error. Failed to create an account object with current account data."));

//--- Download account objects from the files to the collection
   this.LoadObjects();
//--- Save the current account index
   this.m_index_current=this.Index();
  }
//+------------------------------------------------------------------+

次に、クラスコンストラクタで、現在の口座の以前のデータの構造体をリセットし、クラスファイルを保存するためのフォルダを作成します。これは、クラスの「Common_data_folder」\Files\DoEasy\Accountsにあります。
次に、現在の口座データを含む口座オブジェクトが作成され、AddToList()メソッドを使用して口座コレクションリストに追加されます。リストにオブジェクトが追加されなかった場合、適切なメッセージが操作ログに送信されます。追加された場合は、簡単な口座プロパティ(ログイン、クライアント名、会社名、口座残高、レバレッジ、ネッティングでない場合は口座タイプ)を含むメッセージが表示されます。
次のステップは、口座オブジェクトをコレクションリストにアップロードすることです。これらは、クラスオブジェクトを保存するフォルダに保存ファイルが存在する口座オブジェクトです
最後のステップは、プログラムで使用するために、現在の口座データでオブジェクトインデックスを探し、IndexCurrentAccount()メソッドによって値が返される変数にm_index_currentを割り当てることです。

コレクションリストのすべてのオブジェクトを適切なファイルに保存するメソッドは、クラスデストラクタで呼び出されます。

//+------------------------------------------------------------------+
//| デストラクタ                                                      |
//+------------------------------------------------------------------+
CAccountsCollection::~CAccountsCollection(void)
  {
//--- Save account objects from the list to the files
   this.SaveObjects();
  }
//+------------------------------------------------------------------+

以下は、口座オブジェクトをコレクションリストに追加するメソッドです。

//+------------------------------------------------------------------+
//| Add the account object to the list                               |
//+------------------------------------------------------------------+
bool CAccountsCollection::AddToList(CAccount *account)
  {
   if(account==NULL)
      return false;
   if(!this.IsPresent(account))
      return this.m_list_accounts.Add(account);
   return false;
  }
//+------------------------------------------------------------------+

このメソッドは口座オブジェクトへのポインタを受け取り、次にIsPresent()メソッドを使用してコレクションリスト内のそのようなオブジェクトの存在を確認します。そのようなオブジェクトがまだない場合は、コレクションリストに追加され、その追加の結果が返されます

以下は、コレクションリストから口座オブジェクトをファイルに保存するメソッドです。

//+------------------------------------------------------------------+
//| Save account objects from the list to the files                  |
//+------------------------------------------------------------------+
bool CAccountsCollection::SaveObjects(void)
  {
   bool res=true;
   int total=this.m_list_accounts.Total();
   if(total==0)
      return false;
   for(int i=0;i<total;i++)
     {
      CAccount* account=this.m_list_accounts.At(i);
      if(account==NULL)
         continue;
      string file_name=this.m_folder_name+"\\"+account.Server()+" "+(string)account.Login()+".bin";
      if(::FileIsExist(file_name,FILE_COMMON))
         ::FileDelete(file_name,FILE_COMMON); 
      ::ResetLastError();
      int handle=::FileOpen(file_name,FILE_WRITE|FILE_BIN|FILE_COMMON);
      if(handle==INVALID_HANDLE)
        {
         ::Print(DFUN,TextByLanguage("Не удалось открыть для записи файл ","Could not open file for writing: "),file_name,TextByLanguage(". Ошибка ",". Error "),(string)::GetLastError());
         return false;
        }
      res &=account.Save(handle);
      ::FileClose(handle);
     }
   return res;
  }
//+------------------------------------------------------------------+

コレクションリストを(ループで)使用してリストから口座オブジェクトを取得し、口座オブジェクトフォルダへのパス、サーバ名、「.bin」拡張子で構成されるファイル名を作成します。そのようなファイルが口座オブジェクトフォルダに存在する場合、そのファイルは削除され書き込み用に新しいファイルが開かれます開かれたファイルハンドルは、先に説明したCAccountクラスのSave()仮想メソッドに渡されファイルの保存結果は、コレクションリストからすべての口座オブジェクトのファイルへの書き込み結果を返すres変数に追加されます。オブジェクトが保存されると、書き込みのために開かれたファイルは閉じられます

以下は、口座オブジェクトをファイルからコレクションリストにダウンロードするメソッドです。

//+------------------------------------------------------------------+
//| Download account objects from the files to the list              |
//+------------------------------------------------------------------+
bool CAccountsCollection::LoadObjects(void)
  {
   bool res=true;
   string name="";
   long handle_search=::FileFindFirst(this.m_folder_name+"\\*",name,FILE_COMMON);
   if(handle_search!=INVALID_HANDLE)
     {
      do
        {
         string file_name=this.m_folder_name+"\\"+name;
         ::ResetLastError();
         int handle_file=::FileOpen(m_folder_name+"\\"+name,FILE_BIN|FILE_READ|FILE_COMMON);
         if(handle_file!=INVALID_HANDLE)
           {
            CAccount* account=new CAccount();
            if(account!=NULL)
              {
               if(!account.Load(handle_file))
                 {
                  delete account;
                  ::FileClose(handle_file);
                  res &=false;
                  continue;
                 }
               if(this.IsPresent(account))
                 {
                  delete account;
                  ::FileClose(handle_file);
                  res &=false;
                  continue;
                 }
               if(!this.AddToList(account))
                 {
                  delete account;
                  res &=false;
                 }
              }
           }
         ::FileClose(handle_file);
        }
      while(::FileFindNext(handle_search,name));
      ::FileFindClose(handle_search);
     }
   return res;
  }
//+------------------------------------------------------------------+

まず、ライブラリ口座オブジェクトファイルが格納されているフォルダで最初のファイルを見つけます 。次に、do-whileループで読み取るために検出された別のファイルを開き新しい口座オブジェクトを作成CAccountクラスのLoad()仮想メソッドを使用してファイルからデータをアップロードします。そのような(同じ口座データを持つ)オブジェクトがない場合は、オブジェクトがリストに追加されますファイルからオブジェクトにデータをアップロードするとき、またはリストにオブジェクトを追加するときにエラーが発生した場合は、この新しいオブジェクトを削除して(メモリリークを防ぐため)、ファイルを閉じます
ループが完了したら、ファイルから口座オブジェクトにデータをアップロードし、コレクションリストに配置した結果が返されます。

以下は、現在の口座オブジェクトデータを更新するメソッドです。

//+------------------------------------------------------------------+
//| Update the current account data                                  |
//+------------------------------------------------------------------+
void CAccountsCollection::Refresh(void)
  {
   if(this.m_index_current==WRONG_VALUE)
      return;
   CAccount* account=this.m_list_accounts.At(this.m_index_current);
   if(account==NULL)
      return;
   ::ZeroMemory(this.m_struct_curr_account);
   this.m_is_account_event=false;
   this.SetAccountsParams(account);
   
//--- 初回実行
   if(!this.m_struct_prev_account.login)
     {
      this.SavePrevValues();
     }
//--- If the account hash changed
   if(this.m_struct_curr_account.hash_sum!=this.m_struct_prev_account.hash_sum)
     {
      this.m_is_account_event=true;
      this.SavePrevValues();
     }
  }
//+------------------------------------------------------------------+

ここで最初に行うことは、現在の口座データを含む口座オブジェクトのインデックスの有効性を確認することです。何らかの理由で取得できない場合は、メソッドを終了します。次に、リスト内のインデックスによって現在の口座データを持つ口座オブジェクトを取得して、現在の口座データ構造体および口座オブジェクトのプロパティ変更フラグをリセットして、口座オブジェクトプロパティを設定するメソッドを呼び出します。同じメソッドで、口座の以前のステータスとのその後の比較および変更の検出のために、最新の(新しく読み取られた)プロパティを現在の口座データ構造にコピーします。
次に、口座の前のステータスを表すデータ構造に設定するデータを定義します。ログインフィールドがゼロの場合、これは構造体が一度も書き入れられたことがなく、これが最初の起動であることを意味します。したがって、単に前のステータスデータを持つ構造体に現在のステータス構造体からのデータを入力します
次に、現在のステータスハッシュと以前のステータスハッシュを比較して、ハッシュの変更を確認します。変更がある場合は、発生した口座プロパティ変更イベントのフラグを設定し、現在のステータスを後続の比較のために前のステータスとして保存します
後で、重要な口座ステータスの変更の追跡を実装し、この重要な変更に関するイベントメッセージをプログラムに送信します。

クラスの動作全体はライブラリの基本オブジェクト(CEngineクラス)から実行されるため、Engine.mqhファイルに移動して必要な機能を追加します。

まず、口座コレクションファイルをインクルードします

//+------------------------------------------------------------------+
//|                                                       Engine.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 "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Services\TimerCounter.mqh"
//+------------------------------------------------------------------+

クラスのprivateセクションで、口座コレクションオブジェクトを作成し、口座コレクションを使用するためのメソッドを追加します

//+------------------------------------------------------------------+
//| ライブラリ基本クラス                                               |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // 過去の注文と取引のコレクション
   CMarketCollection    m_market;                        // 注文と取引のコレクション
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CArrayObj            m_list_counters;                 // タイマーカウンタのリスト
   bool                 m_first_start;                   // 初期実行フラグ
   bool                 m_is_hedge;                      // ヘッジ勘定フラグ
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // 口座取引イベントフラグ
   bool                 m_is_history_trade_event;        // 口座過去の取引イベントフラグ
   ENUM_TRADE_EVENT     m_last_trade_event;              // Account last trading event
//--- IDによるカウンタインデックスを返す
   int                  CounterIndex(const int id) const;
//--- Return (1) the first launch flag, (2) the flag presence in a trading event
   bool                 IsFirstStart(void);
//--- Working with (1) order, deal and position, as well as (2) account events
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- 直近の(1)市場未決注文、(2)成行注文、(3)直近のポジション、(4)チケット別ポジションを返す
   COrder*              GetLastMarketPending(void);
   COrder*              GetLastMarketOrder(void);
   COrder*              GetLastPosition(void);
   COrder*              GetPosition(const ulong ticket);
//--- Return the last (1) removed pending order, (2) historical market order, (3) historical order (market or pending) by its ticket
   COrder*              GetLastHistoryPending(void);
   COrder*              GetLastHistoryOrder(void);
   COrder*              GetHistoryOrder(const ulong ticket);
//--- (1)すべてのポジション注文のリストからの最初および(2)直近の成行注文、(3)直近の取引を返す
   COrder*              GetFirstOrderPosition(const ulong position_id);
   COrder*              GetLastOrderPosition(const ulong position_id);
   COrder*              GetLastDeal(void);
public:

クラスコンストラクタで、口座コレクションを操作するための新しいタイマーカウンタを作成します。

//+------------------------------------------------------------------+
//| CEngineコンストラクタ                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT)
  {
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
        }
   #endif 
  }
//+------------------------------------------------------------------+

タイマーとそのカウンターについては第3部でで説明しました。

OnTimer()クラスハンドラで、口座コレクションタイマーを追加します。

//+------------------------------------------------------------------+
//| CEngineタイマー                                                   |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer of historical orders, deals, market orders and positions collections
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the order, deal and position collections events
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.TradeEventsControl();
        }
     }
//--- Account collection timer
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the account collections
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.AccountEventsControl();
        }
     }
  }
//+------------------------------------------------------------------+

口座コレクションタイマーは、第3部のライブラリの基本オブジェクトであるCEngineクラスの開発についてのセクションにある注文、取引、ポジションコレクションタイマーと似ています。注文、取引、ポジションコレクションタイマーとの唯一の違いは、別のコレクションイベント処理メソッド(AccountEventsControl()メソッド)が呼び出されることです。

現在の口座プロパティの変更を確認するメソッドを追加しましょう。

//+------------------------------------------------------------------+
//| Check the account events                                         |
//+------------------------------------------------------------------+
void CEngine::AccountEventsControl(void)
  {
   this.m_accounts.Refresh();
  }
//+------------------------------------------------------------------+

このメソッドは、CAccountsCollectionクラスのRefresh()メソッドを呼び出すだけです。

CEngineクラスのpublicセクションで、イベントおよび口座コレクションリストをプログラムに返す2つのメソッドを記述します。これにより、プログラムからコレクションリストに直接アクセスできるようになります。

public:
   //--- 市場の(1)ポジション、(2)未決注文、(3)成行注文のリストを返す
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- 過去の(1)注文、(2)削除済み未決注文、(3)取引、(4)ID別の成行注文のポジションリストを返す
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Return the account list
   CArrayObj*           GetListAllAccounts(void)                        { return this.m_accounts.GetList();    }
//--- Return the event list 
   CArrayObj*           GetListAllEvents(void)                          { return this.m_events.GetList();      }
//--- 直近の取引イベントをリセットする
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Return the (1) last trading event, (2) hedge account flag, (3) flag of working in the tester
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;      }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
   bool                 IsTester(void)                            const { return this.m_is_tester;             }
//--- タイマーカウンタを作成する
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- タイマー
   void                 OnTimer(void);
//--- コンストラクタ/デストラクタ
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

口座コレクションクラスをテストする準備がすべて整いました。しかし、テストを開始する前に、CEventsCollection::Refreshイベントコレクションクラスを変更しましょう。233行目に確認を追加し、新しいイベントとともに古いイベントをプログラムに送信するイベントを定義する際に不定期のアクティベーションを排除します。

//--- If the event is in the account history
   if(is_history_event)
     {
      //--- If the number of historical orders increased (MQL5, MQL4)
      if(new_history_orders>0 && new_market_pendings<0)
        {
         //--- Receive the list of removed pending orders only

また、MetaTrader 4で作業するためにMQL4の取引関数を作成する際に作成されたかなり馬鹿げたエラーも修正しました(DELib.mqhファイル)。問題は、MQL4ではOrderSend()関数がブール値ではなく注文チケットを返すことです。どうやら、私はMQL4を忘れ始めているようです(笑)。

次の例を考えてみましょう。
MQL4関数の動作結果の確認が次のようになっていました(これはMQL5では正しいです)。

if( ! OrderSend(sym,ORDER_TYPE_BUY,volume,price,deviation,sl,tp,comment,(int)magic,0,clrBlue))

MQL4で正しくなるようにエラーを修正しました

if(OrderSend(sym,ORDER_TYPE_BUY,volume,price,deviation,sl,tp,comment,(int)magic,0,clrBlue)==WRONG_VALUE)

テスターにとって大したことではありませんが、エラーには違いありません。
もうすぐ本格的な取引クラスが紹介され、これらの関数はライブラリから削除されます。

口座コレクションのテスト

既に開発したTestDoEasyPart12_1.mq5 EAを使用しましょう。同じフォルダ\MQL5\Experts\TestDoEasy内にTestDoEasyPart12_2.mq5という名前で保存してください。

EAの入力に、操作ログに表示される既存口座データの外観を切り替える変数を導入します。これらは、brief(デフォルトはfalse)とfull(デフォルトはtrue)です。

//--- 入力変数
input ulong    InpMagic             =  123;  // Magic number
input double   InpLots              =  0.1;  // Lots
input uint     InpStopLoss          =  50;   // StopLoss in points
input uint     InpTakeProfit        =  50;   // TakeProfit in points
input uint     InpDistance          =  50;   // Pending orders distance (points)
input uint     InpDistanceSL        =  50;   // StopLimit orders distance (points)
input uint     InpSlippage          =  0;    // Slippage in points
input double   InpWithdrawal        =  10;   // Withdrawal funds (in tester)
input uint     InpButtShiftX        =  40;   // Buttons X shift 
input uint     InpButtShiftY        =  10;   // Buttons Y shift 
input uint     InpTrailingStop      =  50;   // Trailing Stop (points)
input uint     InpTrailingStep      =  20;   // Trailing Step (points)
input uint     InpTrailingStart     =  0;    // Trailing Start (points)
input uint     InpStopLossModify    =  20;   // StopLoss for modification (points)
input uint     InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
input bool     InpFullProperties    =  false;// Show full accounts properties
//--- グローバル変数

OnInit()ハンドラに以下のコードを追加します。

//+------------------------------------------------------------------+
//| エキスパート初期化関数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;

//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Fast check of the account object
   CArrayObj* list=engine.GetListAllAccounts();
   if(list!=NULL)
     {
      int total=list.Total();
      if(total>0)
         Print("\n",TextByLanguage("=========== Список сохранённых аккаунтов ===========","=========== List of saved accounts ==========="));
      for(int i=0;i<total;i++)
        {
         CAccount* account=list.At(i);
         if(account==NULL)
            continue;
         Sleep(100);
         if(InpFullProperties)
            account.Print();
         else
            account.PrintShort();
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

ここでは、CEngineクラスのGetListAllAccounts()メソッドを使用して口座コレクションリストを取得します。ループで後続の各オブジェクトを取得して、入力値に応じて(簡単なエントリ、または口座オブジェクトプロパティの完全なリスト)操作ログにそのプロパティを表示します。

EAを起動し、簡単なエントリが選択されたときに操作ログに表示される内容を確認します(すべての口座プロパティの表示= false)。


次に完全なリストを選択します。F7キーを押して、パラメーターウィンドウで[Show full accounts properties]を[true]に設定します。


これで、既存の各口座のプロパティの完全なリストが操作ログに表示されます。

口座ファイルに書き込むには、最初の口座に接続し、2番目の口座に再接続してから、3番目の口座に移動する必要があることに注意してください。つまり、新しい口座に接続するごとに、前の口座のデータがファイルに書き込まれます。

次の段階

次の記事では、口座プロパティを変更する重要なイベントを追跡し、銘柄オブジェクトとそのコレクションの作業を開始します。

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

目次に戻る

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

第1部: 概念、データ管理
第2部: 過去の注文と取引のコレクション
第3部:注文と取引のコレクション、検索と並び替え
第4部: 取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部: ネッティング勘定イベント
第7部: StopLimit注文発動イベント、注文およびポジション変更イベントの機能の準備
第8部: 注文とポジションの変更イベント。第9部:MQL4との互換性 - データの準備
第10部:MQL4との互換性 - ポジションオープンイベントと指値注文発動イベント
第11部:MQL4との互換性 - ポジション決済イベント