Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XIII): Objektklasse "Account" und die Kollektion von Konto-Objekten
Artyom Trishkin | 30 August, 2019
Inhalt
- Das Konto-Objekt
- Testen des Konto-Objekts
- Die Kollektion von Konto-Objekten
- Testen der Kollektion von Konten
- Was kommt als Nächstes?
Bevor wir die Bibliothek der Handelsklassen entwickeln, müssen wir einige zusätzliche handelsbezogene Klassen schaffen. Genauer gesagt, benötigen wir Daten über ein Handelskonto und die gehandelten Symbole. Dieser Artikel widmet sich dem Konto-Objekt.
Da sich die Kontodaten während des Handels ändern können, werden wir ein Konto-Objekt vorbereiten, gefolgt von der Kollektion der Konto-Objekte. Als Nächstes werden wir Tracking-Ereignisse auf dem Konto implementieren. Dies wird es uns ermöglichen, Veränderungen bei Leverage, Saldo, Gewinn/Verlust, Eigenkapital und Kontobeschränkungen zu erkennen.
Das Konto-Objekt
Ein Volumen des Handelskontos ist identisch mit zuvor angelegten Objekten, die ich in den vergangenen Artikeln beschrieben habe. Der einzige Unterschied zwischen diesem Objekt und den zuvor betrachteten besteht darin, dass es sich nicht um eine Art abstraktes Objekt handelt, bei dem Nachfolger einen bestimmten Objektstatus angeben. Das Konto-Objekt ist ein unabhängiges Objekt, das alle Eigenschaften des Kontos enthält. Solche Objekte sollen in die Kollektion der Konto-Objekte aufgenommen werden, so dass Benutzer Daten verschiedener Konten anhand verschiedener Parameter vergleichen können.
Wie üblich beginnen wir mit der Entwicklung aller Enumerationen der Eigenschaften des Konto-Objekts, die für die Arbeit mit der Klasse
notwendig sind.
Öffnen Sie die Konto-Eigenschaften
in der Editorhilfe:
Für die Funktion AccountInfoInteger()
ID |
Beschreibung |
Typ der Eigenschaften |
ACCOUNT_LOGIN |
Kontonummer |
long |
ACCOUNT_TRADE_MODE |
Typ des Handelskontos |
|
ACCOUNT_LEVERAGE |
Leverage (Hebel) |
long |
ACCOUNT_LIMIT_ORDERS |
Maximalzahl erlaubter aktiver Pending-Orders |
int |
ACCOUNT_MARGIN_SO_MODE |
Modus der Bestimmung die minimal verfügbare Margenhöhe |
|
ACCOUNT_TRADE_ALLOWED |
Handelserlaubnis des aktuellen Kontos |
bool |
ACCOUNT_TRADE_EXPERT |
Handelserlaubnis eines EAs |
bool |
ACCOUNT_MARGIN_MODE |
Modus der Margenberechnung |
|
ACCOUNT_CURRENCY_DIGITS |
Anzahl der Stellen für eine Kontowährung, die für die genaue Darstellung der Handelsergebnisse erforderlich sind |
int |
Für die Funktion AccountInfoDouble()
ID |
Beschreibung |
Typ der Eigenschaften |
ACCOUNT_BALANCE |
Kontosaldo in der Kontowährung |
double |
ACCOUNT_CREDIT |
Gutschrift in der Kontowährung |
double |
ACCOUNT_PROFIT |
Laufender Gewinn auf einem Konto in Kontowährung |
double |
ACCOUNT_EQUITY |
Kapital des Kontos in der Kontowährung |
double |
ACCOUNT_MARGIN |
Reservierte Marge auf einem Konto in der Kontowährung |
double |
ACCOUNT_MARGIN_FREE |
Frei verfügbare Geldmittel in der Kontowährung, um eine Position auf einem Konto zu eröffnen. |
double |
ACCOUNT_MARGIN_LEVEL |
Höhe der Marge auf einem Konto in % |
double |
ACCOUNT_MARGIN_SO_CALL |
Margenhöhe, ab der eine Einzahlung auf ein Konto erforderlich ist (Margin Call). Je nach ACCOUNT_MARGIN_SO_MODE wird die Eigenschaft entweder in % oder in der Kontowährung festgelegt. |
double |
ACCOUNT_MARGIN_SO_SO |
Margenhöhe, ab der die verlustreichste Position geschlossen wird (Stop Out). Je nach ACCOUNT_MARGIN_SO_MODE wird die Eigenschaft entweder in % oder in der Kontowährung festgelegt. |
double |
ACCOUNT_MARGIN_INITIAL |
Auf einem Konto reservierte Mittel, um einen Garantiebetrag für alle Pending-Order zu gewährleisten. |
double |
ACCOUNT_MARGIN_MAINTENANCE |
Auf einem Konto reservierte Gelder, um einen Mindestbetrag für alle offenen Positionen zu gewährleisten. |
double |
ACCOUNT_ASSETS |
Aktuelles Vermögen auf einem Konto |
double |
ACCOUNT_LIABILITIES |
Laufende Verbindlichkeiten auf einem Konto |
double |
ACCOUNT_COMMISSION_BLOCKED |
Aktuelle Summe der blockierten Kommissionen auf einem Konto |
double |
Für die Funktion AccountInfoString()
ID |
Beschreibung |
Typ der Eigenschaften |
ACCOUNT_NAME |
Name des Kontoinhabers |
string |
ACCOUNT_SERVER |
Name des Handelsservers |
string |
ACCOUNT_CURRENCY |
Kontowährung |
string |
ACCOUNT_COMPANY |
Name des kontoführenden Instituts |
string |
Das Konto-Objekt wird alle diese Eigenschaften enthalten, die im Klassenkonstruktor eingetragen werden sollen.
Fügen Sie in der Bibliotheksdatei Defines.mqh die Eigenschaften integer,
double und string
des Konto-Objekts hinzu, die den oben angezeigten Tabellen der Kontoeigenschaften entsprechen.
Da wir die
Enumeration für die Arbeit mit Kontoereignissen bereits erstellt haben, wäre es sinnvoll, Kontoeigentumsdaten vor die zuvor
erstellten
Daten für die Arbeit mit Kontoereignissen zu stellen:
//+------------------------------------------------------------------+ //| 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 | //+------------------------------------------------------------------+
Hier ist uns alles aus den vorangegangenen Artikeln bekannt, so dass es keinen Sinn macht, über die Anordnung der Enumerationen
und das Setzen unbenutzter Objekteigenschaften und Makro-Substitutionen zu verweilen, um die Anzahl der Eigenschaften anzugeben,
die für eine genaue Berechnung einer Adresse der ersten Enumerationskonstante für den nächsten
Typ von Objekteigenschaften übergeben wurden. Alles, was in den vorherigen Artikeln beschrieben wurde, nämlich
im Abschnitt "
Implementierung der Ereignisbehandlung auf einem Netting-Konto"
des sechsten Teils der Bibliotheksbeschreibung.
Das Einzige, worauf wir hier näher eingehen können, ist "Margenberechnungsmodus":
Da es in MQL4 keine Enumeration von ENUM_ACCOUNT_MARGIN_MODE gibt, müssen wir sie für die Kompilierung in MQL4 angeben. Fügen wir es an
das Ende der Datei
ToMQL4.mqh hinzu:
//+------------------------------------------------------------------+ //| Margin calculation mode | //+------------------------------------------------------------------+ enum ENUM_ACCOUNT_MARGIN_MODE { ACCOUNT_MARGIN_MODE_RETAIL_NETTING, ACCOUNT_MARGIN_MODE_EXCHANGE, ACCOUNT_MARGIN_MODE_RETAIL_HEDGING }; //+------------------------------------------------------------------+
Da nun alle Daten vorbereitet sind, kann ein Konto-Objekt angelegt werden.
Erstellen Sie im Bibliotheksordner \MQL5\Include\DoEasy\Objects\ den Unterordner Accounts und stellen Sie
die neue Klasse
CAccount in der Datei Account.mqh vor.
Fügen Sie in der neu erstellten Klassendatei die Deklarationen aller notwendigen Methoden hinzu.
Die
meisten dieser Methoden sind bereits "Standard" für die Bibliotheksobjekte. Es gibt jedoch einen kleinen Vorbehalt: Da diese Klasse
nicht die Existenz von Abkömmlingen impliziert, gibt es keinen 'protected' Klassenkonstruktor, der den Status eines Objekts akzeptiert
und setzt. Daher verfügt das Konto-Objekt über keine Eigenschaft "status", und sein
Konstruktor akzeptiert keine Argumente. Gleichzeitig werden wir die virtuellen
Methoden lassen, die die Flags des Objekts zurückgeben, das eine bestimmte Eigenschaft unterstützt für mögliche zukünftige
Nachkommen der Klasse:
.
//+------------------------------------------------------------------+ //| Account.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #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: //--- Konstructor 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); //--- }; //+------------------------------------------------------------------+
Schreiben Sie den Klassenkonstruktor außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CAccount::CAccount(void) { //--- Sichern der ganzzahligen Eigenschaften 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 ; //--- Sichern der Double-Eigenschaften 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); //--- Sichern der String-Eigenschaften 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); } //+-------------------------------------------------------------------+
Hier ist alles klar: Jeder Objekteigenschaft wird über die Funktion AccountInfo
die entsprechende Account-Eigenschaft zugeordnet.
Für die beiden Eigenschaften, die in MQL4 nicht vorhanden
sind, erfolgt die Auswahl während der bedingten Kompilierungsanweisungen: wir erhalten die entsprechenden Eigenschaften für den
"Margenberechnungsmodus" und die Eigenschaft "Anzahl der Dezimalstellen für die Kontowährung" für MQL5,
während für
MQL4 Sie einfach ACCOUNT_MARGIN_MODE_RETAIL_HEDGING
(Hedge Account) aus der Enumeration ENUM_ACCOUNT_MARGIN_MODE
für die erste Eigenschaft angeben und zwei Dezimalstellen für
die zweite Eigenschaft zurück.
Lassen Sie uns die Methode zum Suchen und Sortieren von Konto-Objekten in ihrer Liste der Kollektionen implementieren.
Die
Methode ist identisch mit den in den Bibliotheksobjekten
zuvor beschriebenen. Deshalb werde ich hier nur die Auflistung anführen:
//+-------------------------------------------------------------------+ //|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; } //+------------------------------------------------------------------+
Um zwei Konto-Objekte zu vergleichen, müssen wir deren unänderliche Eigenschaften vergleichen, um festzustellen, ob diese Objekte zu verschiedenen Konten gehören. Kontonummer (Login), Benutzername und Firmenname werden zur genauen Identifizierung angegeben. Dies sind die Eigenschaften der beiden verglichenen Konten, die wir in der Vergleichsmethode der Konto-Objekte überprüfen werden:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Der Zeiger auf ein verglichenes Objekt wird an die Methode
übergeben und die drei Eigenschaften der beiden Objekte (
Firmenname, Kontonummer
und Kundenname) werden überprüft. Wenn eine der Objekteigenschaften
nicht gleich ist, gehören sie zu verschiedenen Konten —
Rückgabe von false.
Nachdem alle drei Vergleiche erfolgreich bestanden wurden,
wird true
zurück gegeben — die Objekte sind gleich.
Andere Klassenmethoden sind "Service-Methoden" und müssen nicht kontrolliert werden, da ihre Listen alle notwendigen Informationen enthalten. Außerdem haben wir ähnliche Methoden in den vorherigen Teilen der Bibliotheksbeschreibung analysiert:
//+------------------------------------------------------------------+ //| 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") ); } //+------------------------------------------------------------------+
Die vollständige Auflistung der Kontoklasse finden Sie in den an den Artikel angehängten Dateien. Testen wir die Klasse.
Testen des Konto-Objekts
Um zu überprüfen, ob die Klasse die Kontodaten korrekt erhält, wird vorübergehend die Klassendatei zum Hauptobjekt der Bibliothek inkludiert — die Klasse CEngine:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Services\TimerCounter.mqh" #include "Objects\Accounts\Account.mqh" //+------------------------------------------------------------------+ //| Bibliothek der Basisklasse | //+------------------------------------------------------------------+ class CEngine : public CObject {
Nach dem Einbinden der Objektklassendatei des Kontos ist das Programm in der Lage, das Objekt zu sehen.
Zum Testen der Klasse nehmen Sie den EA aus dem vorherigen Artikel — \MQL5\Experts\TestDoEasy\Part11\TestDoEasyPart11.mq5 und speichern Sie es unter dem Namen TestDoEasyPart12_1.mq5 im Ordner \MQL5\Experts\TestDoEasy\Part12.
Um das Konto-Objekt einzubinden und zu testen, fügen Sie folgende Zeilen der Funktion OnInit() des EA hinzu (die Prüfung ist während der Initialisierung durchzuführen):
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+
Hier erstellen Sie das Konto-Objekt und es werden Erfolg verkürzt die Kontodaten angezeigt und die vollständige Liste der Kontoparameter anschließend im Journal ausgedruckt. Entfernen Sie das Konto-Objekt nach Beendigung.
Starten Sie den EA auf dem Chart mit einem beliebigen Symbol und sehen Sie sich die Expertenprotokolle an:
Alle Kontodaten werden korrekt angezeigt.
Die Kollektion von Konto-Objekten
Da beim Ändern eines Kontos alle EAs neu initialisiert werden, werden zuerst die Destruktoren aufgerufen, gefolgt von
Klassenkonstruktoren. Dadurch verliert der EA vor dem Ändern des Kontos das vorher vorhandene Konto-Objekt. Um die Kollektion des
Kontos aufrechtzuerhalten, müssen wir uns die Daten der vorherigen Konten merken, mit dem das Terminal verbunden ist. Speichern Sie
dazu die Daten des aktuellen Konto-Objekts in der Datei im Destruktor der Klasse der Kollektion der Konten und laden Sie Daten aus den
Dateien in den Klassenkonstruktor. Somit wird die Kollektion der Konten mit Daten über alle Konten gefüllt, an die sich das Terminal
während des Programmbetriebs auf Basis der Bibliothek angeschlossen hat. Da die Dateien im gemeinsamen Ordner aller
Client-Terminals gespeichert sind, sieht jedes gestartete Terminal alle Konten, mit denen das Terminal auf dem angeschlossenen PC
arbeitet, vorausgesetzt, dass das bibliotheksbasierte Programm in diesem Terminal arbeitet.
Die neue Klasse der Kollektion der Konten wird die Möglichkeit haben, Daten über alle bestehenden Konten anhand ihrer
verschiedenen Parameter zu vergleichen.
Um das Konto-Objekt in der Datei zu speichern, müssen wir die Methode zum Speichern von Daten in einer Datei in der Klasse CAccount
erstellen.
Alle erstellten Objekte werden von CObject
übernommen — dem Basisobjekt der
Standardbibliothek. Die Klasse verfügt auch über die virtuellen
Methoden zum Speichern und Laden eines Objekts in eine Datei:
//+------------------------------------------------------------------+ //| 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); } //--- Methode zur Identifizierung des Objekts 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); } }; //+------------------------------------------------------------------+
Die Methoden tun nichts, und wir müssen sie in unseren abgeleiteten Klassen neu definieren, wo sie gebraucht werden (Klasse CAccount).
Um alle Eigenschaften des Konto-Objekts in der Datei zu speichern, verwenden wir eine einfache Struktur und speichern sie in der Datei. Die Objektfelder enthalten jedoch Zeilen, d.h. es handelt sich nicht um eine POD-Struktur. Daher müssen wir alle Zeichenketteneigenschaften des Objekts konvertieren, wenn wir sie in den Arrays uchar der Strukturfelder mit der konstanten Größe speichern. In diesem Fall können wir alle Daten über die Eigenschaften des Account Objekts als Struktur mit der Funktion FileWriteArray() in der Datei speichern.
Um das Verzeichnis für die Speicherung der Bibliotheksdateien und die konstante Größe der Arrays uchar zu erstellen, erstellen Sie Makro-Substitutionen in der Datei 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
Da die Länge der Kommentarzeile auf 64 Zeichen begrenzt ist, wird die Array-Größe entsprechend diesem Wert erstellt. Weiterhin müssen wir möglicherweise Auftragsobjekte in Dateien speichern, und die Länge von weniger als 64 Symbolen kann sich als ungeeignet erweisen. Es kann sich durchaus herausstellen, dass eine größere Zeichenkettengröße für die Zeichenketteneigenschaften des Kontos zugewiesen wird. Wenn der Test zeigt, dass die Größe nicht ausreicht, um die Namen der Unternehmen zu speichern, die das Konto betreuen, kann sie jederzeit erhöht werden.
Erstellen Sie die notwendige Struktur zum Speichern der Eigenschaften des Konto-Objekts und Klassenmitgliedervariablen für die Arbeit mit Dateien im 'private' Bereich der Klasse CAccount:
//+------------------------------------------------------------------+ //| 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:Gemäß der Auflistung werden im geschützten Bereich der Klasse CAccount zusätzlich zwei Methoden deklariert — diejenige zum Erzeugen der Struktur aus den Objekt-Eigenschaftsfeldern und eine umgekehrte Methode zum Erzeugen des Konto-Objekts aus der Struktur.
Die erste Methode ist zum Schreiben des Konto-Objekts in die Datei zu verwenden, während die zweite Methode zum Lesen aus der Datei verwendet
werden soll.
Lassen Sie uns die Methoden außerhalb des Klassenkörpers schreiben:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Wie wir in der Auflistung sehen können, werden alle Integer- und Realeigenschaften des Objektes in den gleichnamigen Strukturfeldern
gespeichert.
Um die Zeichenketteneigenschaften zu speichern, konvertieren Sie die
Zeichenkette in das uchar-Array und speichern Sie sie in den entsprechenden Strukturfeldern.
Nach dem Speichern
der Objekteigenschaften wird die gesamte
Struktur in
das uchar-Array gespeichert, das dann in der Datei.
gespeichert wird.
//+------------------------------------------------------------------+ //| Create the account object from the structure | //+------------------------------------------------------------------+ void CAccount::StructToObject(void) { //--- Sichern der ganzzahligen Eigenschaften 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; //--- Sichern der Double-Eigenschaften 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; //--- Sichern der String-Eigenschaften 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); } //+------------------------------------------------------------------+
Das Verfahren der Rücktransformation der Strukturfelder in die Eigenschaften der Konto-Objekte ist nahezu identisch mit dem oben
beschriebenen ersten Verfahren.
Hier erhalten wir die Eigenschaften der Zeichenketten der Konto-Objekte
durch
Umwandlung der uchar-Arrays der Struktur in Zeichenketten.
Deklarieren Sie im 'public' Bereich der Klasse CAccount die virtuellen Methoden Save() und Load():
public: //--- Konstructor 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; } //--- Rückgabe des Flags der Order mit den Eigenschaften 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 | //+------------------------------------------------------------------+
Schreiben wir die Methoden, um das Konto-Objekt in der Datei zu speichern und aus der Datei herunterzuladen:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
wobei:
- das Handle der bereits zum Schreiben geöffneten Datei wird an die Methode
übergeben,
- wir alle Objektfelder in der POD-Struktur speichern,
- wir die POD-Struktur in die Datei schreiben, deren Handle
der Methode übergeben wurde.
Die Methode zum Laden der Objektdaten aus der Datei:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
wobei:
- das Handle der zuvor zum Lesen geöffneten Datei der Methode übergeben wird,
- die Daten aus der Datei in das uchar-Array geladen werden,
- die Array-Daten in der POD-Struktur gespeichert werden,
- die POD-Strukturdaten in die Objektfelder geschrieben werden.
Das Arbeiten mit der Kollektion der Konten geschieht wie folgt: Beim Start des Ausführungsprogramms wird das laufende Konto
geprüft, das Konto-Objekt mit den aktuellen Kontodaten erstellt und in die Liste der Konto-Objekte gestellt. Als Nächstes
werden wir uns den Ordner mit den Dateien der zuvor gespeicherten Konten ansehen. Wenn es Dateien enthält, werden wir sie
nacheinander lesen, um zu prüfen, ob die Konsistenz mit dem aktuellen Konto aufrechterhalten wird, und sie in die Liste der
Kollektion der Konten aufnehmen. Nachdem wir die Liste erstellt haben, überprüfen wir den Status des aktuellen Kontos im Timer
und zeichnen die aufgetretenen Änderungen auf, falls vorhanden.
Für einige Änderungen werden wir Ereignisse erstellen und an das Programm senden, um Änderungen an den Kontoparametern zu
steuern. So ist beispielsweise eine plötzliche Änderung des Hebels ein sehr einschneidendes und unangenehmes Ereignis, über
das ein Benutzer und sein Programm rechtzeitig informiert werden sollten.
Da wir im Timer arbeiten müssen, sowie eine neue Kollektionsliste erstellen müssen, werden wir Makro-Substitutionen mit den Timer-Parametern und Listen-ID für sie in der Datei Defines.mqh erstellen. Achten Sie auch darauf, die Namen der zuvor erstellten Makro-Substitutionen für Kollektion der Timer für die Aufträge, Deals und Positionen zu ändern (fügen Sie " ORD" zu ihrem Namen hinzu, um zwischen Makro-Substitutionen zu unterscheiden, die zu verschiedenen Kollektionen der Timer gehören). Legen Sie die Pause für die Aktualisierung der Kontodaten auf eine Sekunde fest. Ich denke, das wird ausreichen, um Änderungen zu verfolgen und die Belastung des Systems zu verringern:
//+------------------------------------------------------------------+ //| Macro-Substitution | //+------------------------------------------------------------------+ //--- Beschreibung der Funktion mit der Zeilennummer des Fehlers #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
Ersetzen Sie im Text der Klasse CEngine COLLECTION_PAUSE, COLLECTION_COUNTER_STEP und COLLECTION_COUNTER_STEP durch die entsprechenden neuen Namen der Makro-Substitution: COLLECTION_ORD_PAUSE, COLLECTION_ORD_COUNTER_STEP und COLLECTION_ORD_COUNTER_ID.
Da wir die Kollektion der Konten erstellen, bedeutet dies die Möglichkeit, die Eigenschaften mehrerer Konto-Objekte zu vergleichen. Fügen Sie dazu der Kollektion der Konten in der Klasse CSelect Auswahl- und Sortiermethoden zur Auswahl der dem Kriterium entsprechenden Objekte hinzu. Die Klasse wurde im dritten Teil der Bibliotheksbeschreibung beschrieben.
Öffnen Sie die Datei Select.mqh, die im Dienstklassenordner der Bibliothek \MQL5\Include\DoEasy\Services ausgewählt wurde, verbinden Sie die Datei mit der Account-Klasse damit und fügen Sie die neuen Methoden zum Arbeiten mit Account-Objekten hinzu:
//+------------------------------------------------------------------+ //| Select.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #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 //+------------------------------------------------------------------+ //| Klasse zum Sortieren der Objekte, die dem Kriterium entsprechen | //+------------------------------------------------------------------+ class CSelect { private: //--- Vergleichsmethode zweier Werte template<typename T> static bool CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode); public: //+------------------------------------------------------------------+ //| Methods of working with orders | //+------------------------------------------------------------------+ //--- Rückgabe der Auftragsliste mit der (1) Integer-, (2) Double- und (3) String-Eigenschaft, die dem angegebenen Kriterium entspricht 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); }; //+------------------------------------------------------------------+
Implementieren wir die deklarierte Methoden außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| 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 | //| mit dem Maximum der Integer-Eigenschaft | //+------------------------------------------------------------------+ 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 | //| mit dem Maximum der Double-Eigenschaft | //+------------------------------------------------------------------+ 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 | //| mit dem Maximum der String-Eigenschaft | //+------------------------------------------------------------------+ 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 | //| mit dem Minimum der Integer-Eigenschaft | //+------------------------------------------------------------------+ 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 | //| mit dem Minimum der Double-Eigenschaft | //+------------------------------------------------------------------+ 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 | //| mit dem Minimum der String-Eigenschaft | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+
Die Arbeit der Methoden wurde im dritten Teil der Bibliotheksbeschreibung besprochen, so dass wir hier nicht auf ihre Beschreibung
eingehen werden. Sie können jederzeit
zurück zum benötigten Artikel gelangen.
Lassen Sie uns ein Beispiel einer Klasse der Kollektion der Konten erstellen.
Erstellen Sie in der Bibliotheksdatei MQL5\Include\DoEasy\Collections\ die neue AccountsCollection.mqh Klassendatei, beziehen Sie die notwendigen Klassendateien ein und tragen Sie sofort die Standardmethoden ein:
//+------------------------------------------------------------------+ //| AccountsCollection.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #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: //--- Rückgabe des der gesamten Kollektionsliste des Ereignisses "wie besehen" CArrayObj *GetList(void) { return &this.m_list_accounts; } //--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen 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); } //--- Konstructor CAccountsCollection(); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+
Dieser kurze Code im Klassenkonstruktor wird verwendet, um die Liste vorzubereiten, in der die Konto-Objekte gespeichert werden sollen:
- die Liste wird geleert,
- die Liste ist so eingestellt, dass sie nach einer Kontonummer
sortiert wird und
- die ID der Kollektion der Konten ist der Liste zugeordnet.
Die Arbeit der Kontenerfassungsklasse ist wie folgt gegliedert: Wenn das Programm an eine Symboltabelle angehängt ist, haben wir Zugriff auf die aktuellen Daten eines einzelnen Kontos. Wir sind in der Lage, die Veränderungen seiner Eigenschaften zu verfolgen und auf deren Veränderungen zu reagieren. Die restlichen Konten können nur im Programm "verfolgt" werden — ihr letzter Zustand zum Zeitpunkt der Verbindung zu einem neuen Konto. Daher enthält die Liste der Kontoverbindungen die Objekte aller Konten, mit denen wir uns jemals verbunden haben, obwohl wir nur die Änderungen des aktuellen Kontos verfolgen können. Außerdem werden wir in der Lage sein, die Daten aller Konten zu vergleichen, die wir für jedes Objekt haben.
Um wichtige Kontoeigenschaften zu verfolgen, verwenden Sie das Hash-Control — das die Summe aller Kontoeigenschaften ab der aktuellen Zeit mit der Summe vergleicht, die während der vorherigen Überprüfung erhalten wurde. Sobald sich die Summe ändert, überprüfen wir, was sich genau geändert hat und setzen das entsprechende Änderungskennzeichen. Als nächstes, bei der Verfolgung von Kontoereignissen (Änderungen wichtiger Kontoeigenschaften), signalisiert das Flag, dass wir alle verfolgten Eigenschaften überprüfen und Ereignisse über die geänderten Eigenschaften an das Programm senden müssen.
Lassen Sie uns alle notwendigen Variablen und Klassenmethoden hinzufügen und anschließend analysieren:
//+------------------------------------------------------------------+ //| AccountsCollection.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #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; } //--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen 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); }; //+------------------------------------------------------------------+
Im 'private' Bereich der Klasse gibt es die Struktur MqlDataAccount zum Speichern wichtiger Kontoeigenschaften. Sie soll alle verfolgten Objekteigenschaften des Kontos speichern. Wir haben zwei Variablen mit dem Strukturtyp: die erste speichert die Daten des laufenden Kontos, während die andere die vorherigen Daten speichert. Die einzige Eigenschaft, die in der Struktur unverändert bleibt, ist ein login, der die Kontonummer speichert. Der Feldwert soll zur Definition des ersten Starts verwendet werden. Wenn die Struktur im Feld "Login" Null enthält, ist dies der erste Start, und der aktuelle Kontostand sollte für den späteren Vergleich als der vorherige gespeichert werden. Legen Sie im Hash-Feld der Struktur die Summe der Werte aller Strukturfelder fest und vergleichen Sie sie mit dem Wert, der in der Struktur des "vorherigen" Kontostandes eingestellt ist. Wenn eine Abweichung zwischen den Werten dieser beiden Felder festgestellt wird, gilt eine Änderung der Eigenschaften des Konto-Objekts als erkannt.
Da die Liste der Kollektion der Konten die Daten verschiedener Konten speichern soll (alle Konten, mit denen wir während des
bibliothekarischen Programmbetriebs verbunden sind und das aktuelle), obwohl wir die in der Liste gespeicherten Kontodaten nicht
durch Lesen aus der Datei verfolgen können, müssen wir den genauen
Index des Konto-Objekts in der Liste kennen, das das aktuelle Konto-Objekt
ist, das wir verfolgen müssen. Dieser Index soll verwendet werden, um das Konto-Objekt zu erhalten und den Status seiner Eigenschaften im
Timer zu überprüfen. Wir haben auch die
Klassenvariable, die als Flag zum Ändern der Eigenschaften der Konto-Objekte verwendet
werden soll, und die Variable, die die Adresse des Ordners
im Bibliotheksverzeichnis enthält, in dem wir die Klassenobjekte speichern werden.
Der gleiche 'private' Bereich enthält die vier Methoden. Lassen Sie uns einen Blick auf ihre Umsetzung werfen.
Die Methode zum Schreiben von Daten des aktuellen Kontos in die Eigenschaften des Konto-Objekts:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Lassen Sie uns einen Blick auf die Hebelwirkung am Beispiel des Updates werfen:
die
Methode erhält den Zeiger auf das Konto-Objekt, die Daten des
aktuellen Kontos werden zu den Feldern des Konto-Objekts und die
Felder der Struktur mit dem aktuellen Kontostand hinzugefügt. Als Nächstes wird der Wert
jeder erhaltenen Eigenschaft zum Hash hinzugefügt.
Die Methode SavePrevValues() zum Speichern der Struktur des aktuellen Kontostandes in der vorherigen Statusstruktur kopiert einfach die aktuelle Statusstruktur in den vorherigen Status.
Das Verfahren zum Überprüfen des Vorhandenseins des Konto-Objekts in der Kollektionsliste:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode erhält den Zeiger auf das Konto-Objekt,
dessen Daten in der Kollektionsliste gefunden werden sollen. Die Suche erfolgt über eine Kontonummer sowie über Kunden- und
Firmennamen über die Methode
IsEqual(), die ich beim Anlegen der Objektklasse Konto bereits beschrieben habe.
Verwenden
Sie die Objektliste des Kontos (in einer Schleife) , um das Objekt
aus der Liste zu erhalten und vergleichen Sie seine Daten mit den Daten des
an die Methode übergebenen Objekts.
Wenn die Daten
übereinstimmen, wird
true zurückgegeben,
andernfalls false,
wenn nach dem Ende der Schleife keine übereinstimmende Objekte gefunden werden.
Die Methode, die den Objektindex des Kontos in der Liste mit den aktuellen Kontodaten zurückgibt:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Verwenden Sie die Objektliste des Kontos (in einer Schleife),
um das Objekt zu erhalten und seine Kontodaten (Login, Kunden- und Firmennamen) mit den Kontodaten zu vergleichen, mit denen
das Programm gestartet wurde.
Der Schleifenindex wird im Falle einer Übereinstimmung zurückgegeben.
Nach dem Ende der Schleife wird
-1 zurückgegeben, wenn das Objekt mit den aktuellen Kontodaten nicht
gefunden wird.
Die folgenden Methoden werden im Abschnitt 'public' der Klasse hinzugefügt:
Die Methode, die den Variablenwert zurückgibt, der den Index des
Konto-Objekts mit den aktuellen Kontodaten speichert, die
Methode, die das Flag einer aufgetretenen Änderung der Kontoeigenschaften zurückgibt. Außerdem gibt es einen Destruktor
der Klasse (um alle Konten aus der Liste in den Dateien zu speichern), die
Methode, die das Konto-Objekt zur Kollektionsliste hinzufügt, die
Methoden zum Speichern und Laden/Speichern der Objekte in/aus der Datei und die
Methode zum Aktualisieren der aktuellen Kontodaten im Objekt des aktuellen Kontos:
public: //--- Return the full account collection list "as is" CArrayObj *GetList(void) { return &this.m_list_accounts; } //--- Rückgabe der Auswahlliste von (1) Integer-, (2) Double- und (3) String-Eigenschaften, die dem Vergleichskriterium entsprechen 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); }; //+------------------------------------------------------------------+
Lassen Sie uns diese Methoden betrachten.
Die Bibliotheksdateien sind im Terminalordner Files\DoEasy\ mit Ordnern für jede Klasse zu speichern (wenn die Klasse die Dateien speichern muss). Es gibt auch die Klassenvariable m_folder_name, mit der Sie den Namen des Ordners, in dem Konto-Objekte gespeichert werden, festlegen können. Initialisieren Sie sie in der Initialisierungsliste des Klassenkonstruktors sofort zusammen mit der Flag-Variablen einer aufgetretenen Änderung der Kontoeigenschaften:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ 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(); } //+------------------------------------------------------------------+
Als Nächstes setzen Sie im Klassenkonstruktor die Struktur mit den
vorherigen Daten des aktuellen Kontos und erzeugen Sie den Ordner zum
Speichern der Klassendateien, der sich unter "Common_data_folder"\Files\DoEasy\Accounts für die Klasse befinden soll.
Ein
Konto-Objekt mit den aktuellen Kontodaten wird dann erstellt
und mit der Methode
AddToList() zur Kollektionsliste der Konten hinzugefügt. Wenn kein Objekt zur Liste hinzugefügt wird, wird die
entsprechende Nachricht an das Journal gesendet, andernfalls wird die Nachricht mit kurzen Kontoeigenschaften (Login,
Kundenname, Firmenname, Kontostand, Hebelwirkung und Kontoart, wenn nicht Netting) angezeigt.
Der nächste Schritt ist Eintragen der Konto-Objekte in die
Kollektionsliste. Dies sind die Konto-Objekte mit ihren Speicherdateien im Ordner, in dem die Klassenobjekte gespeichert sind.
Der letzte Schritt ist die
Suche nach dem Objektindex mit den aktuellen Kontodaten und Zuweisen von m_index_current
zu seiner Variablen, deren Wert von der Methode IndexCurrentAccount() zur Verwendung in Programmen zurückgegeben
wird.
Die Methode zum Speichern aller Objekte aus der Kollektionsliste in die entsprechenden Dateien wird im Destruktor der Klasse aufgerufen:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CAccountsCollection::~CAccountsCollection(void) { //--- Save account objects from the list to the files this.SaveObjects(); } //+------------------------------------------------------------------+
Die Methode zum Hinzufügen des Konto-Objekts zur Kollektionsliste:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode erhält den Zeiger auf das Konto-Objekt, dann
wird
die Methode IsPresent() verwendet, um das Vorhandensein eines solchen
Objekts in der Kollektionsliste zu überprüfen. Wenn es noch kein solches Objekt gibt, wird es
zur Kollektionsliste hinzugefügt und das Ergebnis des Hinzufügens wird zurückgegeben.
Die Methode zum Speichern von Konto-Objekten aus der Kollektionsliste in die Dateien:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Verwenden Sie die Kollektionsliste (in einer Schleife) , um das Konto-Objekt aus der Liste und Erstellen Sie einen Dateinamen, der aus dem Pfad zum Ordner des Konto-Objekts, Servername und Login (Kontonummer) mit der Erweiterung ".bin" besteht. Wenn eine solche Datei im Objektordner des Kontos existiert, wird die Datei gelöscht und eine neue zum Schreiben geöffnet. Das geöffnete Datei-Handle wird an die virtuelle Methode Save() der Klasse CAccount übergeben, die ich zuvor beschrieben habe, und das Ergebnis Dateispeicherung wird der Variablen res hinzugefügt, die das Ergebnis des Schreibens in die Datei aller Konto-Objekte aus der Kollektionsliste zurückgibt. Die zum Schreiben geöffnete Datei wird nach dem Speichern des Objekts geschlossen.
Die Methode zum Herunterladen von Konto-Objekten aus den Dateien in die Kollektionsliste:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Zuerst finden Sie die allererste Datei im Ordner, in dem
die Bibliothekskontenobjektdateien gespeichert sind. Als Nächstes öffnen Sie eine weitere gefundene Datei zum Lesen in der
Do-while-Schleife,
erzeugen Sie ein neues Konto-Objekt und laden
Sie Daten aus der Datei mit der virtuellen Methode
Load() der Klasse CAccount. Wenn es kein solches Objekt gibt (mit den gleichen Kontodaten), wird das Objekt in
die Liste aufgenommen. Im Falle eines Fehlers beim Lesen von Daten
aus der Datei in das Objekt oder beim Hinzufügen des Objekts zur Liste, entfernen Sie dieses neue Objekt (um Speicherlecks zu
vermeiden) und schließen Sie die Datei.
Nach dem Ende der Schleife wird das Ergebnis des Lesens von Daten aus den Dateien in
das Konto-Objekt und deren Platz in der Kollektionsliste zurückgegeben.
Das Verfahren zum Aktualisieren der Daten des aktuellen Konto-Objekts:
//+------------------------------------------------------------------+ //| 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); //--- Erster Start 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(); } } //+------------------------------------------------------------------+
Hier ist das erste, was wir tun, die Gültigkeit des Index des
Konto-Objekts zu überprüfen, das die aktuellen Kontodaten enthält. Wenn es aus irgendeinem Grund nicht erhalten wurde, wird die
Methode verlassen. Als Nächstes erhalten Sie das Konto-Objekt mit den aktuellen Kontodaten über seinen Index in der Liste, setzen
Sie die Datenstruktur des aktuellen Kontos zurück, setzen Sie das Flag zum Ändern der Eigenschaften des Konto-Objekts zurück
und
rufen Sie die Methode zum Einstellen der Eigenschaften des Konto-Objekts
auf. Die gleiche Methode kopiert die neuesten (neu gelesenen) Eigenschaften in die Datenstruktur des aktuellen Kontos, um sie
anschließend mit dem vorherigen Status des Kontos zu vergleichen und die Änderungen zu erkennen.
Als Nächstes definieren Sie, welche Daten in der Datenstruktur des vorherigen Status des Kontos gesetzt werden. Wenn
das Login-Feld Null enthält, bedeutet das, dass die Struktur nie gefüllt wurde und dies der erste Start ist. So füllen wir
einfach
die Struktur mit den vorherigen Zustandsdaten mit den Daten aus der aktuellen
Zustandsstruktur.
Als Nächstes überprüfen Sie die Hash-Veränderung, indem Sie den aktuellen Status-Hash mit dem
vorherigen Status eins vergleichen. Wenn es Änderungen gibt, setzen Sie das Flag des aufgetretenen Ereignisses zur Änderung der
Kontoeigenschaften und
speichern Sie den aktuellen Status als den vorherigen für ihren nachfolgenden
Vergleich.
Implementieren Sie später die Verfolgung wichtiger Kontostatusänderungen und das Senden von
Ereignismeldungen zu diesen wichtigen Änderungen am Programm.
Da die gesamte Arbeit mit der Klasse aus dem Basisobjekt der Bibliothek (Klasse CEngine) erfolgt, wechseln Sie in die Datei Engine.mqh und fügen Sie die notwendige Funktionsweise hinzu.
Binden Sie zunächst die Datei für die Kollektion der Konten ein:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Services\TimerCounter.mqh" //+------------------------------------------------------------------+
Im 'private' Bereich der Klasse erzeugen Sie das Objekt der Kollektion
der Konten und fügen Sie die Methode für das Arbeiten mit der
Kollektion der Konten hinzu:
//+------------------------------------------------------------------+ //| Bibliothek der Basisklasse | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Kollektion der historischen Aufträge und Deals CMarketCollection m_market; // Kollektion der Marktorder und Deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CArrayObj m_list_counters; // Liste der Timerzähler bool m_first_start; // Flag des Erststarts bool m_is_hedge; // Flag des Hedging-Kontos bool m_is_tester; // Flag of working in the tester bool m_is_market_trade_event; // Flag eines Handelsereignisses des Kontos bool m_is_history_trade_event; // Flag eines historischen Handelsereignisses auf dem Konto ENUM_TRADE_EVENT m_last_trade_event; // Account last trading event //--- Rückgabe des Zählerindex über die 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); //--- Rückgabe der letzten (1) Pending-Order, (2) Marktorder, (3) letzten Position, (4) Position nach Ticket 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); //--- Rückgabe des (1) ersten und (2) des letzten historischen Marktorder aus der Liste aller Positionen, (3) des letzten Deals COrder* GetFirstOrderPosition(const ulong position_id); COrder* GetLastOrderPosition(const ulong position_id); COrder* GetLastDeal(void); public:
Erstellen Sie im Klassenkonstruktor einen neuen Timerzähler für die
Arbeit mit der Kollektion der Konten:
//+------------------------------------------------------------------+ //| CEngine Konstruktor | //+------------------------------------------------------------------+ 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 } //+------------------------------------------------------------------+
Wir haben die Timer und ihre Zähler im dritten Teil der
Bibliotheksbeschreibung diskutiert.
Fügen Sie in der Klasse von OnTimer() den Timer der Kollektion der
Konten hinzu:
//+------------------------------------------------------------------+ //| CEngine Timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer der Kollektion der historischen Aufträge, Deals Marktorders und Positionen 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(); } } } //+------------------------------------------------------------------+
Der Timer der Kollektion der Konten funktioniert ähnlich wie der Timer für die Order-, Deal- und Positionserfassung, der im dritten
Teil der Bibliotheksbeschreibung beschrieben wird (im Abschnitt zur Entwicklung des Bibliotheksbasisobjekts — der Klasse
CEngine). Der einzige Unterschied zum Timer für Kollektionen der
Orders, Deals und Positionen besteht darin, dass eine andere Methode zur Behandlung von Ereignissen aufgerufen wird — die
AccountEventsControl().
Fügen wir die Methode zur Überprüfung der Änderungen in den Eigenschaften des aktuellen Kontos hinzu:
//+------------------------------------------------------------------+ //| Check the account events | //+------------------------------------------------------------------+ void CEngine::AccountEventsControl(void) { this.m_accounts.Refresh(); } //+------------------------------------------------------------------+
Die Methode ruft einfach die Methode Refresh() der Klasse CAccountsCollection auf.
Schreiben Sie im 'public' Teil der Klasse CEngine die beiden Methoden, die die Ereignis
und Konto Kollektionslisten an das Programm zurückgeben. Dies
ermöglicht uns den direkten Zugriff auf die Kollektionslisten aus unseren Programmen:
public: //--- Rückgabe der Liste aller (1) Positionen, (2) Pending-Order und (3) Marktorders CArrayObj* GetListMarketPosition(void); CArrayObj* GetListMarketPendings(void); CArrayObj* GetListMarketOrders(void); //--- Rückgabe der Liste aller historischen (1) Aufträge, (2) gelöschten Pending-Orders, (3) Deals, (4) Positionen nach deren 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(); } //--- Rücksetzen des letzten Handelsereignisses 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; } //--- Erstellen der Timerzählers void CreateCounter(const int id,const ulong frequency,const ulong pause); //--- Timer void OnTimer(void); //--- Constructor/Destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Alles ist bereit, um die Klasse der Kollektion der Konten zu testen. Aber bevor wir mit dem Testen beginnen, ändern wir die Ereignisklasse der Kollektionen CEventsCollection::Refresh. Hinzufügen der Prüfung zur Zeile 233 der Auflistung, um gelegentliche Aktivierungen bei der Definition von Ereignissen zu beseitigen, bei denen ein altes Ereignis zusammen mit dem neuen an das Programm gesendet wird:
//--- Wenn das Ereignis in Kontohistorie existiert if(is_history_event) { //--- If the number of historical orders increased (MQL5, MQL4) if(new_history_orders>0 && new_market_pendings<0) { //--- Erhalt der Liste nur der entfernten Pending-Orders
Ich habe auch eher dumme Fehler beim Schreiben von Handelsfunktionen für MQL4 für die Arbeit im MetaTrader 4 (die Datei DELib.mqh) behoben. Es geht darum, dass die Funktion OrderSend() in MQL4 ein Auftragsticket und keinen booleschen Wert zurückgeben. Anscheinend fange ich an, MQL4 zu vergessen :)
Betrachten wir folgendes Beispiel:
Das Überprüfen des
Ergebnisses der Operation MQL4-Funktion sah wie folgt aus (dies ist korrekt für MQL5):
if( ! OrderSend(sym,ORDER_TYPE_BUY,volume,price,deviation,sl,tp,comment,(int)magic,0,clrBlue))
Ich habe den Fehler behoben, indem ich korrekte Prüfungen für MQL4 implementiert habe:
if(OrderSend(sym,ORDER_TYPE_BUY,volume,price,deviation,sl,tp,comment,(int)magic,0,clrBlue)==WRONG_VALUE)
Dies ist keine große Sache für den Tester, aber es ist immer noch ein Fehler.
Ich werde die vollwertigen
Handelsklassen bald vorstellen, so dass diese Funktionen aus der Bibliothek entfernt werden.
Testen der Kollektion von Konten
Verwenden wir den EA TestDoEasyPart12_1.mq5, das wir bereits entwickelt haben, und speichern wir es unter dem Namen TestDoEasyPart12_2.mq5
im gleichen Ordner \MQL5\Experts\TestDoEasy.
In den Eingabeparameter des EAs wird die Variable zum Umschalten des Aussehens der im Journal angezeigten vorhandenen Kontodaten eingeführt — kurz (standardmäßig false) oder voll ( true):
//--- Eingabeparameter 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 //--- Globale Variablen
Fügen Sie folgenden Code zu der Funktion OnInit() hinzu:
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+
Hier erhalten wir die Liste der Kollektion der Konten mit der Methode GetListAllAccounts() der Klasse CEngine. Wir erhalten jedes nachfolgende Objekt von ihr in einer Schleife, die seine Eigenschaften im Journal je nach Eingabewert anzeigt — entweder einen kurzen Eintrag oder die vollständige Liste der Eigenschaften des Konto-Objekts.
Starten Sie den EA und sehen Sie, was im Journal anzeigt wird, wenn die Kurzbuchung ausgewählt ist ("Show full accounts properties" = falsch):
Jetzt wählen Sie die vollständige Liste aus — drücken Sie F7 und setzen Sie "Show full accounts properties" auf "true"
im Parameterfenster:
Jetzt wird die vollständige Liste der Eigenschaften für jedes der vorhandenen Konten im Journal angezeigt.
Beachten Sie, dass Sie, um Konten in die Datei zu schreiben, eine Verbindung zum ersten Konto herstellen, sich wieder mit dem zweiten
verbinden, dann zum dritten wechseln und so weiter. Mit anderen Worten, frühere Kontodaten werden bei jeder Verbindung zu einem neuen
Konto in die Datei geschrieben.
Was kommt als Nächstes?
Im nächsten Artikel werden wir einige wichtige Ereignisse bei der Änderung der Kontoeigenschaften verfolgen und mit der Arbeit an
Symbolobjekten und deren Kollektion beginnen.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und
herunterladen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.
Frühere Artikel dieser Serie:
Teil 1. Konzept, Datenverwaltung.
Teil
2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung
(Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4.
Handelsereignisse. Konzept.
Teil 5. Klassen und Kollektionen von
Handelsereignissen. Senden von Ereignissen an das Programm.
Teil 6. Ereignisse
auf Netting-Konten.
Teil 7. Ereignis der Aktivierung einer StopLimit-Order,
Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen.
Teil
8. Ereignisse von Änderungen von Orders und Positionen.
Teil 9. Kompatibilität
mit MQL4 - Datenvorbereitung.
Teil 10. Kompatibilität mit MQL4 - Ereignisse der
Positionseröffnung und Aktivierung von Pending-Orders.
Teil 11.
Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen.