Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXX): ordens de negociação pendentes, gerenciamento de objetos-ordens
Sumário
- Ideia
- Objeto-pausa
- Otimizando o código um pouco
- Classe de gerenciamento de objetos-ordens pendentes
- Teste
- O que vem agora?
Ideia
Começando com o artigo 26, gradualmente criamos e testamos o conceito de trabalho com ordens de negociação pendentes. No artigo anterior criamos classes de objetos de ordens pendentes, que correspondiam ao conceito geral de objetos de biblioteca. Hoje, trataremos de classes que permitem gerenciar objetos de ordens pendentes.
Inicialmente, queria criar uma classe independente que gerenciasse as ordens pendentes e que contivesse todos os métodos para trabalhar com elas. Resultou que a classe de negociação base CTrading da biblioteca e a nova classe de gerenciamento de ordens pendentes a serem criadas estavam tão interconectadas que foi mais fácil fazer com que uma nova classe de gerenciamento de objetos-ordens pendentes se tornasse herdeiro da classe de negociação base.
O gerenciamento de objetos de ordens pendentes é feito no temporizador da classe, portanto, tornaremos virtual o temporizador da classe de negociação base, portanto, o temporizador da classe de gerenciamento de ordens pendentes será virtual. Sendo assim, tudo o que tenha a ver com o temporizador da classe de negociação base vamos escrevê-lo no seu temporizador, enquanto tudo o que deva trabalhar na classe de gerenciamento de ordens pendentes vamos escrevê-lo no temporizador da classe em questão.
Além da classe para gerenciar objetos de ordens pendentes, criaremos uma pequena classe para implementar pausas, a fim de, no futuro, não usarmos a função Sleep(), que interrompe o funcionamento do programa por um tempo. O objeto-pausa nos permitirá não depender de ticks, assim, nos fins de semana, será possível testar código que precise de algum tipo de espera. A pausa será monitorada no temporizador.
Enquanto criava o armazenamento de alguns dados nas propriedade "Número mágico" da ordem, tive que configurar - em diferentes classes que exigem esses dados - métodos idênticos retornando os identificadores de magic e de grupo, registrados no valor do magic da ordem. Porém, injustamente esqueci que anteriormente criamos o objeto básico de todos os objetos de biblioteca, do qual podemos herdar objetos recém-criados, e eles, neste caso, obterão a funcionalidade de evento do objeto básico, bem como alguns métodos cujo princípio e propósito são idênticos, e são repetidos de classe para classe. Assim, este é o objeto que deve conter o registro e a obtenção de dados no número mágico, além de gerenciar a exibição de mensagens no log — sinalizador indicando o nível de registro de cada classe, em que há exibição de diferentes mensagens. É mais lógico herdar todas estas classes do objeto base e obter os métodos necessários do que sempre ter que escrever a mesma coisa em cada nova classe.
Com base no exposto, hoje temos que: criar uma classe de objeto-pausa (vamos usá-lo em artigos futuros), realizar uma otimização do código pronto que serve para transferir métodos repetidos de diferentes classes para a classe do objeto base, bem como criar uma classe para gerenciar ordens pendentes. Certamente, aprimoraremos as classes de objetos de ordens pendentes, para isso, precisaremos especificar nelas propriedades adicionais que permitirão comparar o status de dois objetos interconectados — o estado atual das propriedades da ordem e o estado das propriedades do objeto-ordem correspondentes. Isso é necessário para registrar o trabalho da ordem pendente como concluído, para atempadamente exclui-la da lista de solicitações ativas que estão à espera de serem enviadas para o servidor.
E, como habitual, primeiro, escrevemos no arquivo Datas.mqh os índices das novas mensagens:
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 }; //+------------------------------------------------------------------+
e os textos que correspondem a estes novos índices:
{"Отложенный запрос на удаление отложенного ордера","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 order expiration type"}, {"Фактическое время жизни ордера","Actual order lifetime"}, }; //+---------------------------------------------------------------------+
Objeto-pausa
Na pasta de classes de serviço e de funções da biblioteca \MQL5\Include\DoEasy\Services\ criamos a nova classe CPause no arquivo Pause.mqh, e imediatamente escrevemos nela tudo o necessário:
//+------------------------------------------------------------------+ //| 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()){;} }; //+------------------------------------------------------------------+
Logo a seguir, ao arquivo da classe anexamos o arquivo de funções de serviço DELib.mqh que contém a função de exibição de tempo em milissegundos, uma vez que ela é necessária para o método auxiliar da classe que exibe no log a hora de início da pausa.
Na seção privada da classe, declaramos três variáveis-membro da classe que são necessárias para calcular a pausa:
- A variável m_start serve para especificar o número de controle ao criar o objeto-pausa, bem como para registrar nela o novo número de controle que inicia a nova contagem no objeto-pausa criado anteriormente.
- A variável m_time_begin armazena a hora definida para iniciar a contagem da pausa, e serve apenas para exibir mensagens informativas.
- A variável m_wait_msc contém a quantidade de milissegundos de uma pausa — após decorrida essa quantidade de milissegundos, é considerado que a pausa é concluída.
Acho que os métodos da classe não precisam de explicações.
- O método SetTimeBegin(), que define a nova hora de início da nova contagem, primeiro grava na variável m_time_begin o tempo transferido a ele, em seguida, define o novo número de controle que inicia a contagem na variável m_start. O resultado que a função GetTickCount() retorna é o número de início de qualquer contagem na classe.
- O método Passed(), que retorna o número de milissegundos de pausa decorridos, basicamente retorna a diferença em milissegundos entre a medição atual do tempo de controle e o número definido em m_start ao iniciar a contagem da pausa.
Os outros métodos da classe são bastante óbvios, e não precisam serem explicados.
Ainda não estamos usando essa classe, mas faremos isso nos próximos desenvolvimentos da biblioteca.
Para que a classe de pausa seja acessível de qualquer lugar da biblioteca e de programas baseados na biblioteca,
anexamos o arquivo da classe CPause ao arquivo de funções de serviço da biblioteca DELib.mqh:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Otimizando o código um pouco
Bem, agora decidimos que está na hora de transferir os métodos repetidos de classe para classe ao objeto base de todos os objetos de biblioteca, e herdar essas classes do objeto base (se ainda não tiver sido feito).
Primeiro, escrevemos as novas variáveis e métodos na classe do objeto base de todos os objetos da biblioteca:
//+------------------------------------------------------------------+ //| 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(); }; //+------------------------------------------------------------------+
Variável protegida (acessível a herdeiros, mas não a programas), que armazena os valores do nível de registro para cada um dos objetos-herdeiros do objeto base nos quais é necessária a exibição de informações no log. Nela é especificado o nível de registro a partir da enumeração ENUM_LOG_LEVEL gravada em Defines.mqh:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
A definição e retorno de valores desta variável são realizados pelos métodos públicos SetLogLevel() e GetLogLevel() respectivamente.
Já os métodos de definição e retorno de valores de identificadores armazenados no magic da ordem/posição são feitos por métodos públicos.
Já consideramos anteriormente todos esses métodos.
Objeto de negociação base do símbolo no arquivo \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh.
Faremos as alterações necessárias:
anexamos a ele o arquivo do objeto base da biblioteca e fazemos com que sua classe pai se torne uma classe de objeto CBaseObj:
//+------------------------------------------------------------------+ //| 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 {
Após fazer essas alterações, a classe de objeto de negociação do símbolo se torna herdeira do objeto base de todos os objetos de biblioteca e agora todas as variáveis e métodos que já existem na classe pai CBaseObj devem ser removidos dessa classe.
Na seção privada da classe removemos as variáveis a mais que estão na classe pai:
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
Na seção pública da classe, excluímos os métodos de definição e retorno do nível registro:
//--- (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; }
Na lista de inicialização do construtor da classe, excluímos a inicialização do nível de registro:
//+------------------------------------------------------------------+ //| 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)
E escrevemos a inicialização do nível de registro no corpo do método:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Objeto da ordem abstrata base no arquivo \MQL5\Include\DoEasy\Objects\Orders\Order.mqh.
Faremos as alterações necessárias:
anexamos a ele o arquivo do objeto base da biblioteca e fazemos com que sua classe pai se torne uma classe de objeto CBaseObj:
//+------------------------------------------------------------------+ //| 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 {
Na seção privada da classe excluímos os métodos para obter identificadores gravados no magic da ordem:
(deixamos a tabela contendo um lembrete sobre a localização dos valores do identificador no valor do magic)
//+------------------------------------------------------------------+ //| 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:
No construtor privado da classe, adicionamos uma indicação de magic aos métodos para obter identificadores:
//+------------------------------------------------------------------+ //| 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)] = ""; } //+------------------------------------------------------------------+
Classe base de negociação no arquivo \MQL5\Include\DoEasy\Trading.mqh.
Faremos as alterações necessárias. Como o método agora se torna o pai da futura classe de gerenciamento de ordens pendentes,
da seção privada para uma protegida transferimos ponteiros para objetos de coleções e a lista de ponteiros para ordens pendentes:
//+------------------------------------------------------------------+ //| 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:
Na seção privada excluímos a variável para armazenar o nível de registro:
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
Na seção pública da classe inserimos um método que retorna todo o objeto da classe de negociação e fazemos com que o temporizador de classe se torne virtual:
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)
No método de criação de ordem pendente adicionamos a transferência ao método de objeto-ordem, e removemos os métodos para trabalhar com identificadores registrados no valor do magic da ordem:
//--- 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; } }; //+------------------------------------------------------------------+
Na implementação do temporizador de classe, removemos completamente tudo, deixando-a vazia:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { } //+------------------------------------------------------------------+
Aqui, escreveremos código se, no futuro, precisarmos processar algo apenas no temporizador desta classe, que é o pai de outras classes de negociação (nesta implementação, para a futura classe para gerenciar ordens pendentes).
No método de verificação de restrições para negociar CheckTradeConstraints(), no seu bloco para verificar o volume ao fazer operações de negociação que requerem o valor do lote (condição complementada) vamos verificar se o volume é correto somente se seu valor, transmitido pelo parâmetro de entrada, for maior que zero:
//--- 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
Em seguida:
Em todos os métodos de negociação onde é necessária a criação de um objeto de ordem pendente, ao método é transferido o objeto-ordem. E onde não existir esse objeto (nos métodos de abertura de posição e de definição de ordens pendentes) transferimos ao método NULL.
No método de abertura de posição transferimos NULL com ajuda do último parâmetro:
//--- 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; }
E no método de definição de ordem pendente, da mesma maneira, transferimos NULL como objeto-ordem ao método de criação de ordem pendente.
Já nos métodos que trabalham com ordens pendentes e posições já existentes, ao método de criação de ordem pendente transferimos o objeto-ordem, por exemplo,
ao método de molificação de ordens stop da posição existente transferimos o objeto-ordem:
//--- 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; }
E nos métodos restantes, em que é conhecida a ordem ou a posição, transferimos o objeto-ordem ao método de criação de ordem pendente.
Durante o teste, foi detectado que no método de fechamento de posição ClosePosition() era às vezes transferido um volume de ordem a ser fechada inválido quando esta era parcialmente fechada — simplesmente era enviado um volume zero que fazia com que sua verificação fosse ignorada. Depois, por causa disso, esse erro não era depurado. Para remediar a situação, acima já inserimos, no método de verificação de permissão de negociação, a verificação do volume durante o fechamento de posição, já agora vamos adicionar tanto o registro do volume correto ao criar uma ordem pendente quanto o seu envio ao método de verificação.
Para fazer isso, adicionamos ao método uma string:
//--- 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) {
Neste caso, gravamos, na estrutura da solicitação de negociação, o volume assim: se ao método for transferido um valor de -1 ou um valor de volume, transferido ao método, maior do que o volume da posição a ser fechada, gravaremos nas estrutura o volume inteiro da posição (fechamento total). Caso contrário, na estrutura gravamos o volume normalizado, transferido ao método (pode aparecer incorreto, por exemplo, ao dividir o volume 0,05 por 2 quando a posição estiver parcialmente fechada, ao método chegará um volume de 0,025, o que causará um erro)
No método de modificação de ordem pendente ModifyOrder()
adicionalmente inserimos na estrutura da ordem pendente o identificador do magic e o voluem da ordem:
//--- 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) {
Na negociação real, isto não oferece absolutamente nada, pois a ordem é modificado segundo o ticket, mas, para ordens pedentes, isso nos dará uma exibição de entradas no log correta sobre a ordem modificada.
O método de criação de ordem pendente sofreu pequenas alterações:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Olhando para o futuro (como agora estamos editando a classe de negociação base) tenho de dizer que, quando aprimoremos o objeto de ordem pendente, apresentaremos métodos para definir seus valores reais, o que nos permitira comparar o status do objeto (mudou ou foi ativada a ordem em questão), compararemos o status da ordem com os valores registrados na propriedade do objeto da ordem pendente. Se eles forem idênticos, pensaremos que a ordem pendente cumpre sua função.
Ao método de criação de ordem pendente verificamos o que é transferido como objeto-ordem. Se não for transferido NULL (o objeto-ordem existir), no objeto-ordem pendente são definidos os parâmetros iniciais do objeto-ordem. Caso contrário, os parâmetros são definidos da estrutura da ordem de negociação.
Essas são todas as mudanças na classe de negociação base.
Agora vamos modificar a classe do objeto de ordem pendente.
Temos novas propriedades de objeto-ordem pendente. Vamos escrevê-las no arquivo Defines.mqh.
Às propriedades inteiras da ordem pendente adicionamos novas propriedades e alteramos o número de propriedades inteiras de 19 para 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 //+------------------------------------------------------------------+
À lista de propriedades reais adicionamos novas propriedades e alteramos o número de 6 para 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 //+------------------------------------------------------------------+
À lista de possíveis critérios de classificação adicionamos classificação segundo novas propriedades:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Agora aos objetos.
Abrimos o arquivo de classe do objeto base da ordem pendente abstrata \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh e fazemos as alterações necessárias.
Na seção privada da classe declaramos o objeto da classe-pausa, enquanto na seção protegida, dois métodos sobrecarregados para comparar o valor das propriedades do objeto com o objeto transferido ao método e um conjunto de métodos que retornam sinalizadores indicando que a alteração de cada um dos parâmetros da ordem/posição na ordem pendente foi concluída:
//+------------------------------------------------------------------+ //| 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:
Na seção pública da classe declaramos o método virtual que retorna um sinalizador indicando que o trabalho da ordem pendente está concluído, o método que retorna o objeto-ordem atual completamente e os métodos para definir e retornar parâmetros do objeto-pausa:
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(); } //+------------------------------------------------------------------+
O método virtual IsCompleted() aqui — no objeto pai — sempre retorna false e será implementado individualmente para cada objeto-herdeiro. Os métodos para trabalho com o objeto-pausa simplesmente chamam os métodos correspondentes deste objeto, métodos esses que examinamos no início do artigo.
Ao bloco de métodos de acesso simplificado às propriedades do objeto-ordem pendente adicionamos métodos que retornam as propriedades reais da ordem:
//+------------------------------------------------------------------+ //| 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); }
Sempre, ao verificar o seguinte objeto-ordem pendente, nas suas propriedades serão gravadas as propriedades correspondentes da ordem/posição, para as quais é criado o objeto-ordem. Estes métodos permitem obter do objeto-ordem as propriedades atuais da ordem que corresponde a ele.
Neste bloco, modificaremos o método para definir o tempo de criação da ordem pendente e o método para definir seu tempo de espera:
//--- 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); }
Agora estes métodos, ao mesmo tempo que são definidos os valores transmitidos a eles no objeto-ordem pendente, definirão esses valores no objeto-pausa.
Neste mesmo bloco, escreveremos os métodos para definir no objeto-ordem as propriedades reais do objeto-ordem:
//--- 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); } //+------------------------------------------------------------------+
Ao bloco de exibição de descrição de propriedades do objeto adicionamos a definição de métodos que retornam a descrição de propriedades reais da ordem, e no final adicionamos um método virtual que retorna o "cabeçalho" (descrição curta do objeto) que descreve o objeto-ordem pendente:
//+------------------------------------------------------------------+ //| 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;} }; //+------------------------------------------------------------------+
Mais abaixo, consideraremos os métodos para descrever as propriedades reais de um objeto-ordem, enquanto o método virtual para descrever um cabeçalho de ordem pendente aqui retorna NULL, e será implementado em objetos-herdeiros.
No construtor da classe escrevemos a inicialização do objeto-pausa com os valores do tempo de criação do objeto-ordem e seu tempo de espera:
//+------------------------------------------------------------------+ //| 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)); } //+------------------------------------------------------------------+
Assim, imediatamente ao criar um novo objeto-ordem pendente, no seu objeto-pausa será inserida a hora de início da espera e a duração da pausa.
Fora do corpo da classe, escrevemos uma implementação de métodos que retornam os sinalizadores que indicam que as propriedades reais e inteiras do objeto-ordem e o valor passado aos métodos são idênticos:
//+---------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Ao método é transferido o modo pelo qual comparamos a propriedade do objeto e o valor passado ao método.
Um modo é uma indicação de um dos valores da propriedade do objeto a partir da enumeração de suas propriedades reais (ENUM_PEND_REQ_PROP_DOUBLE) ou inteiras (ENUM_PEND_REQ_PROP_INTEGER). Essa propriedade será comparada com o valor transferido ao método.
No método criamos um novo objeto-ordem pendente temporário e definimos para sua propriedade correspondente o valor transferido ao método.
O método Compare() do objeto base CObject da biblioteca padrão retorna 0, indicando que são idênticos. Mas o método é virtual e é implementado nos herdeiros da classe. Aqui o método Compare() retorna 1 se o valor do objeto atual for maior do que o mesmo valor do objeto comparado, -1 se o valor atual for menor que o valor do comparado, e 0 se for igual.
Assim, se os valores comparados forem iguais, na variável res será gravado false.
Mas nós vamos lá escrever a negação lógica do resultado da verificação. Assim, se o método de comparação retornar 0, false será igual a true. E se retornar 1 ou -1, true será igual a false.
Após registrar o resultado da comparação, é excluído o objeto temporário e é retornado resultado da comparação.
Métodos que retornam sinalizadores comparando os valores reais das propriedades da ordem e do objeto-ordem por tipos:
//+------------------------------------------------------------------+ //| 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)); } //+------------------------------------------------------------------+
Os métodos retornam o resultado da comparação de duas propriedades, nomeadamente a real e a registrada no objeto, usando o método de comparação considerado.
Na implementação de métodos que retornam descrições de propriedades do objeto, inserimos novas propriedades:
//+------------------------------------------------------------------+ //| 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) ); } //+------------------------------------------------------------------+
Nos métodos, é verificada a propriedade do objeto transferido a ele, enquanto sua descrição é retornada usando os métodos correspondentes à propriedade.
Métodos que retornam descrições das propriedades reais da ordem gravadas no objeto-ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Os métodos simplesmente compõem e retornam o texto da mensagem correspondente à propriedade retornada pelo método.
Todas essas são todas as alterações no objeto base da ordem pendente abstrata.
Agora, faremos alterações nas classes-herdeiras do objeto-ordem base.
Abrimos o arquivo da classe do objeto de ordem pendente para abrir posições \MQL5\Include\DoEasy\Objects\PendRequest\PendReqOpen.mqh e fazemos alterações.
Inserimos a definição de um método virtual que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); }; //+------------------------------------------------------------------+
E sua implementação:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Classe de ordem pendente para modificar ordens stop de uma posição (arquivo 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); }; //+------------------------------------------------------------------+
Aos métodos que retornam o sinalizador indicando que o objeto suporta algumas de suas propriedades, adicionaremos novas propriedades:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Um método virtual que retorna um sinalizador de conclusão de trabalho da ordem pendente:
//+----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Neste caso, à variável res adicionamos os resultados para verificar que a modificação do StopLoss e TakeProfit está concluída. Se pelo menos uma das verificações retornar false, o resultado geral será false. Retornamos o resultado final do método.
Método que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Classe de ordem pendente para abertura de posição (arquivo 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); }; //+------------------------------------------------------------------+
Método que retorna um sinalizador indicando que a ordem foi concluída:
//+----------------------------------------------------------------------+ //| Return the flag indicating the pending request has completed its work| //+----------------------------------------------------------------------+ bool CPendReqClose::IsCompleted(void) const { return this.IsCompletedVolume(); } //+------------------------------------------------------------------+
Método que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Classe de ordem pendente para definir uma ordem pendente (arquivo 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); }; //+------------------------------------------------------------------+
Método que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Classe de ordem pendente para alterar os parâmetros da ordem pendente (arquivo 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); }; //+------------------------------------------------------------------+
Método que retorna um sinalizador indicando que a ordem foi concluída:
//+----------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Método que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Classe de ordem pendente para excluir uma ordem pendente (arquivo 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); }; //+------------------------------------------------------------------+
Método que retorna o nome abreviado da ordem:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Como podemos ver nas listagens de classes e seus métodos correspondentes, aqui tudo é bem básico, uma vez que cada método usa apenas as propriedades do objeto de ordem pendente abstrata inerentes a ele, enquanto no nome abreviado da ordem é gerado o texto a partir das mensagens de texto que correspondem à ordem.
Por aqui concluímos o aprimoramento de classes-herdeiros da classe base do objeto de ordem de negociação pendente abstrata.
Classe de gerenciamento de objetos-ordens pendentes
Agora, vamos finalmente tratar da classe de gerenciamento de objetos-ordens pendentes.
Como já mencionado acima, a classe em questão será herdada da classe de negociação CTrading, uma vez que, nas classes CTrading, CPendRequest e nas novas classes CTradingControl, todos os dados e métodos estão intimamente interconectados. A classe nesta implementação será muito pequena, funcionará no temporizador, e todo o código no temporizador será transferido do código do temporizador da classe CTrading com pequenas modificações.
Criamos a nova classe CTradingControl no arquivo \MQL5\Include\DoEasy\TradingControl.mqh. Especificamos CTrading como classe base ao ser criada e, imediatamente após isso, anexamos o arquivo Trading.mqh a ela:
//+------------------------------------------------------------------+ //| 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(); }; //+------------------------------------------------------------------+
Vemos isso aqui:
a classe tem um método privado SetActualProperties() que define no objeto-ordem pendente os dados reais da ordem/posição.
Na seção pública, o método Getobject() retorna um ponteiro para todo o objeto de gerenciamento de ordem pendente, enquanto o método virtual Ontimer() é um temporizador da classe de gerenciamento de ordens pendentes onde funcionará tudo.
No construtor da classe limpamos a lista de ordens pendentes e definimos para ele o sinalizador de lista classificada:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTradingControl::CTradingControl() { this.m_list_request.Clear(); this.m_list_request.Sort(); } //+------------------------------------------------------------------+
Essa lista pertence à classe CTrading, porque é na classe de negociação que são criadas as ordens de negociação pendentes.
Fora do corpo da classe, escrevemos a implementação do método para definir os dados reais da ordem no objeto-ordem:
//+------------------------------------------------------------------+ //| 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()); } //+------------------------------------------------------------------+
Ao método são transferidos o ponteiro para o objeto-ordem pendente e o ponteiro para o objeto-ordem pendente. Em seguida, no objeto-ordem, com a ajuda dos respectivos métodos discutidos acima, definimos os valores correspondentes das propriedades da ordem.
O temporizador de classe é realizado o rastreamento de todos os objetos-ordens disponíveis na lista de ordens pendentes. É verificado o tempo de vida de cada ordem pendente e, se expirar, será excluída a ordem. Se a solicitação já foi ativada, quer dizer, se existirem no histórico da conta eventos de negociação que correspondem a ela ou se existir uma ordem/posição com um identificador de ordem pendente no magic que corresponde ao identificador da ordem pendente, tais ordens serão consideradas totalmente usadas e serão excluídas da lista.
Se a solicitação ainda não foi usada e está na hora de sua ativação, ao servidor é enviada uma ordem de negociação gravada na ordem pendente.
O método verifica se é permitido realizar operações de negociação a partir do terminal - o botão "Autotrading" e uma marca de seleção nas configurações do EA "Permitir negociação automática". Se uma ordem pendente for criada pelo código de erro 10027, isto é, negociação automática proibida do lado do terminal, quando o usuário eliminar a causa do erro (ativando o botão "Autotrading" ou marcando nas configurações do EA a caixa de seleção "Permitir negociação automática"), esta ordem pendente será automaticamente ativada, que dizer, será definida uma nova hora de ativação da ordem de pendente para que ela ocorra no seguinte tick, afinal, o usuário consegue corrigir o erro, não precisa aguardar e deve imediatamente enviar a ordem ao servidor para evitar futuras requotizações.
Como o método é bastante grande, eu tentei nos comentários do código descrever em detalhes todas as ações realizadas dentro do temporizador da classe de gerenciamento de ordens pendentes, espero que estejam claramente escritas para estudá-las individualmente.
Temporizador de classe de gerenciamento de ordem pendente:
//+------------------------------------------------------------------+ //| 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; } } } //+------------------------------------------------------------------+
Por hoje, isso é tudo o que é necessário fazer na classe de gerenciamento de ordens de negociação pendentes. Se o funcionamento do temporizador não estiver claro, você poderá fazer todas suas perguntas na discussão do artigo.
Fazemos alterações na classe do objeto base da biblioteca CEngine.
Como agora, em vez da classe de negociação, usaremos seu herdeiro (classe de gerenciamento de ordens pendentes),
em vez da anexação à listagem do arquivo da classe de negociação:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
anexamos o arquivo da classe de gerenciamento de ordens de negociação pendentes:
//+------------------------------------------------------------------+ //| 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" //+------------------------------------------------------------------+
Já em vez do objeto de classe de negociação
//+------------------------------------------------------------------+ //| 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
usaremos o objeto da classe de gerenciamento de ordens de negociação:
//+------------------------------------------------------------------+ //| 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
Por hoje essas são todas as mudanças na biblioteca.
Teste
Para testar uma nova versão da biblioteca, pegamos o EA do artigo anterior e o armazenamos na nova pasta \MQL5\Experts\TestDoEasy\Part30\ usando o novo nome TestDoEasyPart30.mq5.
Vamos compilar o EA e executá-lo no gráfico da conta demo.
Precisamos verificar como funcionam os objetos de ordens pendentes quando no terminal está desativado o botão "Autotrading".
Nas configurações do EA, definimos como 0 a distância de StopLoss e TakeProfit.
Dois parâmetros personalizados são responsáveis por isso:
- StopLoss in points
- TakeProfit in points.
Em seguida, faremos assim:
primeiro, desativamos a negociação automática usando o botão "Autotrading" no terminal, tentamos enviar a ordem de negociação, por exemplo, para definir uma ordem pendente SellLimit com a ajuda do botão que está no painel de negociação do EA.
Logo, recebemos o erro de negociação restrita da pate do terminal,
além disso, será gerada uma ordem pendente.
Em seguida, imediatamente clicamos no botão "Autotrading" para corrigir o erro.
E, segundo o conceito por trás, aqui deve ser ativada apenas a ordem pendente criada que enviará a ordem de negociação ao servidor,
e depois da sua execução
o objeto-ordem será excluído como executado:
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
Agora é necessário verificar se é possível modificar ordens stop de uma ordem pendente recém-colocada (foi definido sem elas).
Novamente desativamos a negociação automática e clicamos no botão "Set StopLoss" no painel de negociação do EA de teste.
Recebemos o erro da proibição de operações de negociação no terminal e
será criada uma solicitação pendente para alterar os parâmetros de uma ordem pendente.
Após a próxima tentativa de negociação que já envia a ordem pendente,
ativamos no terminal a negociação automática, e
para a ordem pendente será colocado StopLoss,
enquanto a ordem pendente será excluída como usada:
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
Aqui pode ser visto que, após ativar a negociação automática, ao repetir a ordem do objeto-ordem pendente, ela tem o mesmo número que ao tentar repetir pela primeira vez a ordem de negociação, ou seja, a lógica para adicionar mais uma tentativa quando o usuário corrige a causa do erro (permissão de negociação automática no terminal) funciona corretamente.
Agora faremos o mesmo, mas para definir o TakeProfit.
E, novamente, tudo funciona bem (neste caso, após a segunda tentativa de negociação feita pelo objeto-ordem pendente),
TakeProfit é colocado pelo objeto-ordem pendente e esta ordem é excluída:
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
Com base nos testes realizados, vemos que agora podemos realizar diferentes operações de negociação com a mesma ordem ou posição (operações de negociação com o mesmo ticket). Nas versões anteriores, só podíamos realizar uma operação de negociação com um ticket, sendo que, depois, o objeto-ordem pendente sempre considerava que já havia sido usada. Agora nós corrigimos esse comportamento.
Com o desenvolvimento da biblioteca, verificaremos e depuraremos gradualmente as outras operações de negociação, ao trabalhar com ordens pendentes, pois tudo requer testes cuidadosos e prolongados para identificar e eliminar as diferentes situações não-padrão.
O que vem agora?
No próximo artigo, continuaremos a desenvolver o conceito de ordens de negociação pendentes.
Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.
Artigos desta série:
Parte 1. Conceito, gerenciamento de dados e primeiros resultadosParte 2. Coleção do histórico de ordens e negócios
Parte 3. Coleção de ordens e posições de mercado, busca e ordenação
Parte 4. Eventos de Negociação. Conceito
Parte 5. Classes e coleções de eventos de negociação. Envio de eventos para o programa
Parte 6. Eventos da conta netting
Parte 7. Eventos de ativação da ordem stoplimit, preparação da funcionalidade para os eventos de modificação de ordens e posições
Parte 8. Eventos de modificação de ordens e posições
Parte 9. Compatibilidade com a MQL4 — preparação dos dados
Parte 10. Compatibilidade com a MQL4 — eventos de abertura de posição e ativação de ordens pendentes
Parte 11. Compatibilidade com a MQL4 — eventos de encerramento de posição
Parte 12. Implementação da classe de objeto "conta" e da coleção de objetos da conta
Parte 13. Eventos do objeto conta
Parte 14. O objeto símbolo
Parte 15. Coleção de objetos-símbolos
Parte 16. Eventos de coleção de símbolos
Parte 17. Interatividade de objetos de biblioteca
Parte 18. Interatividade do objeto-conta e quaisquer de outros objetos da biblioteca
Parte 19. Classe de mensagens de biblioteca
Parte 20. Criação e armazenamento de recursos de programas
Parte 21 Classes de negociação - objeto base de negociação multiplataforma
Parte 22. Classes de negociação - classe básica de negociação, controle de restrições
Parte 23. Classes de negociação - classe básica de negociação, controle de parâmetros válidos
Parte 24. Classes de negociação - classe básica de negociação, correção automática de parâmetros errados
Parte 25. Classes de negociação - classe básica de negociação, processamento de erros retornados pelo servidor de negociação
Parte 26. Trabalho com ordens pendentes, primeira implementação (abertura de posições)
Parte 27. Trabalho com ordens pendentes, posicionamento de ordens pendentes Parte 28. Trabalho com ordens pendentes de negociação - fechamento, exclusão, modificações
Parte 29. Trabalho com ordens de negociação pendentes, classes de objetos-ordens
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7481
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso