
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXX): Schwebende Handelsanfragen - die Verwaltung der Anfrageobjekte
Inhalt
- Konzept
- Das Pause-Objekt
- Ein bisschen Code-Optimierung
- Die Klasse für die Verwaltung von schwebenden Anfrageobjekten
- Tests
- Was kommt als Nächstes?
Konzept
Ausgehend von Artikel 26 haben wir schrittweise das Konzept der Arbeit mit schwebenden Handelsanfragen entwickelt und getestet. Im vorhergehenden Artikel haben wir die Klassen der schwebenden Anfrageobjekte geschaffen, die dem allgemeinen Konzept der Bibliotheksobjekte entsprechen. Dieses Mal werden wir uns mit der Klasse befassen, die die Verwaltung von schwebenden Anfrageobjekten ermöglicht.
Zunächst wollte ich eine eigenständige Klasse zur Verwaltung von schwebende Anfrageobjekte mit allen notwendigen Methoden schaffen. Es stellte sich jedoch heraus, dass die Hauptklasse CTrading der Bibliothek und die geschaffene neue Klasse zur Verwaltung von schwebenden Anfrageobjekten so miteinander verbunden sind, dass es viel einfacher wäre, die neue Klasse zur Verwaltung von schwebenden Anfrageobjekten von der Haupthandelsklasse abzuleiten.
Die gesamte Verwaltung der schwebenden Anfrageobjekte wird im Timer der Klasse durchgeführt, daher machen wir den Timer der Basis-Handelsklasse virtuell, was bedeutet, dass der Timer der Verwaltungsklasse für schwebende Anfragen ebenfalls virtuell sein wird. Dann wird alles, was sich auf den Timer der Basis-Handelsklasse bezieht, im Timer der Klasse eingestellt, während alles, was in der Klasse zur Verwaltung der schwebenden Anfrageobjekte funktionieren soll, im Timer dieser Klasse eingestellt wird.
Abgesehen von der Klasse für die Verwaltung von schwebender Anfrageobjekte werden wir eine kleine Klasse erstellen, um eine Pause zu arrangieren, um die Verwendung der Funktion Sleep() zu vermeiden, die die Programmausführung für eine Verzögerungszeit anhält. Mit dem Pause-Objekt werden wir nicht mehr von Ticks abhängig sein, was bedeutet, dass wir in der Lage sein werden, einen Code zu testen, der an Wochenenden warten muss. Die Pause soll im Timer gesteuert werden.
Beim Reihen einiger Daten nach der Ordnungseigenschaft "Magicnummer" musste ich identische Methoden festlegen, die IDs der Magicnummer und der Gruppen, die in der Reihenfolge der Magicnummern angegeben sind, in verschiedenen Klassen, die diese Daten erfordern, zurückgeben. Ich habe jedoch die Tatsache übersehen, dass ich bereits das Basisobjekt aller Bibliotheksobjekte erstellt hatte, von denen wir die neu erstellten Objekte ableiten müssen. In diesem Fall erhalten die Objekte die Ereignisfunktionalität des Basisobjekts sowie einige Methoden, die in ihren Prinzipien und Zielen ähnlich sind und sich von Klasse zu Klasse wiederholen. Daher ist dies das Objekt, das das Schreiben und den Erhalt von Daten in die Magicnummer enthalten soll, sowie die Verwaltung der Darstellung von Nachrichten im Journal — das Flag, das die Protokollierungsstufe jeder Klasse angibt, die die Darstellung verschiedener Nachrichten enthält. Es wäre vernünftig, alle diese Klassen vom Basisobjekt abzuleiten und die notwendigen Methoden zu erhalten, anstatt in jeder neuen Klasse dasselbe noch einmal zu setzen.
In Anbetracht des oben Gesagten müssen wir die Pausenobjektklasse (die in nachfolgenden Artikeln verwendet werden soll) implementieren, eine gewisse Optimierung des fertigen Codes für die Übertragung von sich wiederholenden Methoden verschiedener Klassen auf die Basisobjektklasse durchführen und die Klasse für die Verwaltung der schwebenden Anfragen erstellen. Außerdem werden wir die Objektklassen für schwebenden Anfragen verbessern, indem wir einige neue Eigenschaften hinzufügen, die es uns ermöglichen, die Zustände zweier miteinander verbundener Objekte zu vergleichen — den aktuellen Status der Ordereigenschaften und den Status der entsprechenden Eigenschaften der Anfrageobjekte. Dies ist notwendig, um die Tatsache der Beendigung der Operation der schwebenden Anfrage zu erkennen, um sie rechtzeitig aus der Liste der aktiven Anfragen zu entfernen, während sie darauf warten, die Anfrage an den Server zu senden.
Wie üblich, fügen wir zunächst die neue Nachrichtenindizes in die Datei Datas.mqh hinzu:
MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE, // Pending request to delete a pending order MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY, // Pending request to modify pending order parameters MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME, // Actual volume MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE, // Actual order price MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT, // Actual StopLimit order price MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL, // Actual StopLoss order price MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP, // Actual TakeProfit order price MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING, // Actual order filling type MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME, // Actual order expiration type MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION, // Actual order lifetime }; //+------------------------------------------------------------------+
und die den neuen Indices entsprechenden Texte:
{"Отложенный запрос на удаление отложенного ордера","Pending request to remove pending order"}, {"Отложенный запрос на модификацию параметров отложенного ордера","Pending request to modify pending order parameters"}, {"Фактический объем","Actual volume"}, {"Фактическая цена установки ордера","Actual order placement price"}, {"Фактическая цена установки StopLimit-ордера","Actual StopLimit order placement price"}, {"Фактическая цена установки StopLoss-ордера","Actual StopLoss order placement price"}, {"Фактическая цена установки TakeProfit-ордера","Actual TakeProfit order placement price"}, {"Фактический тип заливки ордера","Actual order filling type"}, {"Фактический тип экспирации ордера","Actual of order expiration type"}, {"Фактическое время жизни ордера","Actual of order lifetime"}, }; //+---------------------------------------------------------------------+
Das Pause-Objekt
Im Ordner der Klassen und Funktionen des Bibliotheksdienstes \MQL5\Include\DoEasy\Services\ erstellen wir die neue Klasse CPause in der Datei Pause.mqh und fügen sofort alles hinzu, was wir brauchen:
//+------------------------------------------------------------------+ //| Pause.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "DELib.mqh" //+------------------------------------------------------------------+ //| Pause class | //+------------------------------------------------------------------+ class CPause { private: ulong m_start; // Countdown start ulong m_time_begin; // Pause countdown start time ulong m_wait_msc; // Pause in milliseconds public: //--- Set the new (1) countdown start time and (2) pause in milliseconds void SetTimeBegin(const ulong time) { this.m_time_begin=time; this.m_start=::GetTickCount(); } void SetWaitingMSC(const ulong pause) { this.m_wait_msc=pause; } //--- Return (1) the time passed from the countdown start in milliseconds, (2) waiting completion flag //--- (3) pause countdown start time, (4) pause in milliseconds ulong Passed(void) const { return ::GetTickCount()-this.m_start; } bool IsCompleted(void) const { return this.Passed()>this.m_wait_msc; } ulong TimeBegin(void) const { return this.m_time_begin; } ulong TimeWait(void) const { return this.m_wait_msc; } //--- Return the description (1) of the time passed till the countdown starts in milliseconds, //--- (2) pause countdown start time, (3) pause in milliseconds string PassedDescription(void) const { return ::TimeToString(this.Passed()/1000,TIME_SECONDS); } string TimeBeginDescription(void) const { return ::TimeMSCtoString(this.m_time_begin); } string WaitingMSCDescription(void) const { return (string)this.m_wait_msc; } string WaitingSECDescription(void) const { return ::TimeToString(this.m_wait_msc/1000,TIME_SECONDS); } //--- Constructor CPause(void) : m_start(::GetTickCount()){;} }; //+------------------------------------------------------------------+
Wir binden die Datei der Dienstfunktionen DELib.mqh mit der Zeitanzeige in Millisekunden ein — sie wird für die Hilfsklassenmethode benötigt, die die Pausenstartzeit im Journal anzeigt.
Wir deklarieren im 'private' Abschnitt der Klasse drei Klassenvariablen, die für die Pausenberechnung erforderlich sind:
- Die Variable m_start ist notwendig, um eine Referenznummer zum Zeitpunkt der Erstellung des Pausenobjekts zu spezifizieren, sowie um eine neue Referenznummer des neuen Countdown-Starts in ein zuvor erstelltes Pausenobjekt zu schreiben.
- Die Variable m_time_begin speichert die angegebene Pausen-Countdown-Startzeit und wird ausschließlich zur Anzeige von Infomeldungen verwendet.
- Die Variable m_wait_msc enthält die Anzahl der Pausen-Millisekunden — nach Ablauf der angegebenen Anzahl von Millisekunden gilt die Pause als beendet.
Ich glaube, die Klassenmethoden benötigen keine Erklärungen.
- Zunächst schreiben wir die Methode SetTimeBegin() zum Setzen der neuen Countdown-Startzeit die vergangene Zeit in die Variable m_time_begin. Dann setzt wir eine neue Countdown-Start-Referenznummer auf die Variable m_start. Das von der Funktion GetTickCount() zurückgegebene Ergebnis wird als Referenznummer für einen beliebigen Coundown in der Klasse verwendet.
- Die Methode Passed(), die die Anzahl der übergebenen Pausen-Millisekunden zurückgibt, gibt die Differenz in Millisekunden zwischen der aktuellen Referenzzeitmessung und dem in m_start eingestellten Wert beim Start des Pausen-Countdowns zurück.
Die übrigen Klassenmethoden sind ziemlich offensichtlich und bedürfen keiner Erklärung.
Wir werden diese Klasse später brauchen.
Um die Pause-Klasse von überall in der Bibliothek und dem auf der Bibliothek basierenden Programm zugänglich zu machen, binden wir Klassendatei CPause in die Datei der Bibliotheksdienstfunktionen DELib.mqh ein:
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" #include "Pause.mqh" //+------------------------------------------------------------------+
Ein bisschen Code-Optimierung
Es ist an der Zeit, die Methoden, die sich von Klasse zu Klasse wiederholen, in das Basisobjekt aller Bibliotheksobjekte zu verschieben und diese Klassen vom Basisobjekt abzuleiten (falls dies nicht bereits geschehen ist).
Zuerst müssen die neuen Variablen und Methoden in die Klasse des Basisobjekts aller Bibliotheksobjekte geschrieben werden:
//+------------------------------------------------------------------+ //| Base object class for all library objects | //+------------------------------------------------------------------+ #define CONTROLS_TOTAL (10) class CBaseObj : public CObject { private: int m_long_prop_total; int m_double_prop_total; //--- Fill in the object property array template<typename T> bool FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id); protected: CArrayObj m_list_events_base; // Object base event list CArrayObj m_list_events; // Object event list ENUM_LOG_LEVEL m_log_level; // Logging level MqlTick m_tick; // Tick structure for receiving quote data double m_hash_sum; // Object data hash sum double m_hash_sum_prev; // Object data hash sum during the previous check int m_digits_currency; // Number of decimal places in an account currency int m_global_error; // Global error code long m_chart_id; // Control program chart ID bool m_is_event; // Object event flag int m_event_code; // Object event code int m_event_id; // Event ID (equal to the object property value) string m_name; // Object name string m_folder_name; // Name of the folder storing CBaseObj descendant objects bool m_first_start; // First launch flag int m_type; // Object type (corresponds to the collection IDs) //--- Data for storing, controlling and returning tracked properties: //--- [Property index][0] Controlled property increase value //--- [Property index][1] Controlled property decrease value //--- [Property index][2] Controlled property value level //--- [Property index][3] Property value //--- [Property index][4] Property value change //--- [Property index][5] Flag of a property change exceeding the increase value //--- [Property index][6] Flag of a property change exceeding the decrease value //--- [Property index][7] Flag of a property increase exceeding the control level //--- [Property index][8] Flag of a property decrease being less than the control level //--- [Property index][9] Flag of a property value being equal to the control level long m_long_prop_event[][CONTROLS_TOTAL]; // The array for storing object's integer properties values and controlled property change values double m_double_prop_event[][CONTROLS_TOTAL]; // The array for storing object's real properties values and controlled property change values long m_long_prop_event_prev[][CONTROLS_TOTAL]; // The array for storing object's controlled integer properties values during the previous check double m_double_prop_event_prev[][CONTROLS_TOTAL]; // The array for storing object's controlled real properties values during the previous check //--- Return (1) time in milliseconds, (2) milliseconds from the MqlTick time value long TickTime(void) const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ; } ushort MSCfromTime(const long time_msc) const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ; } //--- return the flag of the event code presence in the event object bool IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; } //--- Return the number of decimal places of the account currency int DigitsCurrency(void) const { return this.m_digits_currency; } //--- Returns the number of decimal places in the 'double' value int GetDigits(const double value) const; //--- Set the size of the array of controlled (1) integer and (2) real object properties bool SetControlDataArraySizeLong(const int size); bool SetControlDataArraySizeDouble(const int size); //--- Check the array size of object properties bool CheckControlDataArraySize(bool check_long=true); //--- Check the list of object property changes and create an event void CheckEvents(void); //--- (1) Pack a 'ushort' number to a passed 'long' number long UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value); protected: //--- (1) convert a 'ushort' value to a specified 'long' number byte long UshortToByte(const ushort value,const uchar to_byte) const; public: //--- Set the value of the pbject property controlled (1) increase, (2) decrease, (3) control level template<typename T> void SetControlledValueINC(const int property,const T value); template<typename T> void SetControlledValueDEC(const int property,const T value); template<typename T> void SetControlledValueLEVEL(const int property,const T value); //--- (1) Set, (2) return the error logging level void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- Return the set value of the controlled (1) integer and (2) real object properties increase long GetControlledLongValueINC(const int property) const { return this.m_long_prop_event[property][0]; } double GetControlledDoubleValueINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][0]; } //--- Return the set value of the controlled (1) integer and (2) real object properties decrease long GetControlledLongValueDEC(const int property) const { return this.m_long_prop_event[property][1]; } double GetControlledDoubleValueDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][1]; } //--- Return the specified control level of object's (1) integer and (2) real properties long GetControlledLongValueLEVEL(const int property) const { return this.m_long_prop_event[property][2]; } double GetControlledDoubleValueLEVEL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][2]; } //--- Return the current value of the object (1) integer and (2) real property long GetPropLongValue(const int property) const { return this.m_long_prop_event[property][3]; } double GetPropDoubleValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][3]; } //--- Return the change value of the controlled (1) integer and (2) real object property long GetPropLongChangedValue(const int property) const { return this.m_long_prop_event[property][4]; } double GetPropDoubleChangedValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][4]; } //--- Return the flag of an (1) integer and (2) real property value change exceeding the increase value long GetPropLongFlagINC(const int property) const { return this.m_long_prop_event[property][5]; } double GetPropDoubleFlagINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][5]; } //--- Return the flag of an (1) integer and (2) real property value change exceeding the decrease value long GetPropLongFlagDEC(const int property) const { return this.m_long_prop_event[property][6]; } double GetPropDoubleFlagDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][6]; } //--- Return the flag of an (1) integer and (2) real property value increase exceeding the control level long GetPropLongFlagMORE(const int property) const { return this.m_long_prop_event[property][7]; } double GetPropDoubleFlagMORE(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][7]; } //--- Return the flag of an (1) integer and (2) real property value decrease being less than the control level long GetPropLongFlagLESS(const int property) const { return this.m_long_prop_event[property][8]; } double GetPropDoubleFlagLESS(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][8]; } //--- Return the flag of an (1) integer and (2) real property being equal to the control level long GetPropLongFlagEQUAL(const int property) const { return this.m_long_prop_event[property][9]; } double GetPropDoubleFlagEQUAL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][9]; } //--- Reset the variables of (1) tracked and (2) controlled object data (can be reset in the descendants) void ResetChangesParams(void); virtual void ResetControlsParams(void); //--- Add the (1) object event and (2) the object event reason to the list bool EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam); bool EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value); //--- Set/return the occurred event flag to the object data void SetEvent(const bool flag) { this.m_is_event=flag; } bool IsEvent(void) const { return this.m_is_event; } //--- Return (1) the list of events, (2) the object event code and (3) the global error code CArrayObj *GetListEvents(void) { return &this.m_list_events; } int GetEventCode(void) const { return this.m_event_code; } int GetError(void) const { return this.m_global_error; } //--- Return (1) an event object and (2) a base event by its number in the list CEventBaseObj *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true); CBaseEvent *GetEventBase(const int index); //--- Return the number of (1) object events int GetEventsTotal(void) const { return this.m_list_events.Total(); } //--- (1) Set and (2) return the chart ID of the control program void SetChartID(const long id) { this.m_chart_id=id; } long GetChartID(void) const { return this.m_chart_id; } //--- (1) Set the sub-folder name, (2) return the folder name for storing descendant object files void SetSubFolderName(const string name) { this.m_folder_name=DIRECTORY+name; } string GetFolderName(void) const { return this.m_folder_name; } //--- Return the object name string GetName(void) const { return this.m_name; } //--- Update the object data to search for changes (Calling from the descendants: CBaseObj::Refresh()) virtual void Refresh(void); //--- Return an object type virtual int Type(void) const { return this.m_type; } //--- Return an object event description string EventDescription(const int property, const ENUM_BASE_EVENT_REASON reason, const int source, const string value, const string property_descr, const int digits); //--- Data location in the magic number int value //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value void SetGroupID1(const uchar group,uint &magic) { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16); } void SetGroupID2(const uchar group,uint &magic) { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16); } void SetPendReqID(const uchar id,uint &magic) { magic &=0x00FFFFFF; magic |= (uint)id<<24; } //--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones) uchar ConvToXX(const uchar number,const uchar index) const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index))); } //--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value ushort GetMagicID(const uint magic) const { return ushort(magic & 0xFFFF); } uchar GetGroupID1(const uint magic) const { return uchar(magic>>16) & 0x0F; } uchar GetGroupID2(const uint magic) const { return uchar((magic>>16) & 0xF0)>>4; } uchar GetPendReqID(const uint magic) const { return uchar(magic>>24) & 0xFF; } //--- Constructor CBaseObj(); }; //+------------------------------------------------------------------+
Die geschützte Variable (verfügbar für Nachkommen, aber nicht für Programme) speichert die Werte der Protokollierungsstufe für jedes der Nachkommen des Basisobjekts, das die Anzeige von Informationen im Journal erfordert. Sie erhält die Protokollierungsstufe von der Aufzählung ENUM_LOG_LEVEL, die in Defines.mqh festgelegt wurde:
//+------------------------------------------------------------------+ //| Logging level | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVEL { LOG_LEVEL_NO_MSG, // Trading logging disabled LOG_LEVEL_ERROR_MSG, // Only trading errors LOG_LEVEL_ALL_MSG // Full logging }; //+------------------------------------------------------------------+
Die Variablenwerte werden mit den 'public' Methoden SetLogLevel() und GetLogLevel() gesetzt und zurückgegeben.
Die Methoden zum Setzen und Zurückgeben der Werte der verschiedenen IDs, die in der Magicnummer der Order/Position gespeichert sind, werden ebenfalls 'public' gemacht.
Wir haben diese Methoden schon einmal besprochen, daher macht es keinen Sinn, hier noch einmal darauf einzugehen.
Das Basis-Handelsobjekt des Symbols befindet sich in der Datei \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh.
Lassen Sie uns die notwendigen Verbesserungen vornehmen: die Bibliotheks-Basisobjektdatei in die Datei einbinden und die übergeordnete Klasse CBaseObj zu ihrer Elternklasse machen:
//+------------------------------------------------------------------+ //| TradeObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Trading object class | //+------------------------------------------------------------------+ class CTradeObj : public CBaseObj {
Nach diesen Verbesserungen wird die Handelsobjektklasse des Symbols zu einem Nachkommen des Basisobjekts aller Bibliotheksobjekte. Jetzt müssen wir alle Variablen und Methoden entfernen, die bereits in der Elternklasse CBaseObj vorhanden sind.
Wir entfernen die unnötigen Variablen, die in der Elternklasse vorhanden sind, aus dem 'private' Teil der Klasse:
SActions m_datas; MqlTick m_tick; // Tick structure for receiving prices MqlTradeRequest m_request; // Trade request structure MqlTradeResult m_result; // trade request execution result ENUM_SYMBOL_CHART_MODE m_chart_mode; // Price type for constructing bars ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Margin calculation mode ENUM_ORDER_TYPE_FILLING m_type_filling; // Filling policy ENUM_ORDER_TYPE_TIME m_type_time; // Order type per expiration int m_symbol_expiration_flags; // Flags of order expiration modes for a trading object symbol ulong m_magic; // Magic number string m_symbol; // Symbol string m_comment; // Comment ulong m_deviation; // Slippage in points double m_volume; // Volume datetime m_expiration; // Order expiration time (for ORDER_TIME_SPECIFIED type order) bool m_async_mode; // Flag of asynchronous sending of a trade request ENUM_LOG_LEVEL m_log_level; // Logging level int m_stop_limit; // Distance of placing a StopLimit order in points bool m_use_sound; // The flag of using sounds of the object trading events uint m_multiplier; // The spread multiplier to adjust levels relative to StopLevel
Entfernen wir noch die Methoden zum Setzen und Zurückgeben einer Protokollierungsstufe aus dem 'public' Teil der Klasse:
//--- (1) Return the margin calculation mode, (2) hedge account flag ENUM_ACCOUNT_MARGIN_MODE GetMarginMode(void) const { return this.m_margin_mode; } bool IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } //--- (1) Set, (2) return the error logging level void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- (1) Set, (2) return the filling policy void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.m_type_filling=type; } ENUM_ORDER_TYPE_FILLING GetTypeFilling(void) const { return this.m_type_filling; }
und entfernen auch die Initialisierung der Protokollierungsebene aus der Initialisierungsliste der Konstruktorklasse:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_time(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG)
Fügen wir die Initialisierung der Protokollierungsebene dem der Methodenkörper hinzu:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_time(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy") { //--- Margin calculation mode this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); //--- Spread multiplier this.m_multiplier=1; //--- Set default sounds and flags of using sounds this.m_use_sound=false; this.m_log_level=LOG_LEVEL_ERROR_MSG; this.InitSounds(); } //+------------------------------------------------------------------+
Das Basisobjekt der abstrakten Basisorder befindet sich in der Datei \MQL5\Include\DoEasy\Objects\Orders\Order.mqh
Lassen Sie uns die notwendigen Verbesserungen vornehmen: die Bibliotheks-Basisobjektdatei in die Datei einbinden und die übergeordnete Klasse CBaseObj zu ihrer Elternklasse machen:
//+------------------------------------------------------------------+ //| Order.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Object.mqh> #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract order class | //+------------------------------------------------------------------+ class COrder : public CBaseObj {
Entfernen wir die Methoden zum Empfang der IDs, geschrieben in der Magicnummer der Order aus dem 'private' Teil der Klasse (wir belassen die Stelle im Wert der ID der Magicnummer):
//+------------------------------------------------------------------+ //| Abstract order class | //+------------------------------------------------------------------+ class COrder : public CObject { private: ulong m_ticket; // Selected order/deal ticket (MQL5) long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the order's (1) double and (2) string properties are located at int IndexProp(ENUM_ORDER_PROP_DOUBLE property) const { return(int)property-ORDER_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_ORDER_PROP_STRING property) const { return(int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL; } //--- Data location in the magic number int value //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value ushort GetMagicID(void) const { return ushort(this.Magic() & 0xFFFF); } uchar GetGroupID1(void) const { return uchar(this.Magic()>>16) & 0x0F; } uchar GetGroupID2(void) const { return uchar((this.Magic()>>16) & 0xF0)>>4; } uchar GetPendReqID(void) const { return uchar(this.Magic()>>24) & 0xFF; } public:
Im geschlossenen Klassenkonstruktor fügen wir die Spezifikation einer Magicnummer zu den Methoden zum Empfang von IDs hinzu:
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket) { //--- Save integer properties this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_EXP] = this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE_FILLING] = this.OrderTypeFilling(); this.m_long_prop[ORDER_PROP_TYPE_TIME] = this.OrderTypeTime(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID(); this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = this.PositionTimeUpdateMSC(); //--- Save real properties this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)] = this.OrderPriceStopLimit(); //--- Save string properties this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); //--- Save additional integer properties this.m_long_prop[ORDER_PROP_PROFIT_PT] = this.ProfitInPoints(); this.m_long_prop[ORDER_PROP_TICKET_FROM] = this.OrderTicketFrom(); this.m_long_prop[ORDER_PROP_TICKET_TO] = this.OrderTicketTo(); this.m_long_prop[ORDER_PROP_CLOSE_BY_SL] = this.OrderCloseByStopLoss(); this.m_long_prop[ORDER_PROP_CLOSE_BY_TP] = this.OrderCloseByTakeProfit(); this.m_long_prop[ORDER_PROP_MAGIC_ID] = this.GetMagicID((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_GROUP_ID1] = this.GetGroupID1((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_GROUP_ID2] = this.GetGroupID2((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_PEND_REQ_ID] = this.GetPendReqID((uint)this.GetProperty(ORDER_PROP_MAGIC)); //--- Save additional real properties this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)] = this.ProfitFull(); //--- Save additional string properties this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)] = ""; } //+------------------------------------------------------------------+
Die Basis-Handelsklasse ist in \MQL5\Include\DoEasy\Trading.mqh.
Nehmen wir die notwendigen Verbesserungen vor. Da die Methode zur übergeordneten Methode für die zukünftige Klasse der Verwaltung von schwebenden Anfragen geworden ist, verschieben wir die Zeiger auf die Objektkollektion und die Liste der Zeiger auf schwebenden Anfragen vom 'private' Teil in den 'protected' Bereich:
//+------------------------------------------------------------------+ //| Trading class | //+------------------------------------------------------------------+ class CTrading : public CBaseObj { protected: CAccount *m_account; // Pointer to the current account object CSymbolsCollection *m_symbols; // Pointer to the symbol collection list CMarketCollection *m_market; // Pointer to the list of the collection of market orders and positions CHistoryCollection *m_history; // Pointer to the list of the collection of historical orders and deals CEventsCollection *m_events; // Pointer to the event collection list CArrayObj m_list_request; // List of pending requests private:
Entfernen wir die Variable zur Speicherung der Protokollierungsstufe aus dem 'private' Teil:
private: CArrayInt m_list_errors; // Error list bool m_is_trade_disable; // Flag disabling trading bool m_use_sound; // The flag of using sounds of the object trading events uchar m_total_try; // Number of trading attempts ENUM_LOG_LEVEL m_log_level; // Logging level MqlTradeRequest m_request; // Trading request prices ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Flags of error source in a trading method ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error
Im 'public' Teil der Klasse fügen wir die Methode hinzu, die das gesamte Objekt der Handelsklasse zurückgibt, und machen den Timer der Klasse virtuell:
public: //--- Return itself CTrading *GetObject(void) { return &this; } //--- Constructor CTrading(); //--- Timer virtual void OnTimer(void); //--- Get the pointers to the lists (make sure to call the method in program's OnInit() since the symbol collection list is created there)
Bei der Methode zur Erstellung einer schwebenden Anfrage, fügen wir die Übergabe eines Anfrageobjekts an die Methode und entfernen die Methoden zur Arbeit mit IDs, die in der Magicnummer der Order gesetzt sind:
//--- Create a pending request bool CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order); //--- Data location in the magic number int value //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value void SetGroupID1(const uchar group,uint &magic) { magic &=0xFFF0FFFF; magic |= uint(ConvToXX(group,0)<<16); } void SetGroupID2(const uchar group,uint &magic) { magic &=0xFF0FFFFF; magic |= uint(ConvToXX(group,1)<<16); } void SetPendReqID(const uchar id,uint &magic) { magic &=0x00FFFFFF; magic |= (uint)id<<24; } //--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones) uchar ConvToXX(const uchar number,const uchar index) const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));} //--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value ushort GetMagicID(const uint magic) const { return ushort(magic & 0xFFFF); } uchar GetGroupID1(const uint magic) const { return uchar(magic>>16) & 0x0F; } uchar GetGroupID2(const uint magic) const { return uchar((magic>>16) & 0xF0)>>4; } uchar GetPendReqID(const uint magic) const { return uchar(magic>>24) & 0xFF; } }; //+------------------------------------------------------------------+
Entfernen wir alles aus der Implementieren des Timers der Klasse und lassen diese leer:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { } //+------------------------------------------------------------------+
Hier werden wir den Code für den Fall schreiben, dass wir etwas nur im Timer dieser Klasse behandeln müssen, von der andere Handelsklassen abgeleitet werden (in der aktuellen Implementierung für die zukünftige Klasse der Verwaltung von schwebenden Anfragen).
In der Methode CheckTradeConstraints() zur Überprüfung von Handelsbeschränkungen nämlich im Block zur Überprüfung der Volumengültigkeit bei Handelsoperationen, der den Wert der einen Losgröße erfordert (Bedingung ergänzt), die Volumengültigkeit nur dann überprüft, wenn der von der Eingabe übermittelte Wert Null überschreitet:
//--- If closing or not removing/modifying if(action_type<ACTION_TYPE_CLOSE_BY || action_type==ACTION_TYPE_CLOSE) { //--- In case of close-only, write the error code to the list and return 'false' - there is no point in further checks if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY); return false; } //--- Check the minimum volume if(volume>0) { if(volume<symbol_obj.LotsMin()) { //--- The volume in a request is less than the minimum allowed one. //--- add the error code to the list this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } //--- Check the maximum volume else if(volume>symbol_obj.LotsMax()) { //--- The volume in the request exceeds the maximum acceptable one. //--- add the error code to the list this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } //--- Check the minimum volume gradation double step=symbol_obj.LotsStep(); if(fabs((int)round(volume/step)*step-volume)>0.0000001) { //--- The volume in the request is not a multiple of the minimum gradation of the lot change step //--- add the error code to the list this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } } } //--- When opening a position
Dann:
Eine Methode erhält ein Anfrageobjekt, falls das Erstellen eines schwebenden Anfrageobjekts erforderlich ist. Falls es kein solches Objekt gibt (in den Methoden zur Eröffnung einer Position und zum Setzen von offenen Aufträgen), übergeben wir der Methode NULL.
In der Methode zur Eröffnung einer Position, übergeben wir NULL als letzten Parameter:
//--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal, //--- create a pending request and return 'false' (OpenPosition) if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- If the trading request magic number, has no pending request ID if(this.GetPendReqID((uint)magic)==0) { //--- Play the error sound if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); //--- set the last error code to the return structure int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED) code=10027; trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } //--- Waiting time in milliseconds: //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value, ulong wait=method; //--- Look for the least of the possible IDs. If failed to find //--- or in case of an error while updating the current symbol data, return 'false' int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Write the pending request object ID to the magic number and fill in the remaining unfilled fields of the trading request structure uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); this.m_request.magic=mn; this.m_request.action=TRADE_ACTION_DEAL; this.m_request.symbol=symbol_obj.Name(); this.m_request.type=order_type; //--- Set the number of trading attempts and create a pending request uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,NULL); } return false; }
Bei der Methode zum Setzen einer Pending-Order übergeben wir NULL als Order-Objekt an die Methode zur Erstellung einer Pending-Order, während bei den Methoden, die mit den bereits bestehenden Orders und Positionen arbeiten, das Order-Objekt an die Methode zur Erstellung einer Pending-Order übergeben wird, z.B. das Orderobjekt bei der Methode zur Änderung von Stop Orders der bestehenden Position:
//--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal, //--- create a pending request and return 'false' (ModifyPosition) if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- If the pending request object with the position ticket is not present in the list if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE) { //--- Play the error sound if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true)); //--- set the last error code to the return structure int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED) code=10027; trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } //--- Waiting time in milliseconds: //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value, ulong wait=method; //--- Look for the least of the possible IDs. If failed to find //--- or in case of an error while updating the current symbol data, return 'false' int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Write a type of a conducted operation, as well as a symbol and a ticket of a modified position to the request structure this.m_request.action=TRADE_ACTION_SLTP; this.m_request.symbol=symbol_obj.Name(); this.m_request.position=ticket; this.m_request.type=order_type; this.m_request.volume=order.Volume(); //--- Set the number of trading attempts and create a pending request uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,order); } return false; }
Bei den übrigen Methoden mit der bekannten Reihenfolge oder Position übergeben wir das Anfrageobjekt an die Methode zur Erstellung einer schwebenden Anfrage.
Während des Tests stellte sich heraus, dass die Methode ClosePosition() Positionsschließung im Falle einer Teilschließung manchmal ein ungültiges Volumen einer geschlossenen Position erhielt. Es wurde ein Null-Volumen gesendet und die Verifizierung wurde übersprungen, um nie mehr wiederholt zu werden. Um dies zu beheben, haben wir bereits die Überprüfung des Volumens beim Schließen einer Position zu der Methode hinzugefügt, die klärt, ob der Handel aktiviert ist. Jetzt fügen wir das Schreiben des korrekten Volumens bei der Erstellung einer schwebenden Anfrage sowie das Senden der Verifizierungsmethode hinzu.
Dazu fügen wir eine einzelne Zeichenkette zur Methode hinzu:
//--- Write a deviation and a comment to the request structure this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.volume=(volume==WRONG_VALUE || volume>order.Volume() ? order.Volume() : symbol_obj.NormalizedLot(volume)); //--- If there are trading and volume limitations //--- there are limitations on FreezeLevel - play the error sound and exit ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(volume,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) {
Hier fügen wir das Volumen der Struktur der Handelsanfragen auf folgende Weise hinzu: Wenn -1 an die Methode übergeben wird oder das an die Methode übergebene Volumen das der geschlossenen Position übersteigt, schreiben wir das volle Positionsvolumen (vollständiges Schließen) in die Struktur. Andernfalls schreiben wir das normalisierte Volumen, das an die Methode übergeben wird (es kann ungültig sein, z.B. wenn das Volumen von 0,05 durch 2 geteilt wird, wenn eine Position teilweise geschlossen wird, erhält die Methode 0,025, was einen Fehler verursacht).
Bei der Methode zur Modifizierung der Pending-Order ModifyOrder() fügen wir die ID der Magicnummer und das Auftragsvolumen zur Struktur der Handelsanfrage hinzu:
//--- Write the magic number, volume, filling type, as well as expiration date and type to the request structure this.m_request.magic=order.GetMagicID((uint)order.Magic()); this.m_request.volume=order.Volume(); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling()); this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration()); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime()); //--- If there are trading limitations, //--- StopLevel or FreezeLevel limitations, play the error sound and exit ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,this.m_request.price,action,order_type,symbol_obj,trade_obj,DFUN,this.m_request.stoplimit,this.m_request.sl,this.m_request.tp,ticket); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) {
Dies ist für den realen Handel völlig nutzlos, da eine Bestellung per Ticket geändert wird. Im Falle von Pending-Orders erhalten wir dadurch jedoch die korrekte Anzeige der Einträge über einen geänderten Auftrag im Journal.
Die Methode zur Erstellung einer Pending-Order wurde geringfügig geändert:
//+------------------------------------------------------------------+ //| Create a pending request | //+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order) { //--- Create a new pending request object depending on a request status CPendRequest *req_obj=NULL; switch(status) { case PEND_REQ_STATUS_OPEN : req_obj=new CPendReqOpen(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_CLOSE : req_obj=new CPendReqClose(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_SLTP : req_obj=new CPendReqSLTP(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_PLACE : req_obj=new CPendReqPlace(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_REMOVE : req_obj=new CPendReqRemove(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_MODIFY : req_obj=new CPendReqModify(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; default: req_obj=NULL; break; } if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); return false; } //--- If failed to add the request to the list, display the appropriate message, //--- remove the created object and return 'false' if(!this.m_list_request.Add(req_obj)) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); delete req_obj; return false; } //--- Fill in the properties of a successfully created object by the values passed to the method req_obj.SetTimeActivate(symbol_obj.Time()+wait); req_obj.SetWaitingMSC(wait); req_obj.SetCurrentAttempt(0); req_obj.SetTotalAttempts(attempts); if(order!=NULL) { req_obj.SetActualVolume(order.Volume()); req_obj.SetActualPrice(order.PriceOpen()); req_obj.SetActualStopLimit(order.PriceStopLimit()); req_obj.SetActualSL(order.StopLoss()); req_obj.SetActualTP(order.TakeProfit()); req_obj.SetActualTypeFilling(order.TypeFilling()); req_obj.SetActualTypeTime(order.TypeTime()); req_obj.SetActualExpiration(order.TimeExpiration()); } else { req_obj.SetActualVolume(request.volume); req_obj.SetActualPrice(request.price); req_obj.SetActualStopLimit(request.stoplimit); req_obj.SetActualSL(request.sl); req_obj.SetActualTP(request.tp); req_obj.SetActualTypeFilling(request.type_filling); req_obj.SetActualTypeTime(request.type_time); req_obj.SetActualExpiration(request.expiration); } //--- Display a brief description of a created pending request if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.PrintShort(); } //--- successful return true; } //+------------------------------------------------------------------+
Mit Blick auf die Zukunft (da wir jetzt die Basis-Handelsklasse bearbeiten) ist zu beachten, dass wir bei der Fertigstellung der schwebenden Anfrageobjekte die Methoden zum Setzen seiner tatsächlichen Werte einführen werden, die es uns ermöglichen, den Objektstatus zu vergleichen - ob er sich geändert hat oder ob die Anfragen bereits aktiviert wurde. Wir vergleichen den Status der Auftragseigenschaften mit den Werten, die in den Eigenschaften des schwebenden Anfrageobjekts eingestellt sind. Wenn sie völlig gleich sind, gehen wir davon aus, dass die schwebende Anfrage ihre Funktion erfüllt hat.
Wir prüfen was als Orderobjekt in der Methode zur Erstellung einer schwebenden Handelsanfrage übergeben wird. Wenn etwas anderes als NULL übergeben wird (das Orderobjekt existiert), werden die Initialparameter aus dem Orderobjekt in das schwebende Anfrageobjekt gesetzt. Andernfalls werden die Parameter aus der Struktur der Handelsanfrage gesetzt.
Dies sind alle Änderungen der Basis-Handelsklasse.
Nun wollen wir die Klasse des schwebenden Anfrageobjekts verbessern.
Wir haben jetzt die neuen Objekteigenschaften der schwebenden Anfrage. Wir schreiben sie in die Datei Defines.mqh.
Hinzufügen der neuen Eigenschaften zu den ganzzahligen Eigenschaften der schwebenden Anfrage und Ändern der Anzahl der ganzzahligen Eigenschaften von 19 auf 22:
//+------------------------------------------------------------------+ //| Integer properties of a pending trading request | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_INTEGER { PEND_REQ_PROP_STATUS = 0, // Trading request status (from the ENUM_PEND_REQ_STATUS enumeration) PEND_REQ_PROP_TYPE, // Trading request type (from the ENUM_PEND_REQ_TYPE enumeration) PEND_REQ_PROP_ID, // Trading request ID PEND_REQ_PROP_RETCODE, // Result a request is based on PEND_REQ_PROP_TIME_CREATE, // Request creation time PEND_REQ_PROP_TIME_ACTIVATE, // Next attempt activation time PEND_REQ_PROP_WAITING, // Waiting time between requests PEND_REQ_PROP_CURRENT_ATTEMPT, // Current attempt index PEND_REQ_PROP_TOTAL, // Number of attempts PEND_REQ_PROP_ACTUAL_TYPE_FILLING, // Actual order filling type PEND_REQ_PROP_ACTUAL_TYPE_TIME, // Actual order expiration type PEND_REQ_PROP_ACTUAL_EXPIRATION, // Actual order lifetime //--- MqlTradeRequest PEND_REQ_PROP_MQL_REQ_ACTION, // Type of a performed action in the request structure PEND_REQ_PROP_MQL_REQ_TYPE, // Order type in the request structure PEND_REQ_PROP_MQL_REQ_MAGIC, // EA stamp (magic number ID) in the request structure PEND_REQ_PROP_MQL_REQ_ORDER, // Order ticket in the request structure PEND_REQ_PROP_MQL_REQ_POSITION, // Position ticket in the request structure PEND_REQ_PROP_MQL_REQ_POSITION_BY, // Opposite position ticket in the request structure PEND_REQ_PROP_MQL_REQ_DEVIATION, // Maximum acceptable deviation from a requested price in the request structure PEND_REQ_PROP_MQL_REQ_EXPIRATION, // Order expiration time (for ORDER_TIME_SPECIFIED type orders) in the request structure PEND_REQ_PROP_MQL_REQ_TYPE_FILLING, // Order filling type in the request structure PEND_REQ_PROP_MQL_REQ_TYPE_TIME, // Order lifetime type in the request structure }; #define PEND_REQ_PROP_INTEGER_TOTAL (22) // Total number of integer event properties #define PEND_REQ_PROP_INTEGER_SKIP (0) // Number of request properties not used in sorting //+------------------------------------------------------------------+
Hinzufügen der neuen Eigenschaften und Ändern der Anzahl von 6 auf 11:
//+------------------------------------------------------------------+ //| Real properties of a pending trading request | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_DOUBLE { PEND_REQ_PROP_PRICE_CREATE = PEND_REQ_PROP_INTEGER_TOTAL,// Price at the moment of a request generation PEND_REQ_PROP_ACTUAL_VOLUME, // Actual volume PEND_REQ_PROP_ACTUAL_PRICE, // Actual order price PEND_REQ_PROP_ACTUAL_STOPLIMIT, // Actual stoplimit order price PEND_REQ_PROP_ACTUAL_SL, // Actual stoploss order price PEND_REQ_PROP_ACTUAL_TP, // Actual takeprofit order price //--- MqlTradeRequest PEND_REQ_PROP_MQL_REQ_VOLUME, // Requested volume of a deal in lots in the request structure PEND_REQ_PROP_MQL_REQ_PRICE, // Price in the request structure PEND_REQ_PROP_MQL_REQ_STOPLIMIT, // StopLimit level in the request structure PEND_REQ_PROP_MQL_REQ_SL, // Stop Loss level in the request structure PEND_REQ_PROP_MQL_REQ_TP, // Take Profit level in the request structure }; #define PEND_REQ_PROP_DOUBLE_TOTAL (11) // Total number of event's real properties #define PEND_REQ_PROP_DOUBLE_SKIP (0) // Number of order properties not used in sorting //+------------------------------------------------------------------+
Hinzufügen des Sortierens nach den neuen Eigenschaften der Liste der möglichen Sortierkriterien hinzufügen:
//+------------------------------------------------------------------+ //| Possible pending request sorting criteria | //+------------------------------------------------------------------+ #define FIRST_PREQ_DBL_PROP (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP) #define FIRST_PREQ_STR_PROP (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP+PEND_REQ_PROP_DOUBLE_TOTAL-PEND_REQ_PROP_DOUBLE_SKIP) enum ENUM_SORT_PEND_REQ_MODE { //--- Sort by integer properties SORT_BY_PEND_REQ_STATUS = 0, // Sort by a trading request status (from the ENUM_PEND_REQ_STATUS enumeration) SORT_BY_PEND_REQ_TYPE, // Sort by a trading request type (from the ENUM_PEND_REQ_TYPE enumeration) SORT_BY_PEND_REQ_ID, // Sort by a trading request ID SORT_BY_PEND_REQ_RETCODE, // Sort by a result a request is based on SORT_BY_PEND_REQ_TIME_CREATE, // Sort by a request generation time SORT_BY_PEND_REQ_TIME_ACTIVATE, // Sort by next attempt activation time SORT_BY_PEND_REQ_WAITING, // Sort by a waiting time between requests SORT_BY_PEND_REQ_CURENT, // Sort by the current attempt index SORT_BY_PEND_REQ_TOTAL, // Sort by a number of attempts SORT_BY_PEND_REQ_ACTUAL_TYPE_FILLING, // Sort by actual order filling type SORT_BY_PEND_REQ_ACTUAL_TYPE_TIME, // Sort by actual order expiration type SORT_BY_PEND_REQ_ACTUAL_EXPIRATION, // Sort by actual order lifetime //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_REQ_ACTION, // Sort by a type of a performed action in the request structure SORT_BY_PEND_REQ_MQL_REQ_TYPE, // Sort by an order type in the request structure SORT_BY_PEND_REQ_MQL_REQ_MAGIC, // Sort by an EA stamp (magic number ID) in the request structure SORT_BY_PEND_REQ_MQL_REQ_ORDER, // Sort by an order ticket in the request structure SORT_BY_PEND_REQ_MQL_REQ_POSITION, // Sort by a position ticket in the request structure SORT_BY_PEND_REQ_MQL_REQ_POSITION_BY, // Sort by an opposite position ticket in the request structure SORT_BY_PEND_REQ_MQL_REQ_DEVIATION, // Sort by a maximum acceptable deviation from a requested price in the request structure SORT_BY_PEND_REQ_MQL_REQ_EXPIRATION, // Sort by an order expiration time (for ORDER_TIME_SPECIFIED type orders) in the request structure SORT_BY_PEND_REQ_MQL_REQ_TYPE_FILLING, // Sort by an order filling type in the request structure SORT_BY_PEND_REQ_MQL_REQ_TYPE_TIME, // Sort by order lifetime type in the request structure //--- Sort by real properties SORT_BY_PEND_REQ_PRICE_CREATE = FIRST_PREQ_DBL_PROP, // Sort by a price at the moment of a request generation SORT_BY_PEND_REQ_ACTUAL_VOLUME, // Sort by initial volume SORT_BY_PEND_REQ_ACTUAL_PRICE, // Sort by actual order price SORT_BY_PEND_REQ_ACTUAL_STOPLIMIT, // Sort by actual stoplimit order price SORT_BY_PEND_REQ_ACTUAL_SL, // Sort by actual stoploss order price SORT_BY_PEND_REQ_ACTUAL_TP, // Sort by actual takeprofit order price //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_REQ_VOLUME, // Sort by a requested volume of a deal in lots in the request structure SORT_BY_PEND_REQ_MQL_REQ_PRICE, // Sort by a price in the request structure SORT_BY_PEND_REQ_MQL_REQ_STOPLIMIT, // Sort by StopLimit order level in the request structure SORT_BY_PEND_REQ_MQL_REQ_SL, // Sort by StopLoss order level in the request structure SORT_BY_PEND_REQ_MQL_REQ_TP, // Sort by TakeProfit order level in the request structure //--- Sort by string properties //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_SYMBOL = FIRST_PREQ_STR_PROP, // Sort by a trading instrument name in the request structure SORT_BY_PEND_REQ_MQL_COMMENT // Sort by an order comment in the request structure }; //+------------------------------------------------------------------+
Jetzt wollen wir uns auf die Objekte konzentrieren.
Wir öffnen die Klassendatei des abstrakten Basisobjekts der schwebenden Anfrage \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh und nehmen die notwendigen Änderungen vor.
Im 'private' Teil der Klasse deklarieren wir die Klasse des Pause-Objekts, während im 'protected' Bereich zwei überladene Methoden zum Vergleich von Objekteigenschaften mit dem an die Methode übergebenen Wert und deklarieren, wobei der Satz von Methoden, der die Flags zurückgibt, die anzeigen, dass die schwebenden Anfragen von jedem der Order-/Positionsparameter abgeschlossen wurden:
//+------------------------------------------------------------------+ //| Abstract pending trading request class | //+------------------------------------------------------------------+ class CPendRequest : public CObject { private: MqlTradeRequest m_request; // Trade request structure CPause m_pause; // Pause class object //--- Copy trading request data void CopyRequest(const MqlTradeRequest &request); //--- Return the index of the array the request (1) double and (2) string properties are actually located at int IndexProp(ENUM_PEND_REQ_PROP_DOUBLE property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_PEND_REQ_PROP_STRING property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_DOUBLE_TOTAL; } protected: int m_digits; // Number of decimal places in a quote int m_digits_lot; // Number of decimal places in the symbol lot value bool m_is_hedge; // Hedging account flag long m_long_prop[PEND_REQ_PROP_INTEGER_TOTAL]; // Request integer properties double m_double_prop[PEND_REQ_PROP_DOUBLE_TOTAL]; // Request real properties string m_string_prop[PEND_REQ_PROP_STRING_TOTAL]; // Request string properties //--- Protected parametric constructor CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode); //--- Return (1) the magic number specified in the settings, (2) hedging account flag, (3) flag indicating the real property is equal to the value ushort GetMagicID(void) const { return ushort(this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC) & 0xFFFF);} bool IsHedge(void) const { return this.m_is_hedge; } bool IsEqualByMode(const int mode,const double value) const; bool IsEqualByMode(const int mode,const long value) const; //--- Return the flags indicating the pending request has completed changing each of the order/position parameters bool IsCompletedVolume(void) const; bool IsCompletedPrice(void) const; bool IsCompletedStopLimit(void) const; bool IsCompletedStopLoss(void) const; bool IsCompletedTakeProfit(void) const; bool IsCompletedTypeFilling(void) const; bool IsCompletedTypeTime(void) const; bool IsCompletedExpiration(void) const; public:
Im 'public' Teil der Klasse deklarieren wir die virtuelle Methode, die das Flag über den Abschluss der schwebenden Anfrage zurückgibt, die Methode, die das aktuelle Anfrageobjekt vollständig zurückgibt und die Methoden, die die Parameter des Pause-Objekts setzen und zurückgeben:
public: //--- Default constructor CPendRequest(){;} //--- Set request (1) integer, (2) real and (3) string properties void SetProperty(ENUM_PEND_REQ_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_PEND_REQ_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_PEND_REQ_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string request properties from the properties array long GetProperty(ENUM_PEND_REQ_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_PEND_REQ_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_PEND_REQ_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the request supporting the property virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property) { return true; } //--- Return the flag indicating the pending request has completed its work virtual bool IsCompleted(void) const { return false;} //--- Return itself CPendRequest *GetObject(void) { return &this;} //--- Compare CPendRequest objects by a specified property (to sort the lists by a specified request object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CPendRequest objects by all properties (to search for equal request objects) bool IsEqual(CPendRequest* compared_obj); //--- Return (1) the elapsed number of milliseconds, (2) waiting time completion from the pause object, //--- time of the (3) pause countdown start in milliseconds and (4) waiting time ulong PausePassed(void) const { return this.m_pause.Passed(); } bool PauseIsCompleted(void) const { return this.m_pause.IsCompleted(); } ulong PauseTimeBegin(void) const { return this.m_pause.TimeBegin(); } ulong PauseTimeWait(void) const { return this.m_pause.TimeWait(); } //--- Return the description (1) of an elapsed number of pause waiting seconds, //--- (2) pause countdown start time, as well as waiting time (3) in milliseconds and (4) in seconds string PausePassedDescription(void) const { return this.m_pause.PassedDescription(); } string PauseTimeBeginDescription(void) const { return this.m_pause.TimeBeginDescription(); } string PauseWaitingMSCDescription(void) const { return this.m_pause.WaitingMSCDescription(); } string PauseWaitingSecDescription(void) const { return this.m_pause.WaitingSECDescription(); } //+------------------------------------------------------------------+
Hier gibt die Methode IsCompleted() im übergeordneten Objekt immer 'false' zurück und wird für jedes abgeleitete Objekt einzeln implementiert. Die Methoden zur Arbeit mit dem Pausenobjekt rufen einfach die entsprechenden Methoden des von uns ganz am Anfang des Artikels untersuchten Objekts auf.
Hinzufügen der Methoden, die die tatsächlichen Auftragseigenschaften zurückgeben, zum Block der Methoden für einen vereinfachten Zugriff auf die Eigenschaften des Pausenobjekts:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the request object properties | //+------------------------------------------------------------------+ //--- Return (1) request structure, (2) status, (3) type, (4) price at the moment of the request generation, //--- (5) request generation time, (6) next attempt activation time, //--- (7) waiting time between requests, (8) current attempt index, //--- (9) number of attempts, (10) request ID //--- (11) result a request is based on, //--- (12) order ticket, (13) position ticket, (14) trading operation type MqlTradeRequest MqlRequest(void) const { return this.m_request; } ENUM_PEND_REQ_STATUS Status(void) const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS); } ENUM_PEND_REQ_TYPE TypeRequest(void) const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE); } double PriceCreate(void) const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE); } ulong TimeCreate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE); } ulong TimeActivate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE); } ulong WaitingMSC(void) const { return this.GetProperty(PEND_REQ_PROP_WAITING); } uchar CurrentAttempt(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT); } uchar TotalAttempts(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL); } uchar ID(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID); } int Retcode(void) const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE); } ulong Order(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER); } ulong Position(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION); } ENUM_TRADE_REQUEST_ACTIONS Action(void) const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION); } //--- Return the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime double ActualVolume(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME); } double ActualPrice(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE); } double ActualStopLimit(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT); } double ActualSL(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL); } double ActualTP(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP); } ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void) const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); } ENUM_ORDER_TYPE_TIME ActualTypeTime(void) const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME); } datetime ActualExpiration(void) const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION); }
Jedes Mal, wenn das nächste schwebende Anfrageobjekt geprüft wird, werden die entsprechenden Order-/Positionseigenschaften, für die das Anfrageobjekt erstellt wird, in diese Eigenschaften geschrieben. Diese Methoden ermöglichen es, die aktuellen Eigenschaften des entsprechenden Auftrags vom Anfrageobjekt zu erhalten.
Verbessern wir im gleichen Block die Methode zur Einstellung der Erstellungszeit für die schwebende Anfrage und die Methode zur Einstellung ihrer Wartezeit:
//--- Set (1) the price when creating a request, (2) request creation time, //--- (3) current attempt time, (4) waiting time between requests, //--- (5) current attempt index, (6) number of attempts, (7) ID, //--- (8) order ticket, (9) position ticket void SetPriceCreate(const double price) { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); } void SetTimeCreate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.m_pause.SetTimeBegin(time); } void SetTimeActivate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time); } void SetWaitingMSC(const ulong miliseconds) { this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds); this.m_pause.SetWaitingMSC(miliseconds); }
Zusätzlich zum Setzen der übergebenen Werte des schwebenden Anfrageobjekts setzen diese Methoden nun auch gleichzeitig diese Werte des Pause-Objekts.
Schreiben wir im gleichen Block die Methoden zum Setzen der aktuellen Order-Eigenschaften des Anfrageobjekts:
//--- Set the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime void SetActualVolume(const double volume) { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume); } void SetActualPrice(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price); } void SetActualStopLimit(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price); } void SetActualSL(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price); } void SetActualTP(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price); } void SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type); } void SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type); } void SetActualExpiration(const datetime expiration) { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration); } //+------------------------------------------------------------------+
Im Block der Anzeige von Objekteigenschaften definieren wir die Methoden, die die Beschreibungen der aktuellen Ordereigenschaften zurückgeben. Ganz am Ende fügen wir die virtuelle Methode hinzu, die den "Header" (kurze Objektbeschreibung) zurückgibt, der das schwebende Anfrageobjekt beschreibt:
//+------------------------------------------------------------------+ //| Descriptions of request object properties | //+------------------------------------------------------------------+ //--- Get description of a request (1) integer, (2) real and (3) string property string GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property); //--- Return the names of pending request object parameters string StatusDescription(void) const; string TypeRequestDescription(void) const; string IDDescription(void) const; string RetcodeDescription(void) const; string TimeCreateDescription(void) const; string TimeActivateDescription(void) const; string TimeWaitingDescription(void) const; string CurrentAttemptDescription(void) const; string TotalAttemptsDescription(void) const; string PriceCreateDescription(void) const; string TypeFillingActualDescription(void) const; string TypeTimeActualDescription(void) const; string ExpirationActualDescription(void) const; string VolumeActualDescription(void) const; string PriceActualDescription(void) const; string StopLimitActualDescription(void) const; string StopLossActualDescription(void) const; string TakeProfitActualDescription(void) const; //--- Return the names of trading request structures parameters in the request object string MqlReqActionDescription(void) const; string MqlReqMagicDescription(void) const; string MqlReqOrderDescription(void) const; string MqlReqSymbolDescription(void) const; string MqlReqVolumeDescription(void) const; string MqlReqPriceDescription(void) const; string MqlReqStopLimitDescription(void) const; string MqlReqStopLossDescription(void) const; string MqlReqTakeProfitDescription(void) const; string MqlReqDeviationDescription(void) const; string MqlReqTypeOrderDescription(void) const; string MqlReqTypeFillingDescription(void) const; string MqlReqTypeTimeDescription(void) const; string MqlReqExpirationDescription(void) const; string MqlReqCommentDescription(void) const; string MqlReqPositionDescription(void) const; string MqlReqPositionByDescription(void) const; //--- Display (1) description of request properties (full_prop=true - all properties, false - only supported ones), //--- (2) short message about the request, (3) short request name (2 and 3 - implementation in the class descendants) void Print(const bool full_prop=false); virtual void PrintShort(void){;} virtual string Header(void){return NULL;} }; //+------------------------------------------------------------------+
Die Methoden zur Beschreibung der eigentlichen Eigenschaften des Orderobjekts sollen später beschrieben werden. Die virtuelle Methode des Headers der schwebenden Anfrage liefert hier NULL zurück und soll in den abgeleiteten Objekten implementiert werden.
Im Klassenkonstruktor wird die Initialisierung des Pause-Objekts mit den Werten der Zeit der Erstellung des Anfrageobjekts und seiner Wartezeit angehängt:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) { this.CopyRequest(request); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS); int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)); this.m_digits_lot=(dg==0 ? 1 : dg); this.SetProperty(PEND_REQ_PROP_STATUS,status); this.SetProperty(PEND_REQ_PROP_ID,id); this.SetProperty(PEND_REQ_PROP_RETCODE,retcode); this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST); this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING)); } //+------------------------------------------------------------------+
Daher sind unmittelbar nach dem Anlegen eines neuen Objekts einer schwebenden Anfrage die Warte-Startzeit und die Pausendauer sofort auf dessen Pausenobjekt zu setzen.
Außerhalb des Klassenkörpers implementieren wir die Methoden, die die Flags zurückgeben, die anzeigen, dass die Real- und Ganzzahleigenschaften des Anfrageobjekts gleich dem an die Methoden übergebenen Wert sind:
//+---------------------------------------------------------------------+ //| Return the flag indicating the real value is equal to the passed one| //+---------------------------------------------------------------------+ bool CPendRequest::IsEqualByMode(const int mode,const double value) const { CPendRequest *req=new CPendRequest(); if(req==NULL) return false; req.SetProperty((ENUM_PEND_REQ_PROP_DOUBLE)mode,value); bool res=!this.Compare(req,mode); delete req; return res; } //+------------------------------------------------------------------------+ //| Return the flag indicating the integer value is equal to the passed one| //+------------------------------------------------------------------------+ bool CPendRequest::IsEqualByMode(const int mode,const long value) const { CPendRequest *req=new CPendRequest(); if(req==NULL) return false; req.SetProperty((ENUM_PEND_REQ_PROP_INTEGER)mode,value); bool res=!this.Compare(req,mode); delete req; return res; } //+------------------------------------------------------------------+
Die Methode erhält den Modus für den Vergleich der Objekteigenschaft und den an die Methode übergebenen Wert.
Der Modus umfasst die Angabe eines der Werte der Objekteigenschaften aus der Enumeration seiner realen (ENUM_PEND_REQ_PROP_DOUBLE) oder ganzzahligen Eigenschaften (ENUM_PEND_REQ_PROP_INTEGER). Dies ist die Eigenschaft, die beim Vergleich mit dem an die Methode übergebenen Wert verwendet wird.
In der Methode erzeugen wir ein neues temporäres, schwebendes Anfrageobjekt. Die entsprechende Eigenschaft erhält den der Methode übergebenen Wert .
Die Methode Compare() des Basisobjekts CObject der Standardbibliothek gibt 0 zurück, was Gleichheit bedeutet. Die Methode ist jedoch virtuell und wird in den abgeleiteten Klassen implementiert. Hier ist die Methode Compare() so implementiert, dass sie 1 zurückgibt, wenn der aktuelle Objektwert den entsprechenden Vergleichswert überschreitet. -1 wird zurückgegeben, wenn der Wert niedriger ist, und 0, wenn beide gleich sind.
Wenn also die verglichenen Werte gleich sind, erhält die Variable res den Wert false.
Allerdings fügen wir dort auch die logische Negation eines Verifikationsergebnisses hinzu. Wenn die Vergleichsmethode also 0 zurückgibt, dann ist !false gleich true. Im Fall von 1 oder -1 ist !true gleich false.
Nach dem Schreiben des Vergleichsergebnisses wird das temporäre Objekt entfernt und das Vergleichsergebnis zurückgegeben.
Die Methoden geben das Vergleichsergebnis der Flags der aktuellen Order zurück und fordern die Objekteigenschaftswerte nach ihren Typen an:
//+------------------------------------------------------------------+ //| Return the flag of a successful volume change | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedVolume(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_VOLUME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)); } //+------------------------------------------------------------------+ //| Return the flag of a successful price modification | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedPrice(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_PRICE,this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE)); } //+-------------------------------------------------------------------+ //| Return the flag of a successful StopLimit order price modification| //+-------------------------------------------------------------------+ bool CPendRequest::IsCompletedStopLimit(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_STOPLIMIT,this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)); } //+------------------------------------------------------------------+ //| Return the flag of a successful StopLoss order modification | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedStopLoss(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_SL,this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)); } //+------------------------------------------------------------------+ //| Return the flag of a successful TakeProfit order modification | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedTakeProfit(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TP,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)); } //+------------------------------------------------------------------+ //| Return the flag of a successful order filling type modification | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedTypeFilling(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_FILLING)); } //+-------------------------------------------------------------------+ //| Return the flag of a successful order expiration type modification| //+-------------------------------------------------------------------+ bool CPendRequest::IsCompletedTypeTime(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_TIME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_TIME)); } //+------------------------------------------------------------------+ //| Return the flag of a successful order lifetime modification | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedExpiration(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_EXPIRATION,this.GetProperty(PEND_REQ_PROP_MQL_REQ_EXPIRATION)); } //+------------------------------------------------------------------+
Die Methoden geben das Ergebnis des Vergleichs zweier geeigneter Eigenschaften zurück — der aktuellen und der im Objekt festgelegten, mit der soeben betrachteten Vergleichsmethode.
Fügen wir die neuen Eigenschaften in die Implementierung der Methoden ein, die die Beschreibungen der Objekteigenschaften zurückgeben:
//+------------------------------------------------------------------+ //| Return the description of a request integer property | //+------------------------------------------------------------------+ string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property) { return ( property==PEND_REQ_PROP_STATUS ? this.StatusDescription() : property==PEND_REQ_PROP_TYPE ? this.TypeRequestDescription() : property==PEND_REQ_PROP_ID ? this.IDDescription() : property==PEND_REQ_PROP_RETCODE ? this.RetcodeDescription() : property==PEND_REQ_PROP_TIME_CREATE ? this.TimeCreateDescription() : property==PEND_REQ_PROP_TIME_ACTIVATE ? this.TimeActivateDescription() : property==PEND_REQ_PROP_WAITING ? this.TimeWaitingDescription() : property==PEND_REQ_PROP_CURRENT_ATTEMPT ? this.CurrentAttemptDescription() : property==PEND_REQ_PROP_TOTAL ? this.TotalAttemptsDescription() : property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING ? this.TypeFillingActualDescription() : property==PEND_REQ_PROP_ACTUAL_TYPE_TIME ? this.TypeTimeActualDescription() : property==PEND_REQ_PROP_ACTUAL_EXPIRATION ? this.ExpirationActualDescription() : //--- MqlTradeRequest property==PEND_REQ_PROP_MQL_REQ_ACTION ? this.MqlReqActionDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE ? this.MqlReqTypeOrderDescription() : property==PEND_REQ_PROP_MQL_REQ_MAGIC ? this.MqlReqMagicDescription() : property==PEND_REQ_PROP_MQL_REQ_ORDER ? this.MqlReqOrderDescription() : property==PEND_REQ_PROP_MQL_REQ_POSITION ? this.MqlReqPositionDescription() : property==PEND_REQ_PROP_MQL_REQ_POSITION_BY ? this.MqlReqPositionByDescription() : property==PEND_REQ_PROP_MQL_REQ_DEVIATION ? this.MqlReqDeviationDescription() : property==PEND_REQ_PROP_MQL_REQ_EXPIRATION ? this.MqlReqExpirationDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ? this.MqlReqTypeFillingDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME ? this.MqlReqTypeTimeDescription() : ::EnumToString(property) ); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the description of the request real property | //+------------------------------------------------------------------+ string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property) { return ( property==PEND_REQ_PROP_PRICE_CREATE ? this.PriceCreateDescription() : property==PEND_REQ_PROP_ACTUAL_VOLUME ? this.VolumeActualDescription() : property==PEND_REQ_PROP_ACTUAL_PRICE ? this.PriceActualDescription() : property==PEND_REQ_PROP_ACTUAL_STOPLIMIT ? this.StopLimitActualDescription() : property==PEND_REQ_PROP_ACTUAL_SL ? this.StopLossActualDescription() : property==PEND_REQ_PROP_ACTUAL_TP ? this.TakeProfitActualDescription() : //--- MqlTradeRequest property==PEND_REQ_PROP_MQL_REQ_VOLUME ? this.MqlReqVolumeDescription() : property==PEND_REQ_PROP_MQL_REQ_PRICE ? this.MqlReqPriceDescription() : property==PEND_REQ_PROP_MQL_REQ_STOPLIMIT ? this.MqlReqStopLimitDescription() : property==PEND_REQ_PROP_MQL_REQ_SL ? this.MqlReqStopLossDescription() : property==PEND_REQ_PROP_MQL_REQ_TP ? this.MqlReqTakeProfitDescription() : ::EnumToString(property) ); } //+------------------------------------------------------------------+
Die ihr übergebene Objekteigenschaft wird in den Methoden überprüft und ihre Beschreibung wird mit den entsprechenden Methoden zurückgegeben.
Die Methoden geben Beschreibungen der tatsächlichen Auftragseigenschaften zurück, die in das Anfrageobjekt geschrieben wurden:
//+------------------------------------------------------------------+ //| Return the description of the actual order filling mode | //+------------------------------------------------------------------+ string CPendRequest::TypeFillingActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING)+": "+OrderTypeFillingDescription((ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING)); } //+------------------------------------------------------------------+ //| Return the actual order lifetime type | //+------------------------------------------------------------------+ string CPendRequest::TypeTimeActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME)+": "+OrderTypeTimeDescription((ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME)); } //+------------------------------------------------------------------+ //| Return the description of the actual order lifetime | //+------------------------------------------------------------------+ string CPendRequest::ExpirationActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION)+": "+ (this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)>0 ? ::TimeToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the description of the actual order volume | //+------------------------------------------------------------------+ string CPendRequest::VolumeActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME),this.m_digits_lot); } //+------------------------------------------------------------------+ //| Return the description of the actual order price | //+------------------------------------------------------------------+ string CPendRequest::PriceActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE),this.m_digits); } //+------------------------------------------------------------------+ //| Return the description of the actual StopLimit order price | //+------------------------------------------------------------------+ string CPendRequest::StopLimitActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT),this.m_digits); } //+------------------------------------------------------------------+ //| Return the description of the actual StopLoss order price | //+------------------------------------------------------------------+ string CPendRequest::StopLossActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_SL),this.m_digits); } //+------------------------------------------------------------------+ //| Return the description of the actual TakeProfit order price | //+------------------------------------------------------------------+ string CPendRequest::TakeProfitActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_TP),this.m_digits); } //+------------------------------------------------------------------+
Die Methoden werden zur Vorbereitung und Rückgabe des Nachrichtentextes verwendet, der der von der Methode zurückgegebenen Eigenschaft entspricht.
Dies sind alle Änderungen des Basisobjekts der abstrakten Anfrage.
Nun wollen wir Änderungen an den abgeleiteten Klassen des Basis-Anfrageobjekts vornehmen.
Wir öffnen die Datei der Klasse der Position, die das Objekt der schwebenden Anfrage öffnet \MQL5\Include\DoEasy\Objects\PendRequest\PendReqOpen.mqh und nehmen die Änderungen vor.
Fügen wir die Definition der virtuellen Methode hinzu, die den kurzen Anfragenamen zurückgibt:
//+------------------------------------------------------------------+ //| Pending request for opening a position | //+------------------------------------------------------------------+ class CPendReqOpen : public CPendRequest { public: //--- Constructor CPendReqOpen(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_OPEN,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Und die Umsetzung:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqOpen::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Klasse der schwebenden Anfrage für die Änderung der Stop-Orders einer Position (Datei PendReqSLTP.mqh):
//+------------------------------------------------------------------+ //| Pending request to modify position stop orders | //+------------------------------------------------------------------+ class CPendReqSLTP : public CPendRequest { public: //--- Constructor CPendReqSLTP(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_SLTP,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Return the flag indicating the pending request has completed its work virtual bool IsCompleted(void) const; //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Fügen wir die neuen Eigenschaften zu den Methoden hinzu, die das Flag zurückgeben, das anzeigt, ob das Objekt einige seiner Eigenschaften unterstützt:
//+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { if(property==PEND_REQ_PROP_MQL_REQ_POSITION_BY || property==PEND_REQ_PROP_MQL_REQ_ORDER || property==PEND_REQ_PROP_MQL_REQ_EXPIRATION || property==PEND_REQ_PROP_MQL_REQ_DEVIATION || property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING || property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME || property==PEND_REQ_PROP_ACTUAL_EXPIRATION || property==PEND_REQ_PROP_ACTUAL_TYPE_TIME || property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING ) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property) { if(property==PEND_REQ_PROP_PRICE_CREATE || property==PEND_REQ_PROP_ACTUAL_SL || property==PEND_REQ_PROP_ACTUAL_TP || property==PEND_REQ_PROP_MQL_REQ_SL || property==PEND_REQ_PROP_MQL_REQ_TP ) return true; return false; } //+------------------------------------------------------------------+
Die virtuelle Methode, die das Flag der schwebenden Anfrage zurückgibt, hat ihre Arbeit beendet:
//+----------------------------------------------------------------------+ //| Return the flag indicating the pending request has completed its work| //+----------------------------------------------------------------------+ bool CPendReqSLTP::IsCompleted(void) const { bool res=true; res &= this.IsCompletedStopLoss(); res &= this.IsCompletedTakeProfit(); return res; } //+------------------------------------------------------------------+
Fügen wir hier die Ergebnisse der Verifizierung des Abschlusses der Änderungen von StopLoss und TakeProfit zu der Variablen res hinzu. Wenn mindestens eine der Verifizierungen false ergibt, ist das Gesamtergebnis false. Das Endergebnis der Methode wird zurückgegeben.
Die Methode gibt den Kurznamen einer Anfrage zurück:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqSLTP::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Klasse der schwebenden Anfragen zum Schließen einer Position (Datei PendReqClose.mqh):
//+------------------------------------------------------------------+ //| Pending request to close a position | //+------------------------------------------------------------------+ class CPendReqClose : public CPendRequest { public: //--- Constructor CPendReqClose(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_CLOSE,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Return the flag indicating the pending request has completed its work virtual bool IsCompleted(void) const; //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Die Methode, die das Flag für die Anfrage zurückgibt, hat ihre Arbeit beendet:
//+----------------------------------------------------------------------+ //| Return the flag indicating the pending request has completed its work| //+----------------------------------------------------------------------+ bool CPendReqClose::IsCompleted(void) const { return this.IsCompletedVolume(); } //+------------------------------------------------------------------+
Die Methode gibt den Kurznamen einer Anfrage zurück:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqClose::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Klasse der schwebenden Anfrage für das Platzieren einer Pending-Order (Datei PendReqPlace.mqh):
//+------------------------------------------------------------------+ //| Pending request to place a pending order | //+------------------------------------------------------------------+ class CPendReqPlace : public CPendRequest { public: //--- Constructor CPendReqPlace(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_PLACE,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Die Methode gibt den Kurznamen einer Anfrage zurück:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqPlace::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Klasse der schwebenden Anfrage für das Ändern der Parameter einer Pending-Order (Datei PendReqModify.mqh):
//+------------------------------------------------------------------+ //| Pending request to modify pending order parameters | //+------------------------------------------------------------------+ class CPendReqModify : public CPendRequest { public: //--- Constructor CPendReqModify(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_MODIFY,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Return the flag indicating the pending request has completed its work virtual bool IsCompleted(void) const; //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Die Methode, die das Flag für die Anfrage zurückgibt, hat ihre Arbeit beendet:
//+----------------------------------------------------------------------+ //| Return the flag indicating the pending request has completed its work| //+----------------------------------------------------------------------+ bool CPendReqModify::IsCompleted(void) const { bool res=true; res &= this.IsCompletedPrice(); res &= this.IsCompletedStopLimit(); res &= this.IsCompletedStopLoss(); res &= this.IsCompletedTakeProfit(); res &= this.IsCompletedTypeFilling(); res &= this.IsCompletedTypeTime(); res &= this.IsCompletedExpiration(); return res; } //+------------------------------------------------------------------+
Die Methode gibt den Kurznamen einer Anfrage zurück:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqModify::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Die Klasse der schwebenden Anfrage für das Entfernen einer Pending-Order (Datei PendReqRemove.mqh):
//+------------------------------------------------------------------+ //| Pending request to remove a pending order | //+------------------------------------------------------------------+ class CPendReqRemove : public CPendRequest { public: //--- Constructor CPendReqRemove(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_REMOVE,id,price,time,request,retcode) {} //--- Supported deal properties (1) real, (2) integer virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Display a brief message with request data and (2) a short request name in the journal virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Die Methode gibt den Kurznamen einer Anfrage zurück:
//+------------------------------------------------------------------+ //| Return the short request name | //+------------------------------------------------------------------+ string CPendReqRemove::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Wie wir aus den Codes der Klassen und den entsprechenden Methoden sehen können, ist hier alles recht trivial — jede Methode verwendet nur ihre inhärenten abstrakten Eigenschaften des Anfrageobjekts. Der kurzer Name einer Anfrage besteht aus den entsprechenden Textmeldungen.
Damit ist die Verbesserung der abgeleiteten Klassen der Basisklasse des abstrakten Objekts der schwebenden Anfragen abgeschlossen.
Die Klasse für die Verwaltung von schwebenden Anfrageobjekten
Kommen wir nun endlich zu der Klasse für die Verwaltung von schwebenden Anfragen.
Wie bereits oben erwähnt, soll die Klasse von der Handelsklasse CTrading abgeleitet werden, da alle Daten und Methoden in den Klassen CTrading, CPendRequest und neu hinzugefügten CTradingControl eng miteinander verknüpft sind. In der aktuellen Implementierung ist die Klasse recht klein und arbeitet im Timer. Der gesamte Code soll mit geringfügigen Änderungen aus dem Timer-Code der Klasse CTrading in den Timer verschoben werden.
Erstellen wir die neue Klasse CTradingControl in \MQL5\Include\DoEasy\TradingControl.mqh. Setzen wir die Klasse CTrading als Basisklasse, wenn wir sie erstellen. Stellen wir sicher, dass wir die Datei Trading.mqh direkt nach der Erstellung der Klasse in diese einbinden:
//+------------------------------------------------------------------+ //| PendReqControl.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Trading.mqh" //+------------------------------------------------------------------+ //| Class for managing pending trading requests | //+------------------------------------------------------------------+ class CTradingControl : public CTrading { private: //--- Set actual order/position data to a pending request object void SetActualProperties(CPendRequest *req_obj,const COrder *order); public: //--- Return itself CTradingControl *GetObject(void) { return &this; } //--- Timer virtual void OnTimer(void); //--- Constructor CTradingControl(); }; //+------------------------------------------------------------------+
Die Klasse verfügt über die 'private' Methode SetActualProperties(), die die tatsächlichen Order-/Positionsdaten auf ein schwebendes Anfrageobjekt überträgt.
Im 'public' Bereich gibt die Methode GetObject() den Zeiger auf das gesamte Objekt zur Verwaltung schwebender Anfragen zurück, während die virtuelle Methode OnTimer() ein Zeitgeber der Klasse zur Verwaltung der schwebenden Anfragen ist.
Im Klassenkonstruktor löschen wir die Liste der schwebenden Anfragen und setzen das Flag für die sortierte Liste:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradingControl::CTradingControl() { this.m_list_request.Clear(); this.m_list_request.Sort(); } //+------------------------------------------------------------------+
Die Liste gehört zur Klasse CTrading, da dies die Handelsklasse ist, in der schwebenden Handelsanfragen erstellt werden.
Implementieren wir die Methode zum Setzen der tatsächlichen Auftragsdaten auf ein Anfrageobjekt außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Set order/position data to a pending request object | //+------------------------------------------------------------------+ void CTradingControl::SetActualProperties(CPendRequest *req_obj,const COrder *order) { req_obj.SetActualExpiration(order.TimeExpiration()); req_obj.SetActualPrice(order.PriceOpen()); req_obj.SetActualSL(order.StopLoss()); req_obj.SetActualStopLimit(order.PriceStopLimit()); req_obj.SetActualTP(order.TakeProfit()); req_obj.SetActualTypeFilling(order.TypeFilling()); req_obj.SetActualTypeTime(order.TypeTime()); req_obj.SetActualVolume(order.Volume()); } //+------------------------------------------------------------------+
Die Methode erhält den Zeiger auf ein schwebendes Anfrageobjekt und den Zeiger auf ein Orderobjekt. Als Nächstes werden die entsprechenden Auftragseigenschaftswerte im Anfrageobjekt mit den oben betrachteten Methoden gesetzt.
Der Klassenzeitgeber verfolgt alle vorhandenen Anfrageobjekt in der Liste der schwebenden Anfragen. Die Laufzeit jeder schwebenden Anfrage wird überprüft. Wenn sie abgelaufen ist, wird die Anfrage entfernt. Wenn die Anfrage bereits aktiviert wurde (es gibt ein entsprechendes Handelsereignis in der Konto-Historie oder eine Order/Position mit der ID einer bestimmten schwebenden Anfrage in der Magicnummer), werden solche Anfragen als vollständig ausgeführt betrachtet und aus der Liste entfernt.
Wenn die Anfrage noch nicht aktiviert ist und ihre Aktivierungszeit abgelaufen ist, wird ein Handelsauftrag gemäß der schwebenden Anfrage an den Server gesendet.
Die Methode umfasst die Überprüfung der Fähigkeit, Handelsoperationen auf der Terminalseite durchzuführen — die Schaltfläche AutoTrading und die Option Auto Trading zulassen in den EA-Einstellungen. Wenn eine schwebende Handelsanfrage nach dem Fehlercode 10027 erstellt wurde (AutoTrading ist im Terminal deaktiviert) und die Fehlerursache von einem Händler beseitigt wurde (durch Klicken auf die Schaltfläche AutoTrading oder durch Aktivieren der Option Allow AutoTrading in den EA-Einstellungen), wird die schwebende Anfrage sofort bearbeitet. Eine neue Aktivierungszeit für die schwebende Anfrage wird so eingestellt, dass die Aktivierung durchgeführt wird, da der Nutzer den Fehler behoben hat, und es ist nicht notwendig zu warten. Stattdessen sollte der Auftrag sofort an den Server gesendet werden, um spätere Anfragen zu vermeiden.
Da die Methode recht groß ist, habe ich mein Bestes getan, um in den Code-Kommentaren alle Aktionen, die innerhalb des Zeitgebers der Klasse zur Verwaltung schwebender Anfragen durchgeführt werden, gründlich zu beschreiben.
Der Timer der Klasse für die Verwaltung schwebender Anfragen:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTradingControl::OnTimer(void) { //--- In a loop by the list of pending requests int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- receive the next request object CPendRequest *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- get the request structure and the symbol object a trading operation should be performed for MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) continue; //--- Set the flag disabling trading in the terminal by two properties simultaneously //--- (the AutoTrading button in the terminal and the Allow Automated Trading option in the EA settings) //--- If any of the two properties is 'false', the flag is 'false' as well bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED); //--- If a request object is based on the error code if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR) { //--- if the error has been caused by trading disabled on the terminal side and has been eliminated if(req_obj.Retcode()==10027 && terminal_trade_allowed) { //--- if the current attempt has not exceeded the defined number of trading attempts yet if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1) { //--- Set the request creation time equal to its creation time minus waiting time, i.e. send the request immediately //--- Also, decrease the number of a successful attempt since during the next attempt, its number is increased, and if this is the last attempt, //--- it is not executed. However, this is related to fixing the error cause by a user, which means we need to give more time for the last attempt req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC()); req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0)); } } } //--- if the current attempt exceeds the defined number of trading attempts, //--- or the current time exceeds the waiting time of all attempts //--- remove the current request object and move on to the next one if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts())) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); this.m_list_request.Delete(i); continue; } //--- If this is a position opening or placing a pending order if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING) { //--- Get the pending request ID uchar id=this.GetPendReqID((uint)request.magic); //--- Get the list of orders/positions containing the order/position with the pending request ID CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- If the order/position is present, the request is handled: remove it and proceed to the next if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } } //--- Otherwise: full and partial position closure, removing an order, modifying order parameters and position stop orders else { CArrayObj *list=NULL; //--- if this is a position closure, including a closure by an opposite one if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY) { //--- Get a position with the necessary ticket from the list of open positions list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- If the market has no such position - the request is handled: remove it and proceed to the next one if(list.Total()==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } //--- Otherwise, if the position still exists, this is a partial closure else { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) continue; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this event is a partial closure or there was a partial closure when closing by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { //--- If a position ticket in a trading event coincides with the ticket in a pending trading request if(event.PositionID()==req_obj.Position()) { //--- Get a position object from the list of market positions CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Set actual position data to the pending request object this.SetActualProperties(req_obj,order); //--- If (executed request volume + unexecuted request volume) is equal to the requested volume in a pending request - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one if(::CheckPointer(req_obj)==POINTER_INVALID) continue; } } //--- If this is a modification of position stop orders if(req_obj.Action()==TRADE_ACTION_SLTP) { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) continue; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this is a change of the position's stop orders if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT) { //--- If a position ticket in a trading event coincides with the ticket in a pending trading request if(event.PositionID()==req_obj.Position()) { //--- Get a position object from the list of market positions CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Set actual position data to the pending request object this.SetActualProperties(req_obj,order); //--- If all modifications have worked out - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one if(::CheckPointer(req_obj)==POINTER_INVALID) continue; } //--- If this is a pending order removal if(req_obj.Action()==TRADE_ACTION_REMOVE) { //--- Get the list of removed pending orders from the historical list list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- Leave a single order with the necessary ticket in the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL); //--- If the order is present, the request is handled: remove it and proceed to the next if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } } //--- If this is a pending order modification if(req_obj.Action()==TRADE_ACTION_MODIFY) { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) continue; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this event involves any change of modified pending order parameters if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT) { //--- If an order ticket in a trading event coincides with the ticket in a pending trading request if(event.TicketOrderEvent()==req_obj.Order()) { //--- Get an order object from the list CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(0); if(order==NULL) break; //--- Set actual order data to the pending request object this.SetActualProperties(req_obj,order); //--- If all modifications have worked out - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } } } //--- Exit if the pending request object has been removed after checking its operation if(::CheckPointer(req_obj)==POINTER_INVALID) return; //--- Set the request activation time in the request object req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- If the current time is less than the request activation time, //--- this is not the request time - move on to the next request in the list if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) continue; //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Display the number of a trading attempt in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":"); req_obj.PrintShort(); } //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Opening/closing a position case TRADE_ACTION_DEAL : //--- If no ticket is present in the request structure - this is opening a position if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- If the ticket is present in the request structure - this is a position closure else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Modify StopLoss/TakeProfit position case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Close by an opposite one case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Place a pending order case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Modify a pending order case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Remove a pending order case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } } //+------------------------------------------------------------------+
Für den Moment ist dies alles, was wir in der Klasse für die Verwaltung der schwebenden Anfragen tun müssen. Wenn Sie Fragen zur Funktionsweise des Timers der Klasse haben, stellen Sie diese in den Kommentaren.
Nun wollen wir die Änderungen am Basisobjekt der Klassenbibliothek CEngine vornehmen.
Jetzt wird ein Kind verwendet (die Klasse zur Verwaltung der schwebenden Anfragen) und nicht mehr die Handelsklasse selbst. Statt im Code der Handelsklassendatei:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Trading.mqh" //+------------------------------------------------------------------+
Binden wir die Datei der Klasse für die Verwaltung der schwebenden Handelsanfragen ein:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+
Statt des Handelsklassenobjekts
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CResourceCollection m_resource; // Resource list CTrading m_trading; // Trading class object CArrayObj m_list_counters; // List of timer counters
werden wir das Objekt der Klasse für die Verwaltung der Handelsanfragen verwenden:
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CArrayObj m_list_counters; // List of timer counters
Dies sind erst einmal alle Änderungen in der Bibliothek.
Tests
Um die neue Bibliotheksversion zu testen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part30\ unter dem Namen TestDoEasyPart30.mq5.
Kompilieren Sie den EA und starten Sie ihn auf dem Chart eines Demo-Kontos.
Wir müssen das Verhalten der Objekte der schwebenden Anfragen überprüfen, falls die Schaltfläche AutoTrading im Terminal deaktiviert ist.
Stellen wir in den EA-Einstellungen die Abstände von StopLoss und TakeProfit auf 0 ein.
Die folgenden beiden anpassbaren Parameter werden dafür verwendet:
- StopLoss in Punkten
- TakeProfit in Punkten.
Danach deaktivieren wir das AutoTrading, indem wir auf die Schaltfläche AutoTrading im Terminal klicken und versuchen, eine Handelsanfrage (z.B. zum Setzen einer Pending-SellLimit-Order) über die Schaltfläche des EA-Handelspanels zu senden.
Dies führt dazu, dass der durch den Handel verursachte Fehler durch das Terminal deaktiviert wird, die schwebende Anfrage wird erstellt.
Klicken wir sofort auf AutoTrading, um den Fehler zu beheben.
Die neu erstellte Handelsanfrage sollte sofort aktiviert werden. Die Handelsanfrage soll an den Server gesendet werden, und nach ihre Ausführung , wird das Anfrageobjekt entfernt:
2019.12.27 04:16:28.894 automated trading is disabled 2019.12.27 04:16:33.177 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request: 2019.12.27 04:16:33.177 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:16:33.177 Correction of trade request parameters ... 2019.12.27 04:16:33.178 Pending request created #1: 2019.12.27 04:16:33.178 Pending request to place a pending order: 2019.12.27 04:16:33.178 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088 2019.12.27 04:16:33.178 - Pending request ID: #1, Created 2019.12.26 23:16:30.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:18:10.003 2019.12.27 04:16:33.178 2019.12.27 04:16:37.397 automated trading is enabled 2019.12.27 04:16:37.472 Retry trading attempt #1: 2019.12.27 04:16:37.472 Pending request to place a pending order: 2019.12.27 04:16:37.472 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088 2019.12.27 04:16:37.472 - Pending request ID: #1, Created 2019.12.26 23:16:10.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:17:50.003 2019.12.27 04:16:37.472 2019.12.27 04:16:37.977 - Pending order placed: 2019.12.26 23:16:38.325 - 2019.12.27 04:16:37.977 GBPUSD Placed 0.10 Pending order Sell Limit #500442708 at price 1.30088, Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:16:37.979 OnDoEasyEvent: Pending order placed 2019.12.27 04:16:38.024 Pending request to place a pending order, ID #1: Deleted due completed
Nun müssen wir die Möglichkeit prüfen, die Stop-Orders durch die neu platzierte Pending-Order zu ändern (da sie ohne diese platziert wurde).
AutoTrading wird deaktiviert und dann klicken wir auf Set StopLoss im EA-Testhandelspanel.
Wir rufen den Fehler ab, der durch das Deaktivieren von Handelsoperationen durch das Terminal verursacht wurde.
Eine schwebende Anfrage zur Änderung der Parameter der Pending-Order wird erstellt .
Nach einem weiteren Handelsversuch, der durch eine schwebende Anfrage gesendet wurde, aktivieren wir das AutoTrading im Terminal. StopLoss wird für die Pending-Order gesetzt, während die schwebende Anfrage selbst aufgrund ihrer Ausführung gelöscht wird:
2019.12.27 04:24:11.671 automated trading is disabled 2019.12.27 04:24:16.653 CTrading::ModifyOrder<int,int,double,int>: Invalid request: 2019.12.27 04:24:16.653 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:24:16.653 Correction of trade request parameters ... 2019.12.27 04:24:16.653 Pending request created #1: 2019.12.27 04:24:16.653 Pending request to modify pending order parameters: 2019.12.27 04:24:16.653 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:16.653 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:16.653 - Order expiration type: Good till cancel order 2019.12.27 04:24:16.653 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270 2019.12.27 04:24:16.653 2019.12.27 04:24:25.803 Retry trading attempt #1: 2019.12.27 04:24:25.803 Pending request to modify pending order parameters: 2019.12.27 04:24:25.803 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:25.803 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:25.803 - Order expiration type: Good till cancel order 2019.12.27 04:24:25.803 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270 2019.12.27 04:24:25.803 2019.12.27 04:24:29.770 automated trading is enabled 2019.12.27 04:24:30.022 Retry trading attempt #1: 2019.12.27 04:24:30.022 Pending request to modify pending order parameters: 2019.12.27 04:24:30.022 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:30.022 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:30.022 - Order expiration type: Good till cancel order 2019.12.27 04:24:30.022 - Pending request ID: #1, Created 2019.12.26 23:23:40.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:20.270 2019.12.27 04:24:30.022 2019.12.27 04:24:30.405 - Modified order StopLoss: 2019.12.26 23:16:38.325 - 2019.12.27 04:24:30.405 GBPUSD Pending order Sell Limit #500442708: Modified order StopLoss: [0.00000 --> 1.30208], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:24:30.405 OnDoEasyEvent: Modified order StopLoss 2019.12.27 04:24:30.601 Pending request to modify pending order parameters, ID #1: Deleted due completed
Wie wir sehen können, hat nach der Aktivierung des Autohandels eine wiederholte Anfrage des schwebenden Anfrageobjekts die gleiche Nummer wie beim ersten Versuch einer wiederholten Handelsanfrage, d.h. die Logik des Hinzufügens eines weiteren Versuchs, wenn die Fehlerursache durch einen Benutzer beseitigt wird (durch die Aktivierung des Autohandels im Terminal), hat korrekt funktioniert.
Jetzt wollen wir das gleiche für die Einstellung von TakeProfit tun.
Alles hat wieder korrekt funktioniert (nach dem zweiten Handelsversuch des schwebende Anfrageobjekts), TakeProfit wurde durch das Objekt der schwebenden Anfrage gesetzt und diese Anfrage wurde dann entfernt:
2019.12.27 04:32:46.843 automated trading is disabled 2019.12.27 04:32:50.810 CTrading::ModifyOrder<int,int,int,double>: Invalid request: 2019.12.27 04:32:50.810 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:32:50.810 Correction of trade request parameters ... 2019.12.27 04:32:50.810 Pending request created #1: 2019.12.27 04:32:50.810 Pending request to modify pending order parameters: 2019.12.27 04:32:50.810 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:32:50.810 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:32:50.810 - Order expiration type: Good till cancel order 2019.12.27 04:32:50.810 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:32:50.810 2019.12.27 04:33:08.782 Retry trading attempt #1: 2019.12.27 04:33:08.782 Pending request to modify pending order parameters: 2019.12.27 04:33:08.782 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:08.782 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:08.782 - Order expiration type: Good till cancel order 2019.12.27 04:33:08.782 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:33:08.782 2019.12.27 04:33:29.984 Retry trading attempt #2: 2019.12.27 04:33:29.984 Pending request to modify pending order parameters: 2019.12.27 04:33:29.984 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:29.984 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:29.984 - Order expiration type: Good till cancel order 2019.12.27 04:33:29.984 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:33:29.984 2019.12.27 04:33:31.999 automated trading is enabled 2019.12.27 04:33:32.250 Retry trading attempt #2: 2019.12.27 04:33:32.250 Pending request to modify pending order parameters: 2019.12.27 04:33:32.250 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:32.250 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:32.250 - Order expiration type: Good till cancel order 2019.12.27 04:33:32.250 - Pending request ID: #1, Created 2019.12.26 23:32:27.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:07.943 2019.12.27 04:33:32.250 2019.12.27 04:33:32.352 - Modified order TakeProfit: 2019.12.26 23:24:26.509 - 2019.12.27 04:33:32.352 GBPUSD Pending order Sell Limit #500442708: Modified order TakeProfit: [0.00000 --> 1.29888], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:33:32.352 OnDoEasyEvent: Modified order TakeProfit 2019.12.27 04:33:32.754 Pending request to modify pending order parameters, ID #1: Deleted due completed
Die Tests zeigen, dass wir nun in der Lage sind, verschiedene Handelsgeschäfte mit derselben Order oder Position (Handelsgeschäfte mit dem gleichen Ticket) durchzuführen. In den früheren Versionen konnten wir einen einzigen Handelsvorgang mit einem Ticket durchführen. Danach ging ein schwebendes Anfrageobjekt immer davon aus, dass die Arbeit abgeschlossen war. Dieses Verhalten haben wir nun behoben.
Wir werden die verbleibenden Handelsoperationen im Zusammenhang mit schwebenden Anfragen im Laufe der weiteren Bibliotheksentwicklung nach und nach überprüfen und debuggen, da dies sorgfältige und langwierige Tests erfordert, um verschiedene anormale Situationen zu erkennen und zu beseitigen.
Was kommt als Nächstes?
Im nächsten Artikel werden wir die Entwicklung des Konzepts der schwebenden Handelsanfragen fortsetzen.
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
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten
Teil 20. Erstellen und Speichern von Programmressourcen
Teil 21. Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
Teil 22. Handelsklassen - Basisklasse des Handels, Verifikation der Einschränkungen
Teil 23. Handelsklasse - Basisklasse des Handels, Verifikation der Parameter
Teil 24. Trading classes - Handelsklassen, automatische Korrektur ungültiger Parametern
Teil 25. Handelsklassen - Basisklasse des Handels, Behandlung der Fehlermeldungen vom Server
Teil 26. Arbeiten mit schwebenden Handelsanfragen - Erste Implementation (Öffnen von Positionen)
Teil 27. Arbeiten mit schwebenden Handelsanfragen - Platzieren von Pending-Orders
Teil 28. Arbeiten mit schwebenden Handelsanfragen - Schließen, Entfernen und Ändern
Teil 29. Arbeiten mit schwebenden Handelsanfragen - Die Klasse der Anfrageobjekte
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7481





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.