Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXVII): trabalho com ordens de negociação - posicionamento de ordens pendentes
Sumário
- Preparação de dados
- Depurando a classe de negociação e criando solicitações pendentes para posicionamento de ordens
- Teste
- O que vem agora?
No último artigo começamos a implementar ordens de negociação pendentes e criamos a primeira ordem pendente para abrir uma posição quando na classe de negociação após o envio da solicitação ao servidor era recebido um erro. Hoje, continuaremos a desenvolver a classe de negociação, nomeadamente o trabalho com ordens pendentes, e implementaremos a criação de uma ordem pendente se houver erros ao configurar ordens pendentes.
Ao testar a classe de negociação, apareceram alguns inconvenientes relativamente ao design. Em particular, durante a inicialização de objetos, no construtor da classe, aos objetos de negociação dos símbolos eram atribuídos valores padrão hard-coded que podiam não ser suportados pela especificação do símbolo. Isto causava erros vindos do servidor ao tentar colocar ordens pendentes: o servidor gerava o erro 'tipo de expiração de ordem não suportado' e não o depurava, o que tornava impossível posicionar ordens pendentes. Ao enviar ordens de negociação com valores padrão, à ordem também eram enviados alguns dados não suportados que não eram depurados em lado nenhum. Para contornar essa situação, era necessário diretamente na ordem de negociação especificar os dados corretos, que correspondiam à especificação do símbolo, com base no qual devia ser realizada a operação de negociação.
Isso é inconveniente, pois, em vez de a própria biblioteca depurar automaticamente os valores, é preciso conhecer obrigatoriamente a especificação do símbolo e a inserção manual de valores diretamente no código do programa. Por isso, faremos pequenas correções na lógica de funcionamento da classe de negociação. Assim, no EA no manipulador OnInit(), a escolha de valores corretos fará com que os objetos de negociação dos símbolos sejam inicializados automaticamente. Por padrão, aos métodos de negociação da classe de negociação serão transferidos os valores -1, para os tipos 'preenchimento e expiração de ordens', o que indicará que os valores padrão predefinidos usados estão corretos. Se do programa ao método de negociação for transferido um valor diferente, o valor diferente em questão será usado e, se for incorreto, será depurado quando forem depurados erros na classe de negociação.Preparação de dados
Além da depuração da classe de negociação, à classe do objeto-ordem pendente adicionaremos uma descrição da ordem, exibindo no log todos os
seus parâmetros. Mais para frente, isso irá facilitar-nos o teste do trabalho com objetos de ordens pendentes.
Bem, primeiro,
adicionaremos todas as mensagens necessárias à matriz de mensagens da biblioteca.
Abrimos o arquivo Datas.mqh e inserimos os índices das novas mensagens:
//+------------------------------------------------------------------+ //| List of the library's text message indices | //+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, // Beginning of the parameter list MSG_LIB_PARAMS_LIST_END, // End of the parameter list MSG_LIB_PROP_NOT_SUPPORTED, // Property not supported MSG_LIB_PROP_NOT_SUPPORTED_MQL4, // Property not supported in MQL4 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155, // Property not supported in MetaTrader 5 versions lower than 2155 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, // Property not supported for position MSG_LIB_PROP_NOT_SUPPORTED_PENDING, // Property not supported for pending order MSG_LIB_PROP_NOT_SUPPORTED_MARKET, // Property not supported for market order MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST, // Property not supported for historical market order MSG_LIB_PROP_NOT_SET, // Value not set MSG_LIB_PROP_EMPTY, // Not set MSG_LIB_PROP_AS_IN_ORDER, // According to the order expiration mode MSG_LIB_SYS_ERROR, // Error
...
MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ, // Failed to create a pending request MSG_LIB_TEXT_TRY_N, // Trading attempt # MSG_LIB_TEXT_RE_TRY_N, // Repeated trading attempt # MSG_LIB_TEXT_REQUEST_ACTION, // Type of a performed action MSG_LIB_TEXT_REQUEST_MAGIC, // EA stamp (magic number) MSG_LIB_TEXT_REQUEST_ORDER, // Order ticket MSG_LIB_TEXT_REQUEST_SYMBOL, // Name of a trading instrument MSG_LIB_TEXT_REQUEST_VOLUME, // Requested volume of a deal in lots MSG_LIB_TEXT_REQUEST_PRICE, // Price MSG_LIB_TEXT_REQUEST_STOPLIMIT, // StopLimit MSG_LIB_TEXT_REQUEST_SL, // Stop Loss MSG_LIB_TEXT_REQUEST_TP, // Take Profit MSG_LIB_TEXT_REQUEST_DEVIATION, // Maximum price deviation MSG_LIB_TEXT_REQUEST_TYPE, // Order type MSG_LIB_TEXT_REQUEST_TYPE_FILLING, // Order filling type MSG_LIB_TEXT_REQUEST_TYPE_TIME, // Order lifetime type MSG_LIB_TEXT_REQUEST_EXPIRATION, // Order expiration date MSG_LIB_TEXT_REQUEST_COMMENT, // Order comment MSG_LIB_TEXT_REQUEST_POSITION, // Position ticket MSG_LIB_TEXT_REQUEST_POSITION_BY, // Opposite position ticket MSG_LIB_TEXT_REQUEST_ACTION_DEAL, // Place a market order MSG_LIB_TEXT_REQUEST_ACTION_PENDING, // Place a pending order MSG_LIB_TEXT_REQUEST_ACTION_SLTP, // Change open position Stop Loss and Take Profit MSG_LIB_TEXT_REQUEST_ACTION_MODIFY, // Change parameters of the previously placed trading order MSG_LIB_TEXT_REQUEST_ACTION_REMOVE, // Remove previously placed pending order MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY, // Close a position by an opposite one MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN, // Unknown trading operation type MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK, // Order is executed in the specified volume only, otherwise it is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK, // Order is filled within an available volume, while the unfilled one is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN, // Order is filled within an available volume, while the unfilled one remains MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC, // Order is valid till explicitly canceled MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY, // Order is valid only during the current trading day MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED, // Order is valid till the expiration date MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY, // Order is valid till 23:59:59 of a specified day MSG_LIB_TEXT_REQUEST_DATAS, // Trading request parameters MSG_LIB_TEXT_PEND_REQUEST_DATAS, // Pending trading request parameters MSG_LIB_TEXT_PEND_REQUEST_CREATED, // Pending request created MSG_LIB_TEXT_PEND_REQUEST_DELETED, // Pending request is removed due to its expiration MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE, // Price at the moment of request generation MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE, // Request creation time MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE, // Request activation time MSG_LIB_TEXT_PEND_REQUEST_WAITING, // Waiting time between trading attempts MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT, // Current trading attempt MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS, // Total number of trading attempts MSG_LIB_TEXT_PEND_REQUEST_ID, // Trading request ID MSG_LIB_TEXT_PEND_REQUEST_RETCODE, // Return code a request is based on MSG_LIB_TEXT_PEND_REQUEST_TYPE, // Pending request type MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR, // Pending request generated based on the server return code MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST, // Pending request created by request MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET, // Wait for the first trading attempt };
e inserimos os textos das mensagens em conformidade com os índices:
//+------------------------------------------------------------------+ string messages_library[][TOTAL_LANG]= { {"Начало списка параметров","Beginning of event parameter list"}, {"Конец списка параметров","End of parameter list"}, {"Свойство не поддерживается","Property not supported"}, {"Свойство не поддерживается в MQL4","Property not supported in MQL4"}, {"Свойство не поддерживается в MetaTrader5 версии ниже 2155","Property not supported in MetaTrader 5, build lower than 2155"}, {"Свойство не поддерживается у позиции","Property not supported for position"}, {"Свойство не поддерживается у отложенного ордера","Property not supported for pending order"}, {"Свойство не поддерживается у маркет-ордера","Property not supported for market order"}, {"Свойство не поддерживается у исторического маркет-ордера","Property not supported for historical market order"}, {"Значение не задано","Value not set"}, {"Отсутствует","Not set"}, {"В соответствии с режимом истечения ордера","In accordance with the order expiration mode"}, {"Ошибка ","Error"},
...
{"Не удалось создать отложенный запрос","Failed to create pending request"}, {"Торговая попытка #","Trading attempt #"}, {"Повторная торговая попытка #","Retry trading attempt #"}, {"Тип выполняемого действия","Trade operation type"}, {"Штамп эксперта (magic number)","Expert Advisor ID (magic number)"}, {"Тикет ордера","Order ticket"}, {"Имя торгового инструмента","Trade symbol"}, {"Запрашиваемый объем сделки в лотах","Requested volume for a deal in lots"}, {"Цена","Price"}, {"Уровень StopLimit ордера","StopLimit level of the order"}, {"Уровень Stop Loss ордера","Stop Loss level of the order"}, {"Уровень Take Profit ордера","Take Profit level of the order"}, {"Максимальное отклонение от цены","Maximal deviation from the price"}, {"Тип ордера","Order type"}, {"Тип ордера по исполнению","Order execution type"}, {"Тип ордера по времени действия","Order expiration type"}, {"Срок истечения ордера","Order expiration time"}, {"Комментарий к ордеру","Order comment"}, {"Тикет позиции","Position ticket"}, {"Тикет встречной позиции","Opposite position ticket"}, {"Поставить рыночный ордер","Place market order"}, {"Установить отложенный ордер","Place pending order"}, {"Изменить значения Stop Loss и Take Profit у открытой позиции","Modify Stop Loss and Take Profit values of an opened position"}, {"Изменить параметры ранее установленного торгового ордера","Modify the parameters of the order placed previously"}, {"Удалить ранее выставленный отложенный ордер","Delete the pending order placed previously"}, {"Закрыть позицию встречной","Close a position by an opposite one"}, {"Неизвестный тип торговой операции","Unknown trade action type"}, {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)"}, {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled (IOK)"}, {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market (Return)"}, {"Ордер действителен до явной отмены","Good till cancel order"}, {"Ордер действителен только в течение текущего торгового дня","Good till current trade day order"}, {"Ордер действителен до даты истечения","Good till expired order"}, {"Ордер действителен до 23:59:59 указанного дня","The order will be effective till 23:59:59 of the specified day"}, {"Параметры торгового запроса","Trade request's parameters"}, {"Параметры отложенного торгового запроса","Pending trade request's parameters"}, {"Создан отложенный запрос","Pending request created"}, {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"}, {"Цена в момент создания запроса: ","Price at time of request create: "}, {"Время создания запроса: ","Request creation time: "}, {"Время активации запроса: ","Request activation time: "}, {"Время ожидания между торговыми попытками: ","Waiting time between trading attempts: "}, {"Текущая торговая попытка: ","Current trading attempt: "}, {"Общее количество торговых попыток: ","Total trade attempts: "}, {"Идентификатор торгового запроса: ","Trade request ID: "}, {"Код возврата, на основании которого создан запрос: ","Return code based on which the request was created: "}, {"Тип отложенного запроса: ","Pending request type: "}, {"Отложенный запрос, созданный по коду возврата сервера","Pending request that was created as a result of the server code"}, {"Отложенный запрос, созданный по запросу","Pending request created by request"}, {"Ожидание наступления времени первой торговой попытки","Waiting for the onset time of the first trading attempt"}, }; //+---------------------------------------------------------------------+
Além das falhas descritas acima, notei que os valores dos identificadores de coleções se cruzam com os identificadores de tipos de objetos na biblioteca padrão. Em particular, o identificador de coleção de ordens e de transações históricas COLLECTION_HISTORY_ID possui um valor 0x7779 que corresponde ao valor de tipo de lista da classe de lista dinâmica de instâncias que pertencem à classe CObject e aos seus descendentes CList da biblioteca padrão. Isto está errado, pois os identificadores dos objetos não deviam ter o mesmo valor.
Eis, provavelmente, uma lista incompleta de identificadores de objetos da biblioteca padrão e os valores hexadecimais correspondentes:
Classe básica CObject |
Type
= 0 |
Coleções de dados CArrayChar |
Type
= 0x77 |
Coleções de dados CArrayShort |
Type
= 0x82 |
Coleções de dados CArrayInt |
Type =
0x82 |
Coleções de dados CArrayLong |
Type = 0x84 |
Coleções de dados CArrayFloat |
Type
= 0x87 |
Coleções de dados CArrayDouble |
Type
= 0x87 |
Coleções de dados CArrayString |
Type
= 0x89 |
Coleções de dados CArrayObj |
Type
= 0x7778 |
Coleções de dados CList |
Type
= 0x7779 |
Objetos gráficos Classe Básica CChartObject |
Type
= 0x8888 |
Gráficos de preços CChart |
Type
= 0x1111 |
Como podemos ver, o tipo de objeto-lista coincide com o identificador -
definido para a biblioteca - para a coleção de ordens e de transações históricas.
Corrigimos esta coleção de dados no arquivo
Defines.mqh, aumentando em 1 os valores de todos os identificadores de coleção:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID //--- Data parameters for file operations
Como no futuro implementaremos recursos para negociar usando ordens pendentes, teremos dois tipos de ordens pendentes:
- ordem criada de acordo com o código de erro vindo do servidor de negociação (iremos envolver-nos na implementação de tais ordens
nesta fase);
- ordem pendente criada de acordo com solicitação vinda do programa (negociação usando ordens pendentes que implementaremos
posteriormente).
Por isso, para separar os tipos de ordens, apresentamos o conceito de "tipo de ordem" e os tipos de identificadores correspondentes:
//--- Symbol parameters #define CLR_DEFAULT (0xFF000000) // Default color #define SYMBOLS_COMMON_TOTAL (1000) // Total number of working symbols //--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //+------------------------------------------------------------------+
E no final do arquivo Defines.mqh, inserimos a enumeração de tipos de ordens pendentes:
//+------------------------------------------------------------------+ //| Pending request type | //+------------------------------------------------------------------+ enum ENUM_PENDING_REQUEST_TYPE { PENDING_REQUEST_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR, // Pending request created based on the return code or error PENDING_REQUEST_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,// Pending request created by request }; //+------------------------------------------------------------------+
Para exibir a descrição de ordens de negociação no log, precisamos preparar as funções que farão isso.
No
arquivo de funções de serviço DELib.mqh escrevemos todas as funções necessárias, que simplesmente criarão mensagens usando
textos predefinidos gravados no arquivo Datas.mqh e valores exibidos pelas funções de propriedades.
Funções para exibir descrições indicando o modo de preenchimento
de ordem e de tipo de expiração:
//+------------------------------------------------------------------+ //| Return the order filling mode description | //+------------------------------------------------------------------+ string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type) { return ( type==ORDER_FILLING_FOK ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK) : type==ORDER_FILLING_IOC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK) : type==ORDER_FILLING_RETURN ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN): type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+ //| Return the order expiration type description | //+------------------------------------------------------------------+ string OrderTypeTimeDescription(const ENUM_ORDER_TYPE_TIME type) { return ( type==ORDER_TIME_GTC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC) : type==ORDER_TIME_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY) : type==ORDER_TIME_SPECIFIED ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED) : type==ORDER_TIME_SPECIFIED_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY) : type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+
Funções para exibir descrições indicando estrutura
de ordem de negociação MqlTradeRequest:
//+------------------------------------------------------------------+ //| Display the trading request description in the journal | //+------------------------------------------------------------------+ void PrintRequestDescription(const MqlTradeRequest &request) { string datas= ( " - "+RequestActionDescription(request)+"\n"+ " - "+RequestMagicDescription(request)+"\n"+ " - "+RequestOrderDescription(request)+"\n"+ " - "+RequestSymbolDescription(request)+"\n"+ " - "+RequestVolumeDescription(request)+"\n"+ " - "+RequestPriceDescription(request)+"\n"+ " - "+RequestStopLimitDescription(request)+"\n"+ " - "+RequestStopLossDescription(request)+"\n"+ " - "+RequestTakeProfitDescription(request)+"\n"+ " - "+RequestDeviationDescription(request)+"\n"+ " - "+RequestTypeDescription(request)+"\n"+ " - "+RequestTypeFillingDescription(request)+"\n"+ " - "+RequestTypeTimeDescription(request)+"\n"+ " - "+RequestExpirationDescription(request)+"\n"+ " - "+RequestCommentDescription(request)+"\n"+ " - "+RequestPositionDescription(request)+"\n"+ " - "+RequestPositionByDescription(request) ); Print("================== ",CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)," ==================\n",datas,"\n"); } //+------------------------------------------------------------------+ //| Return the executed action type description | //+------------------------------------------------------------------+ string RequestActionDescription(const MqlTradeRequest &request) { int code_descr= ( request.action==TRADE_ACTION_DEAL ? MSG_LIB_TEXT_REQUEST_ACTION_DEAL : request.action==TRADE_ACTION_PENDING ? MSG_LIB_TEXT_REQUEST_ACTION_PENDING : request.action==TRADE_ACTION_SLTP ? MSG_LIB_TEXT_REQUEST_ACTION_SLTP : request.action==TRADE_ACTION_MODIFY ? MSG_LIB_TEXT_REQUEST_ACTION_MODIFY : request.action==TRADE_ACTION_REMOVE ? MSG_LIB_TEXT_REQUEST_ACTION_REMOVE : request.action==TRADE_ACTION_CLOSE_BY ? MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY : MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN ); return CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTION)+": "+CMessage::Text(code_descr); } //+------------------------------------------------------------------+ //| Return the magic number value description | //+------------------------------------------------------------------+ string RequestMagicDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_ORD_MAGIC)+": "+(string)request.magic; } //+------------------------------------------------------------------+ //| Return the order ticket value description | //+------------------------------------------------------------------+ string RequestOrderDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER)+": "+(request.order>0 ? (string)request.order : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the trading instrument name description | //+------------------------------------------------------------------+ string RequestSymbolDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SYMBOL)+": "+request.symbol; } //+------------------------------------------------------------------+ //| Return the request volume description | //+------------------------------------------------------------------+ string RequestVolumeDescription(const MqlTradeRequest &request) { int dg=(int)DigitsLots(request.symbol); int dgl=(dg==0 ? 1 : dg); return CMessage::Text(MSG_LIB_TEXT_REQUEST_VOLUME)+": "+(request.volume>0 ? DoubleToString(request.volume,dgl) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request price value description | //+------------------------------------------------------------------+ string RequestPriceDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+": "+(request.price>0 ? DoubleToString(request.price,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLimit order price description | //+------------------------------------------------------------------+ string RequestStopLimitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_STOPLIMIT)+": "+(request.stoplimit>0 ? DoubleToString(request.stoplimit,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLoss order price description | //+------------------------------------------------------------------+ string RequestStopLossDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+": "+(request.sl>0 ? DoubleToString(request.sl,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request TakeProfit order price description | //+------------------------------------------------------------------+ string RequestTakeProfitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+": "+(request.tp>0 ? DoubleToString(request.tp,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request deviation size description | //+------------------------------------------------------------------+ string RequestDeviationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_DEVIATION)+": "+(string)request.deviation; } //+------------------------------------------------------------------+ //| Return the request order type description | //+------------------------------------------------------------------+ string RequestTypeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE)+": "+OrderTypeDescription(request.type); } //+------------------------------------------------------------------+ //| Return the request order filling mode description | //+------------------------------------------------------------------+ string RequestTypeFillingDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_FILLING)+": "+OrderTypeFillingDescription(request.type_filling); } //+------------------------------------------------------------------+ //| Return the request order lifetime type description | //+------------------------------------------------------------------+ string RequestTypeTimeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_TIME)+": "+OrderTypeTimeDescription(request.type_time); } //+------------------------------------------------------------------+ //| Return the request order expiration time description | //+------------------------------------------------------------------+ string RequestExpirationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_EXPIRATION)+": "+(request.expiration>0 ? TimeToString(request.expiration) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request order comment description | //+------------------------------------------------------------------+ string RequestCommentDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_COMMENT)+": "+(request.comment!="" && request.comment!=NULL ? "\""+request.comment+"\"" : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request position ticket description | //+------------------------------------------------------------------+ string RequestPositionDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION)+": "+(request.position>0 ? (string)request.position : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request opposite position ticket description | //+------------------------------------------------------------------+ string RequestPositionByDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION_BY)+": "+(request.position_by>0 ? (string)request.position_by : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+
Enquanto modificamos a classe de negociação - para depurar de erros -, vamos escrever tudo o que for necessário para criar um objeto-ordem
pendente para posicionar ordens pendentes.
Depurando a classe de negociação e criando solicitações pendentes para posicionamento de ordens
Notei uma característica interessante de alguns símbolos cujo gráfico é construído segundo preços Last. Às vezes, eles não têm preços Ask e Bid, ou um deles. Para, de algum modo, obter preços, tive que criar métodos adicionais (e alterar os existentes) na classe do símbolo abstrato CSymbol (eles verificam o tipo de gráfico e, se for construído segundo preços Last, será verificada a presença de preços de Ask e Bid e, se existirem, eles serão usados, caso contrário, será utilizado o último preço).
No arquivo Symbol.mqh, no bloco de métodos de acesso simplificado às propriedades do objeto-símbolo alteraremos
o tipo de método que retorna o tempo. Como o tempo é retornado em milissegundos, o tipo deve ser ulong em vez de datetime.
Também declararemos
três métodos adicionais, cujo objetivo foi mencionado acima:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Integer properties long Status(void) const { return this.GetProperty(SYMBOL_PROP_STATUS); } int IndexInMarketWatch(void) const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW); } bool IsCustom(void) const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM); } color ColorBackground(void) const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); } ENUM_SYMBOL_CHART_MODE ChartMode(void) const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); } bool IsExist(void) const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST); } bool IsExist(const string name) const { return this.SymbolExists(name); } bool IsSelect(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT); } bool IsVisible(void) const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE); } long SessionDeals(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); } long SessionBuyOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); } long SessionSellOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); } long Volume(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME); } long VolumeHigh(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); } long VolumeLow(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW); } long Time(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME); } int Digits(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS); } int DigitsLot(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); } int Spread(void) const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD); } bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); } int OrderModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE); } ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void) const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE); } ENUM_SYMBOL_OPTION_MODE OptionMode(void) const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE); } ENUM_SYMBOL_OPTION_RIGHT OptionRight(void) const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT); } //--- Real properties double Bid(void) const { return this.GetProperty(SYMBOL_PROP_BID); } double BidHigh(void) const { return this.GetProperty(SYMBOL_PROP_BIDHIGH); } double BidLow(void) const { return this.GetProperty(SYMBOL_PROP_BIDLOW); } double Ask(void) const { return this.GetProperty(SYMBOL_PROP_ASK); } double AskHigh(void) const { return this.GetProperty(SYMBOL_PROP_ASKHIGH); } double AskLow(void) const { return this.GetProperty(SYMBOL_PROP_ASKLOW); } double Last(void) const { return this.GetProperty(SYMBOL_PROP_LAST); } double LastHigh(void) const { return this.GetProperty(SYMBOL_PROP_LASTHIGH); } double LastLow(void) const { return this.GetProperty(SYMBOL_PROP_LASTLOW); } double VolumeReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); } double VolumeHighReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); } double VolumeLowReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); } double OptionStrike(void) const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); } double Point(void) const { return this.GetProperty(SYMBOL_PROP_POINT); } double TradeTickValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); } double TradeTickValueProfit(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); } double TradeTickValueLoss(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); } double TradeTickSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); } double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } double MarginLongInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); } double MarginBuyStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); } double MarginBuyLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); } double MarginBuyStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); } double MarginLongMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); } double MarginBuyStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); } double MarginBuyLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); } double MarginBuyStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); } double MarginShortInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); } double MarginSellStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); } double MarginSellLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); } double MarginSellStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); } double MarginShortMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); } double MarginSellStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); } double MarginSellLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); } double MarginSellStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); } double SessionVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); } double SessionTurnover(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); } double SessionInterest(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); } double SessionBuyOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); } double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; double NormalizedLot(const double volume) const; double BidLast(void) const; double BidLastHigh(void) const; double BidLastLow(void) const; double AskLast(void) const; double AskLastHigh(void) const; double AskLastLow(void) const; //--- String properties string Name(void) const { return this.GetProperty(SYMBOL_PROP_NAME); } string Basis(void) const { return this.GetProperty(SYMBOL_PROP_BASIS); } string CurrencyBase(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_BASE); } string CurrencyProfit(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_PROFIT); } string CurrencyMargin(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_MARGIN); } string Bank(void) const { return this.GetProperty(SYMBOL_PROP_BANK); } string Description(void) const { return this.GetProperty(SYMBOL_PROP_DESCRIPTION); } string Formula(void) const { return this.GetProperty(SYMBOL_PROP_FORMULA); } string ISIN(void) const { return this.GetProperty(SYMBOL_PROP_ISIN); } string Page(void) const { return this.GetProperty(SYMBOL_PROP_PAGE); } string Path(void) const { return this.GetProperty(SYMBOL_PROP_PATH); } string Category(void) const { return this.GetProperty(SYMBOL_PROP_CATEGORY); } string Exchange(void) const { return this.GetProperty(SYMBOL_PROP_EXCHANGE); } //+------------------------------------------------------------------+
Já tivemos esses três tipos de métodos para o preço Bid, no entanto, eles não tinham verificação de presença de preço. Fazemos alterações neles:
//+------------------------------------------------------------------+ //| Return Bid or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID) : (this.GetProperty(SYMBOL_PROP_BID)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_BID)) ); } //+------------------------------------------------------------------+ //| Return maximum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDHIGH) : (this.GetProperty(SYMBOL_PROP_BIDHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_BIDHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDLOW) : (this.GetProperty(SYMBOL_PROP_BIDLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_BIDLOW)) ); } //+------------------------------------------------------------------+
Aqui tudo é conforme mencionado acima: verificamos o tipo de construção do gráfico e, se for construído segundo Bid, retornamos os preços Bid correspondentes, se for construído segundo Last, verificaremos que o preço Bid da propriedade de símbolo retornada seja zero. Se for zero, usaremos o último preço Last correspondente, caso contrário, o preço Bid respectivo.
Escreveremos exatamente a mesma implementação para métodos que retornam preços Ask:
//+------------------------------------------------------------------+ //| Return Ask or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASK) : (this.GetProperty(SYMBOL_PROP_ASK)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_ASK)) ); } //+------------------------------------------------------------------+ //| Return maximum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKHIGH) : (this.GetProperty(SYMBOL_PROP_ASKHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_ASKHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKLOW) : (this.GetProperty(SYMBOL_PROP_ASKLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_ASKLOW)) ); } //+------------------------------------------------------------------+
Agora, são esses métodos que usaremos na biblioteca ao calcular os níveis de preços para obter os preços Ask ou Last e Bid ou Last.
Ao exibir mensagens sobre eventos de negociação no log, agora exibimos notações adicionais para os identificadores do magic e de dois
grupos, desde que dados adicionais sejam armazenados no valor do número mágico (já implementamos
isso no artigo anterior). Mas, se o número mágico também contiver o identificador da ordem pendente, não o imprimiremos no log. Fazemos
este ajuste. Simplesmente adicionamos um par de linhas a cada uma das classes de eventos nos arquivos EventModify.mqh, EventOrderPlaced.mqh,
EventOrderRemoved.mqh, EventPositionClose.mqh e EventPositionOpen.mqh aos seus métodos de breve
descrição de evento.
Em cada um dos arquivos substituímos uma destas strings:
string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2 : "");
por estas duas:
string pend_req_id=(this.GetPendReqID()>0 ? ", ID: "+(string)this.GetPendReqID() : ""); string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2+pend_req_id : "");
Assim, adicionamos uma descrição do identificador do evento pendente (se houver) à descrição do número mágico.
À seção pública da classe que pertence ao objeto de negociação do símbolo CTradeObj no arquivo TradeObj.mqh adicionamos os métodos que retornam descrições dos campos da estrutura da ordem pendente MqlTradeRequest:
//--- Return the description of the (1) executed action type, (2) magic number, (3) order ticket, (4) volume, //--- (5) open, (6) StopLimit order, (7) StopLoss, (8) TakeProfit price, (9) deviation, //--- type of (10) order, (11) execution, (12) lifetime, (13) order expiration date, //--- (14) comment, (15) position ticket, (16) opposite position ticket string GetRequestActionDescription(void) const { return RequestActionDescription(this.m_request); } string GetRequestMagicDescription(void) const { return RequestMagicDescription(this.m_request); } string GetRequestOrderDescription(void) const { return RequestOrderDescription(this.m_request); } string GetRequestSymbolDescription(void) const { return RequestSymbolDescription(this.m_request); } string GetRequestVolumeDescription(void) const { return RequestVolumeDescription(this.m_request); } string GetRequestPriceDescription(void) const { return RequestPriceDescription(this.m_request); } string GetRequestStopLimitDescription(void) const { return RequestStopLimitDescription(this.m_request); } string GetRequestStopLossDescription(void) const { return RequestStopLossDescription(this.m_request); } string GetRequestTakeProfitDescription(void) const { return RequestTakeProfitDescription(this.m_request); } string GetRequestDeviationDescription(void) const { return RequestDeviationDescription(this.m_request); } string GetRequestTypeDescription(void) const { return RequestTypeDescription(this.m_request); } string GetRequestTypeFillingDescription(void) const { return RequestTypeFillingDescription(this.m_request); } string GetRequestTypeTimeDescription(void) const { return RequestTypeTimeDescription(this.m_request); } string GetRequestExpirationDescription(void) const { return RequestExpirationDescription(this.m_request); } string GetRequestCommentDescription(void) const { return RequestCommentDescription(this.m_request); } string GetRequestPositionDescription(void) const { return RequestPositionDescription(this.m_request); } string GetRequestPositionByDescription(void) const { return RequestPositionByDescription(this.m_request); } //--- Open a position
Os métodos simplesmente chamam as funções correspondentes que criamos anteriormente no arquivo de funções de serviço da biblioteca.
Por alguma razão, anteriormente esqueci-me da transferência do tipo de preenchimento ordens aos métodos de abertura de posição.
Adicionamos este
parâmetro à declaração do método de abertura de posições da classe:
//--- Open a position bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
e acrescentamos na implementação do método:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) {
Como no futuro implementaremos recursos para negociar usando ordens pendentes, introduzimos o conceito de tipo de ordem pendente:
No arquivo da classe de negociação Trading.mqh na classe da ordem pendente CPendingReq
adicionamos
à seção privada uma variável-membro para armazenar o tipo de ordem pendente:
//+------------------------------------------------------------------+ //| Pending request object class | //+------------------------------------------------------------------+ class CPendingReq : public CObject { private: MqlTradeRequest m_request; // Структура торгового запроса uchar m_id; // Идентификатор торгового запроса int m_type; // Тип отложенного запроса int m_retcode; // Результат, на основании которого создан запрос double m_price_create; // Цена при создании запроса ulong m_time_create; // Время создания запроса ulong m_time_activate; // Время активации очередной попытки ulong m_waiting_msc; // Продолжительность ожидания между запросами uchar m_current_attempt; // Номер текущей попытки uchar m_total_attempts; // Количество попыток
À seção pública da classe adicionamos: um método que retorne o código de
retorno do servidor com base no qual é criada a ordem pendente, um método que
retorne as descrições das propriedades da ordem pendente e o tipo de
ordem, bem como um método que exiba no log todos os dados da ordem de
negociação:
public: //--- Return (1) the request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID MqlTradeRequest MqlRequest(void) const { return this.m_request; } double PriceCreate(void) const { return this.m_price_create; } ulong TimeCreate(void) const { return this.m_time_create; } ulong TimeActivate(void) const { return this.m_time_activate; } ulong WaitingMSC(void) const { return this.m_waiting_msc; } uchar CurrentAttempt(void) const { return this.m_current_attempt; } uchar TotalAttempts(void) const { return this.m_total_attempts; } uchar ID(void) const { return this.m_id; } int Retcode(void) const { return this.m_retcode; } //--- 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) request ID void SetPriceCreate(const double price) { this.m_price_create=price; } void SetTimeCreate(const ulong time) { this.m_time_create=time; } void SetTimeActivate(const ulong time) { this.m_time_activate=time; } void SetWaitingMSC(const ulong miliseconds) { this.m_waiting_msc=miliseconds;} void SetCurrentAttempt(const uchar number) { this.m_current_attempt=number; } void SetTotalAttempts(const uchar number) { this.m_total_attempts=number; } void SetID(const uchar id) { this.m_id=id; } //--- Return the description of the (1) request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID string MqlRequestDescription(void) const { return RequestActionDescription(this.m_request); } string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TYPE) + (this.Type()==PENDING_REQUEST_ID_TYPE_ERR ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR) : CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST) ); } string PriceCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE)+": "+ ::DoubleToString(this.PriceCreate(),(int)::SymbolInfoInteger(this.m_request.symbol,SYMBOL_DIGITS)); } string TimeCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE)+TimeMSCtoString(this.TimeCreate()); } string TimeActivateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE)+TimeMSCtoString(this.TimeActivate());} string WaitingMSCDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING)+(string)this.WaitingMSC(); } string CurrentAttemptDescription(void) const { return (CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT)+ (this.CurrentAttempt()==0 ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET) : (string)this.CurrentAttempt()) ); } string TotalAttemptsDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS)+(string)this.TotalAttempts(); } string IdDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ID)+(string)this.ID(); } string RetcodeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_RETCODE)+(string)this.Retcode(); } string ReasonDescription(void) const { return CMessage::Text(this.m_retcode); } //--- Return a request type virtual int Type(void) const { return this.m_type; } //--- Display request data in the journal void Print(void); //--- Constructors CPendingReq(void){;} CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode); }; //+------------------------------------------------------------------+
Os métodos que retornam descrições sobre as propriedades do objeto da ordem pendente simplesmente criam uma descrição composta com base no
cabeçalho que descreve a propriedade e seus valores. O método Type(), que retorna o tipo de ordem pendente, é criado virtualmente, uma vez
que o objeto básico CObject (cujo herdeiro é o objeto da ordem pendente) já
vem com esse método virtual definido. E para implementar o retorno do tipo objeto-herdeiro é necessário predefinir o método no
herdeiro, o que já fizemos — agora este método retorna o valor da variável m_type, definida na classe da ordem pendente.
No construtor da classe, definimos o valor para o tipo de ordem pendente:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by IDs | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return(this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0); return 0; } //+------------------------------------------------------------------+
Como nossas ordens pendentes serão criadas de acordo com dois princípios: segundo o código de retorno do servidor e segundo a solicitação
vinda do programa, para definir o tipo de ordem pendente basta saber o código de resposta do servidor. Faremos precisamente isso: se
o valor do código de erro for maior que zero (se o servidor retornar um erro), significa
que esta solicitação foi criada segundo o código de retorno do servidor, caso contrário, se
o código for zero, o objeto da ordem pendente foi criado segundo a solicitação vinda do programa.
Modificamos o método virtual Compare() do objeto da ordem pendente.
Antigamente, ele sempre comparava os objetos
apenas segundo uma propriedade, nomeadamente segundo o identificador da ordem:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by properties | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return ( //--- Compare by ID mode==0 ? (this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0) : //--- Compare by type (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0) ); } //+------------------------------------------------------------------+
Agora, após introduzir os tipos de ordens pendentes, precisamos comparar objetos segundo duas propriedades, nomeadamente por identificador
e tipo.
Para fazer isso, alteramos o método para comparar dois objetos:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by properties | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return ( //--- Compare by ID mode==0 ? (this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0) : //--- Compare by type (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0) ); } //+------------------------------------------------------------------+
Aqui acontece a escolha das propriedades comparadas a partir do valor da variável mode. Se o valor for zero, os objetos serão comparados segundo o identificador, já se for diferente de zero, segundo os tipos de objetos.
Também fora do corpo da classe escreveremos o método que exibe no log uma descrição completa de todas as propriedades do objeto-ordem pendente:
//+------------------------------------------------------------------+ //| Display request data in the journal | //+------------------------------------------------------------------+ void CPendingReq::Print(void) { string action=" - "+RequestActionDescription(this.m_request)+"\n"; string symbol="",order="",volume="",price="",stoplimit="",sl="",tp="",deviation="",type="",type_filling=""; string type_time="",expiration="",position="",position_by="",magic="",comment="",request_data=""; string type_req=" - "+this.TypeDescription()+"\n"; if(this.m_request.action==TRADE_ACTION_DEAL) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; volume=" - "+RequestVolumeDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; deviation=" - "+RequestDeviationDescription(this.m_request)+"\n"; type=" - "+RequestTypeDescription(this.m_request)+"\n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; comment=" - "+RequestCommentDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+volume+price+sl+tp+deviation+type+type_filling+magic+comment+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_SLTP) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; position=" - "+RequestPositionDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+sl+tp+position+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_PENDING) { symbol=" - "+RequestSymbolDescription(this.m_request)+"\n"; volume=" - "+RequestVolumeDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; stoplimit=" - "+RequestStopLimitDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; type=" - "+RequestTypeDescription(this.m_request)+"\n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; comment=" - "+RequestCommentDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+symbol+volume+price+stoplimit+sl+tp+type+type_filling+type_time+expiration+magic+comment+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_MODIFY) { order=" - "+RequestOrderDescription(this.m_request)+"\n"; price=" - "+RequestPriceDescription(this.m_request)+"\n"; sl=" - "+RequestStopLossDescription(this.m_request)+"\n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+order+price+sl+tp+type_time+expiration+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_REMOVE) { order=" - "+RequestOrderDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+order+ " ==================\n" ); } else if(this.m_request.action==TRADE_ACTION_CLOSE_BY) { position=" - "+RequestPositionDescription(this.m_request)+"\n"; position_by=" - "+RequestPositionByDescription(this.m_request)+"\n"; magic=" - "+RequestMagicDescription(this.m_request)+"\n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+ action+position+position_by+magic+ " ==================\n" ); } string datas= ( " - "+this.TypeDescription()+"\n"+ " - "+this.IdDescription()+"\n"+ " - "+this.RetcodeDescription()+" \""+this.ReasonDescription()+"\"\n"+ " - "+this.TimeCreateDescription()+"\n"+ " - "+this.PriceCreateDescription()+"\n"+ " - "+this.TimeActivateDescription()+"\n"+ " - "+this.WaitingMSCDescription()+" ("+TimeToString(this.WaitingMSC()/1000,TIME_MINUTES|TIME_SECONDS)+")"+"\n"+ " - "+this.CurrentAttemptDescription()+"\n"+ " - "+this.TotalAttemptsDescription()+"\n" ); ::Print("================== ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DATAS)," ==================\n",datas,request_data); } //+------------------------------------------------------------------+
No método, nas variáveis de string são selecionadas as descrições de todas as propriedades do objeto, incluindo as descrições dos campos da estrutura da ordem de negociação, que faz parte do objeto. A quantidade de dados exibidos depende do tipo de ação executada na ordem de negociação, uma vez que o tipo de ação define o número de campos na estrutura da ordem de negociação. Por isso, é verificado o valor do campo action e são exibidos apenas os campos correspondentes a ele. Primeiro, é exibida uma descrição das variáveis do objeto e, em seguida, uma descrição dos campos da estrutura da ordem. Assim, todas as propriedades do objeto-ordem pendente são exibidas no log de acordo com o tipo de ação de negociação da ordem (action).
Anteriormente, ao método de abertura de posição na classe CTradeObj adicionamos uma propriedade adicional, nomeadamente tipo
de preenchimento de ordem.
Agora adicionamos esta mesma propriedade à definição do método privado para abertura de posição na classe
CTrading:
//--- (1) Open a position, (2) place a pending order template<typename SL,typename TP> bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
E à configuração de métodos públicos de abertura de posições Buy e Sell adicionamos essas mesmas propriedades:
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Assim, na implementação desses métodos fora do corpo da classe, também adicionamos estes parâmetros:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+
Na seção privada da classe declaramos um método que retorna o índice do
objeto-ordem na lista segundo identificador:
//--- Look for the first free pending request ID int GetFreeID(void); //--- Return the request object index in the list by ID int GetIndexPendingRequestByID(const uchar id); public:
A implementação do temporizador de classe sofreu pequenas alterações.
Código de implementação do temporizador completo
com descrição lógica nos comentários:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::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 CPendingReq *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; //--- 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(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); req_obj.Print(); } this.m_list_request.Delete(i); continue; } //--- 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) { this.m_list_request.Delete(i); continue; } //--- 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 the 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()); //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Open a position case TRADE_ACTION_DEAL : this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); 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; //--- default: break; } } } //+------------------------------------------------------------------+
Espero que este conteúdo esteja claramente escrito para estudá-lo individualmente.
No método de correção de erros RequestErrorsCorrecting() adicionamos a depuração do tipo de expiração após o recebimento do erro "data de expiração de ordem inválida" (apenas parte do código com correções):
//--- The specified type of order execution by balance is not supported if(this.IsPresentErorCode(10030)) request.type_filling=symbol_obj.GetCorrectTypeFilling(); //--- Invalid order expiration in a request - if(this.IsPresentErorCode(10022)) { //--- Set a correct order expiration type request.type_time=symbol_obj.GetCorrectTypeExpiration(); //--- if the expiration type is not supported as set by the expiration date and the expiration data is defined, reset the expiration date if(!symbol_obj.IsExpirationModeSpecified() && request.expiration>0) request.expiration=0; } //--- View the list of remaining errors and correct trading request parameters
Anteriormente, ao objeto-símbolo adicionamos novos métodos para obter preços Ask e ajustamos os métodos para obter preços Bid. Agora, precisamos substituir todas as ocorrências das strings "Ask()" e "Bid()" em toda a listagem por "AskLast()" e "BidLast()", respectivamente. Para fazer isso, é conveniente usar a função de pesquisa e substituição (Ctrl+H) e, após pesquisar o código inteiro, substituímos todas as strings necessárias. Assim, sempre que precisarmos usar Ask e Bid do objeto-símbolo, usaremos a seleção automática de preços adequados.
Por exemplo, o método para definir o preço da ordem de negociação agora ficará assim com os preços substituídos:
//+------------------------------------------------------------------+ //| Set trading request prices | //+------------------------------------------------------------------+ template <typename PS,typename SL,typename TP,typename PL> bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PS price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj) { //--- Reset prices ::ZeroMemory(this.m_request); //--- Update all data by symbol if(!symbol_obj.RefreshRates()) { this.AddErrorCodeToList(10021); // No quotes to handle the request return false; } //--- Open/close price if(price>0) { //--- price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(price)=="double") this.m_request.price=::NormalizeDouble(price,symbol_obj.Digits()); //--- price parameter type (int) - the distance has been passed else if(typename(price)=="int" || typename(price)=="uint" || typename(price)=="long" || typename(price)=="ulong") { //--- Calculate the order price switch((int)action) { //--- Pending order case ORDER_TYPE_BUY_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break; //--- Default - current position open prices default : this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); break; } } //--- unsupported price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE)); return false; } } //--- If no price is specified, use the current prices else { this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); } //--- StopLimit order price or distance if(limit>0) { //--- limit order price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(limit)=="double") this.m_request.stoplimit=::NormalizeDouble(limit,symbol_obj.Digits()); //--- limit order price parameter type (int) - the distance has been passed else if(typename(limit)=="int" || typename(limit)=="uint" || typename(limit)=="long" || typename(limit)=="ulong") { //--- Calculate a limit order price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.stoplimit=::NormalizeDouble(this.m_request.price-limit*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.stoplimit=::NormalizeDouble(this.m_request.price+limit*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported limit order price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE)); return false; } } //--- Order price stop order prices are calculated from double price_open= ( (action==ORDER_TYPE_BUY_STOP_LIMIT || action==ORDER_TYPE_SELL_STOP_LIMIT) && limit>0 ? this.m_request.stoplimit : this.m_request.price ); //--- StopLoss if(sl>0) { //--- StopLoss parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(sl)=="double") this.m_request.sl=::NormalizeDouble(sl,symbol_obj.Digits()); //--- StopLoss parameter type (int) - calculate the placement distance else if(typename(sl)=="int" || typename(sl)=="uint" || typename(sl)=="long" || typename(sl)=="ulong") { //--- Calculate the StopLoss price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.sl=::NormalizeDouble(price_open-sl*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.sl=::NormalizeDouble(price_open+sl*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported StopLoss types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE)); return false; } } //--- TakeProfit if(tp>0) { //--- TakeProfit parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(tp)=="double") this.m_request.tp=::NormalizeDouble(tp,symbol_obj.Digits()); //--- TakeProfit parameter type (int) - calculate the placement distance else if(typename(tp)=="int" || typename(tp)=="uint" || typename(tp)=="long" || typename(tp)=="ulong") { if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.tp=::NormalizeDouble(price_open+tp*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.tp=::NormalizeDouble(price_open-tp*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported TakeProfit types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE)); return false; } } //--- All prices are recorded return true; } //+------------------------------------------------------------------+
Aqui não forneceremos todos os códigos com as substituições feitas, uma vez que todos já foram corrigidos e estão nos arquivos anexados no final do artigo.
Na implementação do método privado para definir ordens pendentes, adicionamos um bloco para criar uma ordem de negociação pendente quando um erro for recebido do servidor:
//+------------------------------------------------------------------+ //| Place a pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> bool CTrading::PlaceOrder(const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_stop, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return false; } //--- Get a trading object from a symbol object CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } //--- Set the prices //--- If failed to set - write the "internal error" flag, set the error code in the return structure, //--- display the message in the journal and return 'false' if(!this.SetPrices(order_type,price_stop,sl,tp,price_limit,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // No quotes to process the request return false; } //--- In case of trading limitations, funds insufficiency, //--- there are limitations on StopLevel - play the error sound and exit this.m_request.volume=volume; this.m_request.type_filling=type_filling; this.m_request.type_time=type_time; this.m_request.expiration=expiration; ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume, this.m_request.price, action, order_type, symbol_obj, trade_obj, DFUN, this.m_request.stoplimit, this.m_request.sl, this.m_request.tp); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) { //--- If trading is completely disabled if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "abort trading operation" - set the last error code to the return structure, //--- display a journal message, play the error sound and exit if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal if(method==ERROR_CODE_PROCESSING_METHOD_WAIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); //--- Instead of creating a pending request, we temporarily wait the required time period (the CheckErrors() method result is returned) ::Sleep(method); symbol_obj.Refresh(); } //--- If the check result is "create a pending request", do nothing temporarily if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); } } //--- In the loop by the number of attempts for(int i=0;i<this.m_total_try;i++) { //--- Send the request res=trade_obj.SetOrder(order_type, this.m_request.volume, this.m_request.price, this.m_request.sl, this.m_request.tp, this.m_request.stoplimit, magic, comment, this.m_request.expiration, this.m_request.type_time, this.m_request.type_filling); //--- If the request is executed successfully or the asynchronous order sending mode is set, play the success sound //--- set for a symbol trading object for this type of trading operation and return 'true' if(res || trade_obj.IsAsyncMode()) { if(this.IsUseSounds()) trade_obj.PlaySoundSuccess(action,order_type); return true; } //--- If the request is not successful, play the error sound set for a symbol trading object for this type of trading operation else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode())); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); method=this.ResultProccessingMethod(trade_obj.GetResultRetcode()); //--- If "Disable trading for the EA" is received as a result of sending a request, enable the disabling flag and end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { this.SetTradingDisableFlag(true); break; } //--- If "Exit the trading method" is received as a result of sending a request, end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { break; } //--- If "Correct the parameters and repeat" is received as a result of sending a request - //--- correct the parameters and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT) { this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj); continue; } //--- If "Update data and repeat" is received as a result of sending a request - //--- update data and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH) { symbol_obj.Refresh(); continue; } //--- If "Wait and repeat" is received as a result of sending a request //--- create a pending request and end the loop if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- If the trading request magic number, has no pending request ID if(this.GetPendReqID((uint)magic)==0) { //--- Waiting time in milliseconds: //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value, //--- for the "Create a pending request" handling method - till there is a zero waiting time ulong wait=(method>ERROR_CODE_PROCESSING_METHOD_PENDING ? method : 0); //--- 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 request ID to the magic number, while a symbol name is set in the request structure, //--- set the trading operation and order types, comment and position type, filling and expiration type uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); this.m_request.magic=mn; this.m_request.symbol=symbol_obj.Name(); this.m_request.action=TRADE_ACTION_PENDING; this.m_request.type=order_type; this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : trade_obj.GetTypeExpiration()); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Pass the number of trading attempts to the pending request uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj); break; } } } } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+
Todas as ações para criar uma ordem pendente são descritas nos comentários do código. De qualquer forma, o que não estiver claro pode ser discutido nas comentários do artigo.
Para simplificar a depuração, visando ver o resultado da criação de ordem pendente, no método de criação de objeto-ordem pendente fazemos uma adição para exibirmos no log as propriedades da ordem recém-criada:
//+------------------------------------------------------------------+ //| Create a pending request | //+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const uchar id,const uchar attempts,const ulong wait,const MqlTradeRequest &request,const int retcode,CSymbol *symbol_obj) { //--- Create a new pending request object CPendingReq *req_obj=new CPendingReq(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); 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; } //--- Filled in the fields 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(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.Print(); } return true; } //+------------------------------------------------------------------+
No final da listagem da classe de negociação escrevemos a implementação do método que retorna o índice do objeto-ordem na lista segundo identificador:
//+------------------------------------------------------------------+ //| Return the request object index in the list by ID | //+------------------------------------------------------------------+ int CTrading::GetIndexPendingRequestByID(const uchar id) { CPendingReq *req=new CPendingReq(); if(req==NULL) return WRONG_VALUE; req.SetID(id); this.m_list_request.Sort(); int index=this.m_list_request.Search(req); delete req; return index; } //+------------------------------------------------------------------+
Ao método é transferido o identificador solicitado, é criado objeto-ordem temporário e para ele é definido o identificador passado para o método.
Em seguida, à lista contendo objetos-ordens atribuímos o sinalizador da lista classificada. Por padrão, o modo de classificação é zero e, nesse modo, no método Compare() da classe CPendingReq já criamos a comparação de objetos segundo identificador. Por isso, com segurança podemos usar o método de pesquisa de objeto Search() na matriz dinâmica de ponteiros para objetos. O método retorna o índice do objeto encontrado na lista ou -1 se o objeto não for encontrado. Antes de sair do método excluímos o objeto-ordem temporária e retornamos o índice recebido do objeto encontrado ou -1.
Tudo o que precisamos fazer é à classe do objeto básico da biblioteca CEngine nas definições de seus métodos para enviar ordens de negociação adicionar um parâmetro indicando o tipo de preenchimento de ordens.
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order template<typename PR,typename SL,typename TP> bool PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Устанавливает отложенный ордер (1) SellStop, (2) SellLimit, (3) SellStopLimit template<typename PR,typename SL,typename TP> bool PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Se o valor padrão for -1, os valores corretos de tipos de preenchimento de ordem serão obtidos dos objetos de negociação dos símbolos com base nos quais será realizada a transação.
Adicionamos os mesmos parâmetros aos códigos de implementação desses métodos de negociação:
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenBuy(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenSell(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+
Na implementação atual, essas são todas as melhorias e mudanças necessárias.
Teste
Para testar o posicionamento de ordens pendentes, pegaremos o EA do artigo anterior e o salvaremos na nova pasta \MQL5\Experts\TestDoEasy\Part27\ usando o novo nome TestDoEasyPart27.mq5.
No EA, na função de inicialização da biblioteca adicionamos a definição de valores corretos para tipos de ordens e para seus tipos de expiração (para todos os objetos de negociação de todos os símbolos utilizados no EA):
//+------------------------------------------------------------------+ //| Initializing DoEasy library | //+------------------------------------------------------------------+ void OnInitDoEasy() { //--- Check if working with the full list is selected used_symbols_mode=InpModeUsedSymbols; if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL) { int total=SymbolsTotal(false); string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов."; string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols."; string caption=TextByLanguage("Внимание!","Attention!"); string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\""; string en="Full list mode selected.\nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\""; string message=TextByLanguage(ru,en); int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2); int mb_res=MessageBox(message,caption,flags); switch(mb_res) { case IDNO : used_symbols_mode=SYMBOLS_MODE_CURRENT; break; default: break; } } //--- Fill in the array of used symbols used_symbols=InpUsedSymbols; CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols); //--- Set the type of the used symbol list in the symbol collection engine.SetUsedSymbols(array_used_symbols); //--- Displaying the selected mode of working with the symbol object collection Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Number of used symbols: ",". Number of symbols used: "),engine.GetSymbolsCollectionTotal()); //--- Create resource text files engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red); //--- Pass all existing collections to the trading class engine.TradingOnInit(); //--- Set the default magic number for all used symbols engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number)); //--- Set synchronous passing of orders for all used symbols engine.TradingSetAsyncMode(false); //--- Set the number of trading attempts in case of an error engine.TradingSetTotalTry(InpTotalAttempts); //--- Set correct order expiration and filling types to all trading objects engine.TradingSetCorrectTypeExpiration(); engine.TradingSetCorrectTypeFilling(); //--- Set standard sounds for trading objects of all used symbols engine.SetSoundsStandart(); //--- Set the general flag of using sounds engine.SetUseSounds(InpUseSounds); //--- Set the spread multiplier for symbol trading objects in the symbol collection engine.SetSpreadMultiplier(InpSpreadMultiplier); //--- Set controlled values for symbols //--- Get the list of all collection symbols CArrayObj *list=engine.GetListAllUsedSymbols(); if(list!=NULL && list.Total()!=0) { //--- In a loop by the list, set the necessary values for tracked symbol properties //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program /* for(int i=0;i<list.Total();i++) { CSymbol* symbol=list.At(i); if(symbol==NULL) continue; //--- Set control of the symbol price increase by 100 points symbol.SetControlBidInc(100000*symbol.Point()); //--- Set control of the symbol price decrease by 100 points symbol.SetControlBidDec(100000*symbol.Point()); //--- Set control of the symbol spread increase by 40 points symbol.SetControlSpreadInc(400); //--- Set control of the symbol spread decrease by 40 points symbol.SetControlSpreadDec(400); //--- Set control of the current spread by the value of 40 points symbol.SetControlSpreadLevel(400); } */ } //--- Set controlled values for the current account CAccount* account=engine.GetAccountCurrent(); if(account!=NULL) { //--- Set control of the profit increase to 10 account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0); //--- Set control of the funds increase to 15 account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0); //--- Set profit control level to 20 account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0); } } //+------------------------------------------------------------------+
E, curiosamente, essas são todas as mudanças no EA. Tudo o resto hoje já foi feito por nós nos códigos da biblioteca.
Para testar o posicionamento de ordens de negociação pendentes, faremos exatamente o mesmo da última vez, isto é, desligamos a Internet, tentamos definir uma ordem pendente, recebemos um erro de falta de comunicação com o servidor de negociação, recebemos uma mensagem no log sobre a criação da ordem pendente e a exibição dos seus parâmetros. Em seguida, reconectamos a Internet e, como resultado, devemos obter o acionamento da ordem pendente e a definição da ordem pendente solicitada.
Conferimos.
Compilamos e iniciamos o EA. Desligamos a Internet de qualquer forma e aguardamos que apareça esse ícone no canto inferior direito do terminal:
Após desconectar a Internet e clicar no botão Sell, o servidor de negociação retorna um erro e no log são exibidas as entradas sobre erro e sobre
criação da ordem pendente:
Em seguida, conectamos a Internet, restaurando assim a comunicação com o servidor de negociação:
Assim que a conexão é restaurada, a biblioteca começa a processar a ordem pendente, enviando-a para o servidor.
Como resultado, temos uma ordem pendente definida e uma entrada no log:
2019.12.05 16:38:32.591 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request: 2019.12.05 16:38:32.591 No connection with the trade server 2019.12.05 16:38:32.591 Correction of trade request parameters ... 2019.12.05 16:38:32.610 Trading attempt #1. Error: No connection with the trade server 2019.12.05 16:38:32.610 Pending request created #1: 2019.12.05 16:38:32.610 ================== Pending trade request's parameters ================== 2019.12.05 16:38:32.610 - Pending request type: Pending request that was created as a result of the server code 2019.12.05 16:38:32.610 - Trade request ID: 1 2019.12.05 16:38:32.610 - Return code based on which the request was created: 10031 "No connection with the trade server" 2019.12.05 16:38:32.610 - Request creation time: 2019.12.05 11:37:39.054 2019.12.05 16:38:32.610 - Price at time of request create: : 1.10913 2019.12.05 16:38:32.610 - Request activation time: 2019.12.05 11:37:59.054 2019.12.05 16:38:32.610 - Waiting time between trading attempts: 20000 (00:00:20) 2019.12.05 16:38:32.610 - Current trading attempt: Waiting for the onset time of the first trading attempt 2019.12.05 16:38:32.610 - Total trade attempts: 5 2019.12.05 16:38:32.610 ================== Trade request's parameters ================== 2019.12.05 16:38:32.610 - Trade operation type: Place pending order 2019.12.05 16:38:32.610 - Trade symbol: EURUSD 2019.12.05 16:38:32.610 - Requested volume for a deal in lots: 0.10 2019.12.05 16:38:32.610 - Price: 1.10963 2019.12.05 16:38:32.610 - StopLimit level of the order: Value not set 2019.12.05 16:38:32.610 - Stop Loss level of the order: 1.11113 2019.12.05 16:38:32.610 - Take Profit level of the order: 1.10813 2019.12.05 16:38:32.610 - Order type: Pending order Sell Limit 2019.12.05 16:38:32.610 - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK) 2019.12.05 16:38:32.610 - Order expiration type: Good till cancel order 2019.12.05 16:38:32.610 - Order expiration time: Value not set 2019.12.05 16:38:32.610 - Magic number: 24379515 2019.12.05 16:38:32.610 - Order comment: "Pending order SellLimit" 2019.12.05 16:38:32.610 ================== 2019.12.05 16:38:32.610 2019.12.05 16:38:45.185 Retry trading attempt #1 2019.12.05 16:38:45.185 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.185 Trading is prohibited for the current account 2019.12.05 16:38:45.185 Correction of trade request parameters ... 2019.12.05 16:38:45.185 Trading operation aborted 2019.12.05 16:38:45.512 Retry trading attempt #2 2019.12.05 16:38:45.512 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.512 Trading is prohibited for the current account 2019.12.05 16:38:45.512 Correction of trade request parameters ... 2019.12.05 16:38:45.512 Trading operation aborted 2019.12.05 16:38:45.852 Retry trading attempt #3 2019.12.05 16:38:46.405 - Pending order placed: 2019.12.05 11:38:45.933 - 2019.12.05 16:38:46.405 EURUSD Placed 0.10 Pending order Sell Limit #491179168 at price 1.10963, sl 1.11113, tp 1.10813, Magic number 24379515 (123), G1: 4, G2: 7, ID: 1 2019.12.05 16:38:46.472 OnDoEasyEvent: Pending order placed
O que vemos aqui:
Primeiro, obtemos um erro vindo do servidor de negociação "Sem conexão com o servidor de
negociação".
Em seguida, recebemos uma mensagem sobre a criação de uma
ordem de negociação pendente com o identificador #1, na qual todos estão registrados os parâmetros
do objeto e os parâmetros de estrutura de ordem neste
objeto.
Depois disso, são executadas duas tentativas de
negociação repetidas #1 e #2, enviadas a partir da ordem de negociação pendente, que retorna o erro da falta de permissão para negociar na
conta (após reconectar, não há tempo de ativar a permissão para negociar na conta).
E, da
terceira tentativa, enviada a partir do objeto-ordem pendente de negociação, já é possível definir a ordem pendente.
Na descrição do número mágico da ordem pendente colocada, temos o número mágico 24379515, o identificador do mágico definido nos parâmetros do EA (123), o identificador do primeiro grupo "G1: 4", o identificador do segundo grupo "G2: 7" e o identificador da ordem pendente "ID: 1"
Lembre-se:
Chamo bastante a atenção para o fato de que o resultado da classe de negociação com as ordens pendentes descritas neste artigo e o EA de teste anexado ao artigo não devem, em caso algum, ser usados para negociação real!Este artigo, seus materiais e o resultado são apenas uma continuação da ideia de teste de ordens pendentes e, nesta apresentação, não são um produto acabado para uso em contas reais, mas, sim, para uso somente em modo de demonstração ou no testador.
O que vem agora?
No próximo artigo, continuaremos trabalhando nos recursos básicos de ordens pendentes, nomeadamente, na modificação, exclusão e fechamento de ordens/posições.
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 resultados
Parte
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 básico 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
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7418
- 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