MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第12部): 「口座」オブジェクトクラスと口座オブジェクトのコレクション
内容
ライブラリの取引クラスを開発する前に、追加の取引関連クラスを作成する必要があります。具体的には、取引口座と取引銘柄のデータが必要です。本稿では、口座オブジェクトに専念します。
口座データは取引中に変更される可能性があるため、口座オブジェクトを準備してから口座オブジェクトの収集を行います。次に、口座追跡イベントを実装して、レバレッジ、残高、利益/損失、エクイティ、口座の制限の変化を検出できるようにします。
口座オブジェクト
取引口座のボリュームは、以前に作成され過去の記事で説明したオブジェクトと同じです。このオブジェクトと以前に検討されたオブジェクトとの唯一の違いは、これは、後続オブジェクトが特定のオブジェクトステータスを指定する抽象オブジェクトではないということです。口座オブジェクトは、すべての口座プロパティを備えた独立したオブジェクトです。このようなオブジェクトは口座オブジェクトコレクションに追加されるため、さまざまなパラメータで異なる口座のデータを比較できるようになります。
通常どおり、クラスの操作に必要な口座オブジェクトプロパティのすべての列挙の開発から始めます。
エディターのヘルプで口座プロパティを開きます。
ID |
説明 |
Property type |
ACCOUNT_LOGIN |
Account number |
long |
ACCOUNT_TRADE_MODE |
Trading account type |
|
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 |
|
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 |
|
ACCOUNT_CURRENCY_DIGITS |
Number of digits for an account currency necessary for accurate display of trading results |
int |
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 |
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ファイルで、上記に表示された口座プロパティの表に対応する口座オブジェクトのinteger、real、stringプロパティを追加します。
口座イベントを使用するための列挙は既に作成されているため、以前に作成した口座イベントを使用するためのデータの前に口座プロパティデータを配置するのが妥当です。
//+------------------------------------------------------------------+ //| 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\Part12でTestDoEasyPart12_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; } //+------------------------------------------------------------------+
ここで
- 書き込み用にすでに開かれているファイルのハンドルがメソッドに渡されます
- すべてのオブジェクトフィールドをPOD構造に保存します
- メソッドがハンドルを受け取ったファイルにPOD構造を書き込みます
以下は、ファイルからオブジェクトデータをダウンロードするメソッドです。
//+------------------------------------------------------------------+ //| 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.mqhでtimerパラメータでマクロ置換を作成して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との互換性 - ポジション決済イベント
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/6952
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索