Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXV): processamento de erros retornados pelo servidor de negociação
Artyom Trishkin | 1 março, 2020
Sumário
Ideia
Bem... Em nossa classe de negociação, já criamos um controle tanto de parâmetros
válidos de terminal, como de conta e de símbolo para negociar, realizamos a correção
automática de parâmetros mal definidos da ordem de negociação, e agora resta tratar do processamento das respostas do servidor
relativamente à ordem de negociação preparada e enviada.
Depois de enviarmos uma ordem de negociação para o servidor, em vez de
assumirmos que o trabalho está concluído, precisamos examinar o que recebemos em resposta à ordem de negociação. Nessa ordem de ideias, o
servidor retorna códigos de erro ou a ausência de erros. Precisamos receber e processar esses códigos, caso o servidor retorne um erro.
Vamos processá-lo exatamente da mesma maneira que processamos os parâmetros inválidos da ordem de negociação:
- Não há erros — ordem enfileirada com êxito para ser executada,
- Restringir negociação do EA — por exemplo, uma restrição completa, por parte do servidor, para realizar operações de
negociação,
- Sair do método de negociação — por exemplo, não há hipótese de conseguir enviar com êxito a ordem ao servidor, a posição
já está encerrada ou a ordem pendente foi removida,
- Corrigir os parâmetros da ordem de negociação e tentar novamente — existem alguns valores errados nos
parâmetros da ordem de negociação; provavelmente durante a preparação da solicitação ao servidor, os dados foram alterados e agora é
preciso corrigi-los,
- Atualizar dados e tentar novamente — os dados foram alterados no servidor, mas não é necessário ajustar os valores da
ordem de negociação,
- Esperar e tentar novamente — é necessária alguma espera, por exemplo, se o preço estiver próximo a um dos níveis de stop de
posição, o parâmetro FreezeLevel restringe a modificação, pois a ordem de stop está prestes a ser acionada. A espera permite que
aguardar até que a ordem de stop seja acionada e a ordem de negociação cancelada ou até o preço para sair da zona de congelamento e enviar a
ordem ao servidor com êxito,
- Criar ordem pendente — falaremos sobre isso no próximo artigo.
Existem apenas mais códigos de retorno do que antes ao corrigir possíveis erros na ordem de negociação, e nem todo código pode ser corrigido e nem
toda ordem pode ser repetida. Mas, para excluir erros corrigíveis, tentaremos processá-los e reenviar a ordem de negociação.
Nos métodos para enviar ordens de negociação, após uma verificação preliminar das restrições e dos erros na ordem de negociação, realizamos
um ciclo para reenviar a ordem de negociação ao servidor. Isto é, se após a primeira solicitação ao servidor recebermos um erro, enviaremos a
ordem de negociação tantas vezes quanto estabelecido o número de tentativas de negociação para a classe de negociação, ou até que a ordem
seja enviada com sucesso ao servidor ou até o final do número de tentativas.
Após a conclusão sem êxito de todas as tentativas de envio de
ordem ao servidor, a partir do método de negociação retornaremos false, assim, no
programa de chamada, neste caso, poderemos ver o código do último erro retornado pelo servidor de negociação, para, desse modo, tomar uma
decisão sobre o processamento do erro em questão.
Terminamos com a teoria, agora deitemos mãos à obra.
Implementação
À classe de conta CAccount ao arquivo Account.mqh à seção de acesso simplificado às propriedades do
objeto-conta
adicionamos o método que retorna o sinalizador indicando trabalho
em conta hedge:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the account object properties | //+------------------------------------------------------------------+ //--- Return the account's integer properties ENUM_ACCOUNT_TRADE_MODE TradeMode(void) const { return (ENUM_ACCOUNT_TRADE_MODE)this.GetProperty(ACCOUNT_PROP_TRADE_MODE); } ENUM_ACCOUNT_STOPOUT_MODE MarginSOMode(void) const { return (ENUM_ACCOUNT_STOPOUT_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_SO_MODE); } ENUM_ACCOUNT_MARGIN_MODE MarginMode(void) const { return (ENUM_ACCOUNT_MARGIN_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_MODE); } long Login(void) const { return this.GetProperty(ACCOUNT_PROP_LOGIN); } long Leverage(void) const { return this.GetProperty(ACCOUNT_PROP_LEVERAGE); } long LimitOrders(void) const { return this.GetProperty(ACCOUNT_PROP_LIMIT_ORDERS); } long TradeAllowed(void) const { return this.GetProperty(ACCOUNT_PROP_TRADE_ALLOWED); } long TradeExpert(void) const { return this.GetProperty(ACCOUNT_PROP_TRADE_EXPERT); } long CurrencyDigits(void) const { return this.GetProperty(ACCOUNT_PROP_CURRENCY_DIGITS); } long ServerType(void) const { return this.GetProperty(ACCOUNT_PROP_SERVER_TYPE); } long FIFOClose(void) const { return this.GetProperty(ACCOUNT_PROP_FIFO_CLOSE); } bool IsHedge(void) const { return this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } //--- Return the account's real properties
Ao arquivo Defines.mqh adicionamos a substituição de macro para indicar o
número padrão de tentativas de negociação para uma classe de negociação.
Como hoje prepararemos adicionalmente a base para a
criação de ordens pendentes e precisamos de um temporizador para a classe de negociação,
imediatamente inserimos
os parâmetros do temporizador da classe de negociação:
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ //--- Describe the function with the error line number #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Function description" #define COUNTRY_LANG ("Russian") // Country language #define END_TIME (D'31.12.3000 23:59:59') // End date for account history data requests #define TIMER_FREQUENCY (16) // Minimal frequency of the library timer in milliseconds #define TOTAL_TRY (5) // Default number of trading attempts //--- Standard sounds #define SND_ALERT "alert.wav" #define SND_ALERT2 "alert2.wav" #define SND_CONNECT "connect.wav" #define SND_DISCONNECT "disconnect.wav" #define SND_EMAIL "email.wav" #define SND_EXPERT "expert.wav" #define SND_NEWS "news.wav" #define SND_OK "ok.wav" #define SND_REQUEST "request.wav" #define SND_STOPS "stops.wav" #define SND_TICK "tick.wav" #define SND_TIMEOUT "timeout.wav" #define SND_WAIT "wait.wav" //--- Parameters of the orders and deals collection timer #define COLLECTION_ORD_PAUSE (250) // Orders and deals collection timer pause in milliseconds #define COLLECTION_ORD_COUNTER_STEP (16) // Increment of the orders and deals collection timer counter #define COLLECTION_ORD_COUNTER_ID (1) // Orders and deals collection timer counter ID //--- Parameters of the account collection timer #define COLLECTION_ACC_PAUSE (1000) // Account collection timer pause in milliseconds #define COLLECTION_ACC_COUNTER_STEP (16) // Account timer counter increment #define COLLECTION_ACC_COUNTER_ID (2) // Account timer counter ID //--- Symbol collection timer 1 parameters #define COLLECTION_SYM_PAUSE1 (100) // Pause of the symbol collection timer 1 in milliseconds (for scanning market watch symbols) #define COLLECTION_SYM_COUNTER_STEP1 (16) // Increment of the symbol timer 1 counter #define COLLECTION_SYM_COUNTER_ID1 (3) // Symbol timer 1 counter ID //--- Symbol collection timer 2 parameters #define COLLECTION_SYM_PAUSE2 (300) // Pause of the symbol collection timer 2 in milliseconds (for events of the market watch symbol list) #define COLLECTION_SYM_COUNTER_STEP2 (16) // Increment of the symbol timer 2 counter #define COLLECTION_SYM_COUNTER_ID2 (4) // Symbol timer 2 counter ID //--- Trading class timer parameters #define COLLECTION_REQ_PAUSE (300) // Trading class timer pause in milliseconds #define COLLECTION_REQ_COUNTER_STEP (16) // Trading class timer counter increment #define COLLECTION_REQ_COUNTER_ID (5) // Trading class timer counter ID //--- Collection list IDs #define COLLECTION_HISTORY_ID (0x7779) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777A) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777B) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777C) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777D) // Symbol collection list ID //--- Data parameters for file operations #define DIRECTORY ("DoEasy\\") // Library directory for storing object folders #define RESOURCE_DIR ("DoEasy\\Resource\\") // Library directory for storing resource folders //--- Symbol parameters #define CLR_DEFAULT (0xFF000000) // Default color #define SYMBOLS_COMMON_TOTAL (1000) // Total number of working symbols //+------------------------------------------------------------------+
À lista de sinalizadores de métodos de processamento de erros do servidor de negociação adicionamos dois sinalizadores: sinalizador
de erro no preço da ordem pendente e sinalizador de erro no preço da ordem stop limit,
enquanto aos métodos de processamento de erros e de códigos de retorno de erros do servidor adicionamos o método
de correção de parâmetros da ordem de negociação:
//+------------------------------------------------------------------+ //| Flags indicating the trading request error handling methods | //+------------------------------------------------------------------+ enum ENUM_TRADE_REQUEST_ERR_FLAGS { TRADE_REQUEST_ERR_FLAG_NO_ERROR = 0, // No error TRADE_REQUEST_ERR_FLAG_FATAL_ERROR = 1, // Disable trading for an EA (critical error) - exit TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR = 2, // Library internal error - exit TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST = 4, // Error in the list - handle (ENUM_ERROR_CODE_PROCESSING_METHOD) TRADE_REQUEST_ERR_FLAG_PRICE_ERROR = 8, // Placement price error TRADE_REQUEST_ERR_FLAG_LIMIT_ERROR = 16, // Limit order price error }; //+------------------------------------------------------------------+ //| The methods of handling errors and server return codes | //+------------------------------------------------------------------+ enum ENUM_ERROR_CODE_PROCESSING_METHOD { ERROR_CODE_PROCESSING_METHOD_OK, // No errors ERROR_CODE_PROCESSING_METHOD_DISABLE, // Disable trading for the EA ERROR_CODE_PROCESSING_METHOD_EXIT, // Exit the trading method ERROR_CODE_PROCESSING_METHOD_CORRECT, // Correct trading request parameters and repeat ERROR_CODE_PROCESSING_METHOD_REFRESH, // Update data and repeat ERROR_CODE_PROCESSING_METHOD_PENDING, // Create a pending request ERROR_CODE_PROCESSING_METHOD_WAIT, // Wait and repeat }; //+------------------------------------------------------------------+
No arquivo Datas.mqh inserimos índices de novas mensagens:
//--- CTrading MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED, // Trade operations are not allowed in the terminal (the AutoTrading button is disabled) MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED, // EA is not allowed to trade (F7 --> Common --> Allow Automated Trading) MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED, // Trading is disabled for the current account MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED, // Trading on the trading server side is disabled for EAs on the current account MSG_LIB_TEXT_REQUEST_REJECTED_DUE, // Request was rejected before sending to the server due to: MSG_LIB_TEXT_INVALID_REQUEST, // Invalid request: MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR, // Insufficient funds for performing a trade MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED, // Exceeded maximum allowed aggregate volume of orders and positions in one direction MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME, // Request volume is less than the minimum acceptable one MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME, // Request volume exceeds the maximum acceptable one MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED, // Close by is disabled MSG_LIB_TEXT_INVALID_VOLUME_STEP, // Request volume is not a multiple of the minimum lot change step gradation MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL, // Symbols of opposite positions are not equal MSG_LIB_TEXT_SL_LESS_STOP_LEVEL, // StopLoss violates requirements for symbol's StopLevel MSG_LIB_TEXT_TP_LESS_STOP_LEVEL, // TakeProfit violates requirements for symbol's StopLevel MSG_LIB_TEXT_PRICE_LESS_STOP_LEVEL, // Order distance in points is less than a value allowed by symbol's StopLevel parameter MSG_LIB_TEXT_LIMIT_LESS_STOP_LEVEL, // Limit order distance in points relative to a stop order is less than a value allowed by symbol's StopLevel parameter MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL, // The distance from the price to StopLoss is less than a value allowed by symbol's FreezeLevel parameter MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL, // The distance from the price to TakeProfit is less than a value allowed by symbol's FreezeLevel parameter MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL, // The distance from the price to an order activation level is less than a value allowed by symbol's FreezeLevel parameter MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE, // Unsupported StopLoss parameter type (should be 'int' or 'double') MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE, // Unsupported TakeProfit parameter type (should be 'int' or 'double') MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE, // Unsupported price parameter type (should be 'int' or 'double') MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE, // Unsupported limit order price parameter type (should be 'int' or 'double') MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ, // Unsupported price parameter type in a request MSG_LIB_TEXT_TRADING_DISABLE, // Trading disabled for the EA until the reason is eliminated MSG_LIB_TEXT_TRADING_OPERATION_ABORTED, // Trading operation is interrupted MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST, // Correcting trading request parameters MSG_LIB_TEXT_CREATE_PENDING_REQUEST, // Creating a pending request MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT, // Unable to correct a lot MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ, // Failed to create a pending request MSG_LIB_TEXT_TRY_N, // Trading attempt # };
e os textos destas mensagens:
{"Дистанция установки ордера в пунктах меньше разрешённой параметром StopLevel символа","Distance to place order in points less than allowed by symbol's StopLevel"}, {"Дистанция установки лимит-ордера относительно стоп-ордера меньше разрешённой параметром StopLevel символа","Distance to place limit order relative to stop order less than allowed by symbol's StopLevel"}, {"Дистанция от цены до StopLoss меньше разрешённой параметром FreezeLevel символа","Distance from price to StopLoss less than allowed by symbol's FreezeLevel"}, {"Дистанция от цены до TakeProfit меньше разрешённой параметром FreezeLevel символа","Distance from price to TakeProfit less than allowed by symbol's FreezeLevel"}, {"Дистанция от цены до цены срабатывания ордера меньше разрешённой параметром FreezeLevel символа","Distance from price to order triggering price less than allowed by symbol's FreezeLevel"}, {"Неподдерживаемый тип параметра StopLoss (необходимо int или double)","Unsupported StopLoss parameter type (int or double required)"}, {"Неподдерживаемый тип параметра TakeProfit (необходимо int или double)","Unsupported TakeProfit parameter type (int or double required)"}, {"Неподдерживаемый тип параметра цены (необходимо int или double)","Unsupported price parameter type (int or double required)"}, {"Неподдерживаемый тип параметра цены limit-ордера (необходимо int или double)","Unsupported type of price parameter for limit order (int or double required)"}, {"Неподдерживаемый тип параметра цены в запросе","Unsupported price parameter type in request"}, {"Торговля отключена для эксперта до устранения причины запрета","Trading for expert disabled till this ban eliminated"}, {"Торговая операция прервана","Trading operation aborted"}, {"Корректировка параметров торгового запроса ...","Correction of trade request parameters ..."}, {"Создание отложенного запроса","Create pending request"}, {"Нет возможности скорректировать лот","Unable to correct lot"}, {"Не удалось создать отложенный запрос","Failed to create pending request"}, {"Торговая попытка #","Trading attempt #"}, };
No arquivo do objeto básico de negociaçãoTradeObj.mqh foram feitas pequenas alterações.
Ao método para
definir ordens pendentes foi adicionado o parâmetro tipo de ordem segundo
execução
(por algum motivo, esqueci-me e não o fiz imediatamente, em vez disso, usei o definido por padrão):
//--- Place an order bool SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=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);
Agora, se transferido um valor maior que -1, será usado o valor transferido ao método, caso contrário, o valor padrão:
//+------------------------------------------------------------------+ //| Set an order | //+------------------------------------------------------------------+ bool CTradeObj::SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=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) { ::ResetLastError(); //--- If an invalid order type has been passed, write the error code and description, send the message to the journal and return 'false' if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY #ifdef __MQL4__ || type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT #endif ) { this.m_result.retcode=MSG_LIB_SYS_INVALID_ORDER_TYPE; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),OrderTypeDescription(type)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_PENDING; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.volume = volume; this.m_request.type = type; this.m_request.stoplimit = price_stoplimit; this.m_request.price = price; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.expiration = expiration; this.m_request.type_time = (type_time>WRONG_VALUE ? type_time : this.m_type_time); this.m_request.type_filling= (type_filling>WRONG_VALUE ? type_filling : this.m_type_filling); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server #ifdef __MQL5__ return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result)); #else ::ResetLastError(); int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE); ::SymbolInfoTick(this.m_symbol,this.m_tick); if(ticket!=WRONG_VALUE) { this.m_result.retcode=::GetLastError(); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.order=ticket; this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price); this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume); this.m_result.comment=CMessage::Text(this.m_result.retcode); return true; } else { this.m_result.retcode=::GetLastError(); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.comment=CMessage::Text(this.m_result.retcode); return false; } #endif } //+------------------------------------------------------------------+
Também foram corrigidos os erros nas ordens de negociação, pois anteriormente se o gráfico era plotado segundo o preço Last, o preço na ordem de
negociação era definido como Ask e Last. Agora sempre é definido como Ask e Bid, independentemente do preço do gráfico.
Outras pequenas alterações podem ser vistas nos arquivos anexados no final do artigo, uma vez que elas são insignificantes e não faz sentido nós
debruçar sobre elas aqui.
Ao arquivo Trading.mqh da classe de negociação CTrading na sua seção privada inserimos a lista
de ordens pendentes e a variável para armazenar o número de tentativas de
negociação:
//+------------------------------------------------------------------+ //| Trading class | //+------------------------------------------------------------------+ class CTrading { private: CAccount *m_account; // Pointer to the current account object CSymbolsCollection *m_symbols; // Pointer to the symbol collection list CMarketCollection *m_market; // Pointer to the list of the collection of market orders and positions CHistoryCollection *m_history; // Pointer to the list of the collection of historical orders and deals CArrayObj m_list_request; // List of pending requests CArrayInt m_list_errors; // Error list bool m_is_trade_disable; // Flag disabling trading bool m_use_sound; // The flag of using sounds of the object trading events uchar m_total_try; // Number of trading attempts ENUM_LOG_LEVEL m_log_level; // Logging level MqlTradeRequest m_request; // Trading request prices ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Flags of error source in a trading method ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error
Mais para frente, na lista de ordens de negociação, armazenaremos objetos da classe de ordem pendente e na variável m_total_try inseriremos o número de tentativas de negociação definido por padrão para a classe de negociação em seu construtor:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTrading::CTrading() { this.m_list_errors.Clear(); this.m_list_errors.Sort(); this.m_list_request.Clear(); this.m_list_request.Sort(); this.m_total_try=TOTAL_TRY; this.m_log_level=LOG_LEVEL_ALL_MSG; this.m_is_trade_disable=false; this.m_err_handling_behavior=ERROR_HANDLING_BEHAVIOR_CORRECT; ::ZeroMemory(this.m_request); } //+------------------------------------------------------------------+
Aqui limpamos a lista de ordens pendentes e definimos o sinalizador de lista
classificada.
Aos parâmetros do método para verificar preços em relação ao StopLevel adicionamos
o preço de ordem limitada definido para uma ordem do tipo StopLimit:
bool CheckPriceByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj,const double limit=0);
E ao próprio método adicionamos uma verificação:
//+------------------------------------------------------------------+ //| Return the flag checking the validity of the distance | //| from the price to the placement level by StopLevel | //+------------------------------------------------------------------+ bool CTrading::CheckPriceByStopLevel(const ENUM_ORDER_TYPE order_type,const double price,const CSymbol *symbol_obj,const double limit=0) { double lv=symbol_obj.TradeStopLevel()*symbol_obj.Point(); double pr=(this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? symbol_obj.Ask() : symbol_obj.Bid()); return (limit==0 ? //--- Order placement prices relative to the price ( order_type==ORDER_TYPE_SELL_STOP || order_type==ORDER_TYPE_SELL_STOP_LIMIT || order_type==ORDER_TYPE_BUY_LIMIT ? price<(pr-lv) : order_type==ORDER_TYPE_BUY_STOP || order_type==ORDER_TYPE_BUY_STOP_LIMIT || order_type==ORDER_TYPE_SELL_LIMIT ? price>(pr+lv) : true ) : //--- Limit order placement prices relative to the stop order price ( order_type==ORDER_TYPE_BUY_STOP_LIMIT ? limit<(price-lv) : order_type==ORDER_TYPE_SELL_STOP_LIMIT ? limit>(price+lv) : true ) ); } //+------------------------------------------------------------------+
Neste caso: se o preço da ordem limitada for zero, verificaremos os preços de ordens stop e de ordens limitadas, caso contrário, examinamos os preços de ordens stop limit (preço para definir a ordem limitada em relação ao preço para definir uma ordem stop, que desencadeia uma ordem stop limit).
Ao método que retorna como processar erros vamos transferir o código de erro, e ao método de correção de erro vamos adicionar o ponteiro para o objeto de negociação:
//--- Return the error handling method ENUM_ERROR_CODE_PROCESSING_METHOD ResultProccessingMethod(const uint result_code); //--- Correct errors ENUM_ERROR_CODE_PROCESSING_METHOD RequestErrorsCorrecting(MqlTradeRequest &request,const ENUM_ORDER_TYPE order_type,const uint spread_multiplier,CSymbol *symbol_obj,CTradeObj *trade_obj);
Como temos muitos métodos para abrir posições e fazer ordens, todos acabam sendo quase os mesmos. A diferença está apenas nos tipos de posições
abertas e ordens colocadas.
Para não escrever o mesmo código para cada método,
declararemos e implementaremos mais dois
métodos privados: para abrir posições e para
fazer ordens pendentes:
//--- (1) Open a position, (2) place a pending ord 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); template<typename PS,typename PL,typename SL,typename TP> bool 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); public: //--- Constructorр
Na seção pública da classe declaramos um temporizador
(necessário para trabalhar com a classe de ordens pendentes), o método que retorna a
lista de ordens pendentese o método para definir o número de tentativas de
negociação:
public: //--- Constructor CTrading(); //--- Timer void OnTimer(void); //--- Get the pointers to the lists (make sure to call the method in program's OnInit() since the symbol collection list is created there) void OnInit(CAccount *account,CSymbolsCollection *symbols,CMarketCollection *market,CHistoryCollection *history) { this.m_account=account; this.m_symbols=symbols; this.m_market=market; this.m_history=history; } //--- Return the list of (1) errors and (2) pending requests CArrayInt *GetListErrors(void) { return &this.m_list_errors; } CArrayObj *GetListRequests(void) { return &this.m_list_request;} //--- Set the number of trading attempts void SetTotalTry(const uchar number) { this.m_total_try=number; } //--- Check limitations and errors
Complementamos a especificação do método para fechamento de posições com volume de fechamento, por padrão WRONG_VALUE , para fechamento completo da posição, caso contrário, fechamento parcial segundo o volume especificado:
bool ClosePosition(const ulong ticket,const double volume=WRONG_VALUE,const string comment=NULL,const ulong deviation=ULONG_MAX);
Nas especificações dos métodos para configurar ordens pendentes adicionamos
os tipos de execução de
ordens por saldo. Anteriormente, sempre era usado o valor padrão para a classe. Agora, o valor do tipo de execução da ordem
será selecionado com base no valor passado para o método: se for WRONG_VALUE, o será
definido o valor padrão, caso contrário, o valor passado para o método:
//--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order template<typename PS,typename SL,typename TP> bool PlaceBuyStop(const double volume, const string symbol, const PS 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 PS,typename SL,typename TP> bool PlaceBuyLimit(const double volume, const string symbol, const PS 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 PS,typename PL,typename SL,typename TP> bool PlaceBuyStopLimit(const double volume, const string symbol, const PS 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); //--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order template<typename PS,typename SL,typename TP> bool PlaceSellStop(const double volume, const string symbol, const PS 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 PS,typename SL,typename TP> bool PlaceSellLimit(const double volume, const string symbol, const PS 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 PS,typename PL,typename SL,typename TP> bool PlaceSellStopLimit(const double volume, const string symbol, const PS 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); //--- Modify a pending order template<typename PS,typename PL,typename SL,typename TP> bool ModifyOrder(const ulong ticket, const PS price=WRONG_VALUE, const SL sl=WRONG_VALUE, const TP tp=WRONG_VALUE, const PL limit=WRONG_VALUE, datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
Escrevemos a implementação do temporizador, por enquanto, prepararemos apenas o processamento de lista de ordens pendentes:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { } } //+------------------------------------------------------------------+
Implementação do método que retorna como processar códigos de retorno do servidor de negociação:
//+------------------------------------------------------------------+ //| Return the error handling method | //+------------------------------------------------------------------+ ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::ResultProccessingMethod(const uint result_code) { switch(result_code) { #ifdef __MQL4__ //--- Malfunctional trade operation case 9 : //--- Account disabled case 64 : //--- Invalid account number case 65 : return ERROR_CODE_PROCESSING_METHOD_DISABLE; //--- No error but result is unknown case 1 : //--- General error case 2 : //--- Old client terminal version case 5 : //--- Not enough rights case 7 : //--- Market closed case 132 : //--- Trading disabled case 133 : //--- Order is locked and being processed case 139 : //--- Buy only case 140 : //--- The number of open and pending orders has reached the limit set by the broker case 148 : //--- Attempt to open an opposite order if hedging is disabled case 149 : //--- Attempt to close a position on a symbol contradicts the FIFO rule case 150 : return ERROR_CODE_PROCESSING_METHOD_EXIT; //--- Invalid trading request parameters case 3 : //--- Invalid price case 129 : //--- Invalid stop levels case 130 : //--- Invalid volume case 131 : //--- Not enough money to perform the operation case 134 : //--- Expirations are denied by broker case 147 : return ERROR_CODE_PROCESSING_METHOD_CORRECT; //--- Trade server is busy case 4 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- No connection to the trade server case 6 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Too frequent requests case 8 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- No price case 136 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Broker is busy case 137 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Too many requests case 141 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Modification denied because the order is too close to market case 145 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Trade context is busy case 146 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)1000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- Trade timeout case 128 : //--- Price has changed case 135 : //--- New prices case 138 : return ERROR_CODE_PROCESSING_METHOD_REFRESH; //--- MQL5 #else //--- Auto trading disabled by the server case 10026 : return ERROR_CODE_PROCESSING_METHOD_DISABLE; //--- Request canceled by a trader case 10007 : //--- Request expired case 10012 : //--- Trading disabled case 10017 : //--- Market closed case 10018 : //--- Order status changed case 10023 : //--- Request unchanged case 10025 : //--- Request blocked for handling case 10028 : //--- Transaction is allowed for live accounts only case 10032 : //--- The maximum number of pending orders is reached case 10033 : //--- Reached the maximum order and position volume for this symbol case 10034 : //--- Invalid or prohibited order type case 10035 : //--- Position with the specified ID already closed case 10036 : //--- A close order is already present for a specified position case 10039 : //--- The maximum number of open positions is reached case 10040 : //--- Request to activate a pending order is rejected, the order is canceled case 10041 : //--- Request is rejected, because the rule "Only long positions are allowed" is set for the symbol case 10042 : //--- Request is rejected, because the rule "Only short positions are allowed" is set for the symbol case 10043 : //--- Request is rejected, because the rule "Only closing of existing positions is allowed" is set for the symbol case 10044 : //--- Request is rejected, because the rule "Only closing of existing positions by FIFO rule is allowed" is set for the symbol case 10045 : return ERROR_CODE_PROCESSING_METHOD_EXIT; //--- Requote case 10004 : //--- Request rejected case 10006 : //--- Prices changed case 10020 : return ERROR_CODE_PROCESSING_METHOD_REFRESH; //--- Invalid request case 10013 : //--- Invalid request volume case 10014 : //--- Invalid request price case 10015 : //--- Invalid request stop levels case 10016 : //--- Insufficient funds for request execution case 10019 : //--- Invalid order expiration in a request case 10022 : //--- The specified type of order execution by balance is not supported case 10030 : //--- Closed volume exceeds the current position volume case 10038 : return ERROR_CODE_PROCESSING_METHOD_CORRECT; //--- No quotes to process the request case 10021 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT; //--- Too frequent requests case 10024 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000; // ERROR_CODE_PROCESSING_METHOD_WAIT //--- An order or a position is frozen case 10029 : return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000; // ERROR_CODE_PROCESSING_METHOD_WAIT; //--- Request handling error case 10011 : return ERROR_CODE_PROCESSING_METHOD_PENDING; //--- Auto trading disabled by the client terminal case 10027 : return ERROR_CODE_PROCESSING_METHOD_PENDING; //--- No connection to the trade server case 10031 : return ERROR_CODE_PROCESSING_METHOD_PENDING; //--- Order placed case 10008 : //--- Request executed case 10009 : //--- Request executed partially case 10010 : #endif //--- "OK" default: break; } return ERROR_CODE_PROCESSING_METHOD_OK; } //+------------------------------------------------------------------+
Neste caso, tudo é simples, isto é, ao método é transferido o código
recebido do servidor após o envio de ordem de negociação, em seguida, aqueles códigos que permitem corrigir erros serão processados
pelo método de correção de erros, enquanto os códigos que exigem a atualização de dados e o reenvio ordem serão processados em conformidade,
etc.
Como os servidores MQL5 e MQL4 retornam diferentes códigos de erro, no método também é realizada a compilação condicional para MQL4
e MQL5.
Todos os códigos que exigem o mesmo tipo de processamento
são agrupados num único case do operador swith,
e retornam um único método para processar o código de retorno do servidor de negociação para eles.
Implementação do método de tratamento de erros do servidor de negociação:
//+------------------------------------------------------------------+ //| Correct errors | //+------------------------------------------------------------------+ ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::RequestErrorsCorrecting(MqlTradeRequest &request, const ENUM_ORDER_TYPE order_type, const uint spread_multiplier, CSymbol *symbol_obj, CTradeObj *trade_obj) { //--- The empty error list means no errors are detected, return success int total=this.m_list_errors.Total(); if(total==0) return ERROR_CODE_PROCESSING_METHOD_OK; //--- Trading is disabled for the current account //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Trading on the trading server side is disabled for EAs on the current account //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Trading operations are disabled in the terminal //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Trading operations are disabled for the EA //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Disable trading on a symbol //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_TRADE_MODE_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_TRADE_MODE_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Close only //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_TRADE_MODE_CLOSEONLY)) { trade_obj.SetResultRetcode(MSG_SYM_TRADE_MODE_CLOSEONLY); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Market orders are disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_MARKET_ORDER_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_MARKET_ORDER_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Limit orders are disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_LIMIT_ORDER_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_LIMIT_ORDER_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Stop orders are disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_STOP_ORDER_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_STOP_ORDER_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- StopLimit orders are disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_STOP_LIMIT_ORDER_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_STOP_LIMIT_ORDER_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Sell only //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_TRADE_MODE_SHORTONLY)) { trade_obj.SetResultRetcode(MSG_SYM_TRADE_MODE_SHORTONLY); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Buy only //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_TRADE_MODE_LONGONLY)) { trade_obj.SetResultRetcode(MSG_SYM_TRADE_MODE_LONGONLY); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- CloseBy orders are disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_SYM_CLOSE_BY_ORDER_DISABLED)) { trade_obj.SetResultRetcode(MSG_SYM_CLOSE_BY_ORDER_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Exceeded maximum allowed aggregate volume of orders and positions in one direction //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Close by is disabled //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Symbols of opposite positions are not equal //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Unsupported price parameter type in a request //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Trading disabled for the EA until the reason is eliminated //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(MSG_LIB_TEXT_TRADING_DISABLE)) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- The maximum number of pending orders is reached //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(10033)) { trade_obj.SetResultRetcode(10033); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Reached the maximum order and position volume for this symbol //--- write the error code to the base trading class object and return "exit from the trading method" if(this.IsPresentErorCode(10034)) { trade_obj.SetResultRetcode(10034); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; } //--- Correcting trading request parameters //--- Price, according to which stop orders are placed double price_set=(this.IsPresentErrorFlag(TRADE_REQUEST_ERR_FLAG_PRICE_ERROR) ? request.price : request.stoplimit); //--- First, adjust stop orders relative to the order/position level if(this.IsPresentErorCode(MSG_LIB_TEXT_SL_LESS_STOP_LEVEL)) request.sl=this.CorrectStopLoss(order_type,price_set,request.sl,symbol_obj,spread_multiplier); if(this.IsPresentErorCode(MSG_LIB_TEXT_TP_LESS_STOP_LEVEL)) request.tp=this.CorrectTakeProfit(order_type,price_set,request.tp,symbol_obj,spread_multiplier); //--- Pending orders price double shift=0; if(this.IsPresentErrorFlag(TRADE_REQUEST_ERR_FLAG_PRICE_ERROR)) { price_set=request.price; request.price=this.CorrectPricePending(order_type,price_set,0,symbol_obj,spread_multiplier); shift=request.price-price_set; //--- If this is not a stop limit order, move stop orders by the calculated correcting order level shift if(request.stoplimit==0) { if(request.sl>0) request.sl=this.CorrectStopLoss(order_type,request.price,request.sl+shift,symbol_obj,spread_multiplier); if(request.tp>0) request.tp=this.CorrectTakeProfit(order_type,request.price,request.tp+shift,symbol_obj,spread_multiplier); } } //--- 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)) { //--- 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 for(int i=0;i<total;i++) { int err=this.m_list_errors.At(i); if(err==NULL) continue; switch(err) { //--- Correct an invalid volume and disabling stop levels in a trading request case MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME : case MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME : case MSG_LIB_TEXT_INVALID_VOLUME_STEP : request.volume=symbol_obj.NormalizedLot(request.volume); break; case MSG_SYM_SL_ORDER_DISABLED : request.sl=0; break; case MSG_SYM_TP_ORDER_DISABLED : request.tp=0; break; //--- If unable to select the position lot, return "abort trading attempt" since the funds are insufficient even for the minimum lot case MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR : request.volume=this.CorrectVolume(request.price,order_type,symbol_obj,DFUN); if(request.volume==0) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_EXIT; break; } //--- No quotes to process the request case 10021 : trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT - wait 5 seconds //--- No connection to the trade server case 10031 : trade_obj.SetResultRetcode(10031); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT - wait 5 seconds //--- Proximity to the order activation level is handled by five-second waiting - during this time, the price may go beyond the freeze level case MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL : case MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL : case MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT - wait 5 seconds default: break; } } //--- No errors - return ОК trade_obj.SetResultRetcode(0); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); return ERROR_CODE_PROCESSING_METHOD_OK; } //+------------------------------------------------------------------+
Na listagem do método, nos comentários do código, são descritas todas as ações para manipular erros retornados pelo servidor de negociação.
Implementação de método privado para abrir posições:
//+------------------------------------------------------------------+ //| 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) { //--- Set the trading request result as 'true' and the error flag as "no errors" bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name. If failed to get CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); //--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false' 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 failed to get - write the "internal error" flag, display the message in the journal and return 'false' 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,0,sl,tp,0,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; } //--- Write the volume to the request structure this.m_request.volume=volume; //--- Get the method of handling errors from the CheckErrors() method while checking for errors in the request parameters ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume,symbol_obj.Ask(),action,order_type,symbol_obj,trade_obj,DFUN,0,this.m_request.sl,this.m_request.tp); //--- In case of trading limitations, funds insufficiency, //--- if there are limitations by StopLevel or FreezeLevel ... if(method!=ERROR_CODE_PROCESSING_METHOD_OK) { //--- If trading is completely disabled, set the error code to the return structure, //--- display a journal message, play the error sound and exit 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_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_CREATE_PENDING_REQUEST)); //--- Instead of creating a pending request, we temporarily wait the required time period (the CheckErrors() method result is returned) ::Sleep(method); //--- after waiting, update all data 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.OpenPosition(type,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,comment,deviation); //--- 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); //--- Get the error handling method 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 - //--- in this implementation, we wait the number of milliseconds equal to the 'method' value and move on to the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_WAIT) { ::Sleep(method); continue; } //--- If "Create a pending request" is received as a result of sending a request - //--- create a pending request with the trading request parameters and end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_PENDING) { break; } } } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+Este método é comentado em detalhes diretamente na listagem e será usado para abrir posições. Buy e Sell:
//+------------------------------------------------------------------+ //| 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) { //--- 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); } //+------------------------------------------------------------------+ //| 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) { //--- 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); } //+------------------------------------------------------------------+
Estes métodos simplesmente chamam um método privado comum para abrir uma posição indicando
o tipo de posição a ser aberta.
Implementação de método privado para fazer ordens pendentes:
//+------------------------------------------------------------------+ //| 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_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_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 - //--- in this implementation, we wait the number of milliseconds equal to the 'method' value and move on to the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_WAIT) { Sleep(method); continue; } //--- If "Create a pending request" is received as a result of sending a request - //--- create a pending request with the trading request parameters and end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_PENDING) { break; } } } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+
Este método é comentado em detalhes diretamente na listagem e será usado para definir vários tipos de ordens pendentes:
//+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> bool CTrading::PlaceBuyStop(const double volume, const string symbol, const PS 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) { //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_BUY_STOP,volume,symbol,price,0,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> bool CTrading::PlaceBuyLimit(const double volume, const string symbol, const PS 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) { //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_BUY_LIMIT,volume,symbol,price,0,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> bool CTrading::PlaceBuyStopLimit(const double volume, const string symbol, const PS 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) { #ifdef __MQL5__ //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> bool CTrading::PlaceSellStop(const double volume, const string symbol, const PS 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) { //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_SELL_STOP,volume,symbol,price,0,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ template<typename PS,typename SL,typename TP> bool CTrading::PlaceSellLimit(const double volume, const string symbol, const PS 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) { //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_SELL_LIMIT,volume,symbol,price,0,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> bool CTrading::PlaceSellStopLimit(const double volume, const string symbol, const PS 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) { #ifdef __MQL5__ //--- Return the result of sending a trading request using the PlaceOrder() method return this.PlaceOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
Os outros métodos para fechar posições e excluir ordens pendentes e métodos para modificar posições e ordens são semelhantes aos métodos privados para abrir posições/definir ordens pendentes. Todos os códigos de método são comentados em detalhes, e podem ser estudados individualmente, uma vez que todos os arquivos estão anexados no final do artigo.
Nesta fase, assim concluímos a classe de negociação.
Agora precisamos fazer algumas alterações na classe do objeto base da biblioteca CEngine.
Se o valor mínimo para definir ordens de stop e ordens pendentes (StopLevel) for flutuante, precisaremos definir o multiplicador de
spread, porque geralmente com esse estado de coisas, o spread multiplicado por uma certa quantia é usado para indicar a distância permitida
para definir stops. Com base nisso, precisamos de um método que permita definir o multiplicador de spread para especificá-lo na classe de
negociação.
Na seção pública da classe declaramos este método:
//--- Set the spread multiplier for symbol trading objects in the symbol collection void SetSpreadMultiplier(const uint value=1,const string symbol=NULL) { this.m_trading.SetSpreadMultiplier(value,symbol); } //--- Open (1) Buy, (2) Sell position
O método simplesmente chama o método de classe de negociação com o mesmo nome (que examinamos anteriormente no último artigo) e permite especificar um multiplicador comum para todos os símbolos usados e multiplicadores individuais para os símbolos especificados.
Como a classe de negociação usará em breve um temporizador para trabalhar com ordens pendentes,
no construtor da
classe CEngine criamos um novo contador de temporizador para a classe de
negociação:
//+------------------------------------------------------------------+ //| CEngine constructor | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true), m_last_trade_event(TRADE_EVENT_NO_EVENT), m_last_account_event(WRONG_VALUE), m_last_symbol_event(WRONG_VALUE), m_global_error(ERR_SUCCESS) { this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_is_tester=::MQLInfoInteger(MQL_TESTER); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE); this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE); this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1); this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2); this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE); ::ResetLastError(); #ifdef __MQL5__ if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } //---__MQL4__ #else if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } #endif //--- } //+------------------------------------------------------------------+
No temporizador da classe CEngine inserimos o bloco para trabalhar com o temporizador da classe de negociação:
//+------------------------------------------------------------------+ //| CEngine timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer of the collections of historical orders and deals, as well as of market orders and positions int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If unpaused, work with the order, deal and position collections events if(counter.IsTimeDone()) this.TradeEventsControl(); } //--- If this is a tester, work with collection events by tick else this.TradeEventsControl(); } } //--- Account collection timer index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If unpaused, work with the account collection events if(counter.IsTimeDone()) this.AccountEventsControl(); } //--- If this is a tester, work with collection events by tick else this.AccountEventsControl(); } } //--- Timer 1 of the symbol collection (updating symbol quote data in the collection) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If the pause is over, update quote data of all symbols in the collection if(counter.IsTimeDone()) this.m_symbols.RefreshRates(); } //--- In case of a tester, update quote data of all collection symbols by tick else this.m_symbols.RefreshRates(); } } //--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If the pause is over if(counter.IsTimeDone()) { //--- update data and work with events of all symbols in the collection this.SymbolEventsControl(); //--- When working with the market watch list, check the market watch window events if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH) this.MarketWatchEventsControl(); } } //--- If this is a tester, work with events of all symbols in the collection by tick else this.SymbolEventsControl(); } } //--- Trading class timer index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If unpaused, work with the list of pending requests if(counter.IsTimeDone()) this.m_trading.OnTimer(); } //--- In case of the tester, work with the list of pending orders by tick else this.m_trading.OnTimer(); } } } //+------------------------------------------------------------------+
Alteramos um pouco o método para fechar completamente a posição:
//+------------------------------------------------------------------+ //| Close a position in full | //+------------------------------------------------------------------+ bool CEngine::ClosePosition(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX) { return this.m_trading.ClosePosition(ticket,WRONG_VALUE,comment,deviation); } //+------------------------------------------------------------------+
Como o método para fechar posições agora é o único para fechamento total e parcial, para fechar completamente posições, precisamos
transferir -1 como o volume da posição fechada, o que fazemos aqui.
Essas são todas as alterações e melhorias necessárias para implementar o processamento de códigos de retorno do servidor de negociação.
Teste
Para verificar o processamento dos erros retornados pelo servidor de negociação, é aconselhável definir essas condições de negociação que causarão erros, por exemplo, um atraso na execução. Durante o tempo de atraso, os preços são alterados, causando um retorno de erro "preços alterados".
Para testar, pegamos o EA do artigo anterior e o salvamos na nova \MQL5\Experts\TestDoEasy\ Part25\ usando o novo nome TestDoEasyPart25.mq5.
Em princípio, basta iniciar o EA imediatamente, sem alterações. Mas ainda faremos um pequeno ajuste.
No bloco de
parâmetros de entrada do EA alteramos a slippage padrão de zero para a cinco pontos
e adicionamos o multiplicador de spread:
//--- input variables input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 40; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput bool InpUseSounds = true; // Use sounds
Na função de inicialização da biblioteca definimos o multiplicador de spread
para todos os objetos de negociação de todos os símbolos usados, e comentamos o
bloco que define o controle para aumentar os valores dos parâmetros dos símbolos, a fim de excluir o rastreamento e,
consequentemente, as entradas extras no log do testador:
//+------------------------------------------------------------------+ //| 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 synchronous passing of orders for all used symbols engine.TradingSetAsyncMode(false); //--- 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); } } //+------------------------------------------------------------------+
No testador de estratégia, definimos um atraso de 4 segundos.
Para fazer isso, no menu suspenso selecione "Latência
personalizada..."
... e no campo de entrada que aparece, inserimos 4000 milissegundos:
Agora, no testador, as ordens de negociação enviadas ao servidor vão se atrasar quatro segundos.
Iniciamos o EA no modo visual e tentamos abrir várias posições e fechá-las num mercado veloz:
Como podemos ver, aqui nem sempre é possível na primeira tentativa abrir uma posição, pois recebemos uma requote. O EA faz o número necessário de tentativas de negociação, mas não mais do que cinco (definido por padrão), e isso pode ser visto nos registros de "Trading attempt" com o número da tentativa e uma explicação do erro "Requote". Com o fechamento simultâneo de posições, eles também recebem requote, e a última posição após cinco tentativas nunca é fechada. Além disso, já foi possível fechá-lo manualmente, mas não na primeira tentativa. Mas o EA honestamente elaborou o algoritmo incorporado na biblioteca com um número especificado de tentativas de negociação.
Nas versões mais recentes do MetaTrader 5, começando com a compilação 2201, o testador pode definir os parâmetros do símbolo no qual está sendo executado o teste. Assim, podemos configurar restrições de negociação para certo símbolo e testar o comportamento da biblioteca quando detectar as restrições definidas para um símbolo.
Para abrir a janela de configurações do símbolo, devemos clicar no botão à direita do item para selecionar o timeframe de teste:
Configuramos o símbolo para negociar apenas usando posições longas, e definimos o limite de volume para posições abertas simultaneamente e ordens pendentes posicionadas numa direção e iguais a 0,5.
Assim, podemos negociar apenas posições longas e ter no mercado um volume total máximo de não mais que 0,5 lotes de posições e ordens de compra. Isto é, ao abrir posições com o lote 0.1, podemos abrir apenas cinco posições ou definir uma ordem pendente de compra e abrir quatro posições:
Aqui, para manter o experimento nítido, certamente, era necessário desativar o fechamento automático de posições quando era excedido o valor de lucro especificado nas configurações. Mas, em princípio, já é evidente que não fomos capazes de abrir posições curtas desde o início, uma vez que recebemos um aviso de que para o símbolo apenas eram permitidas compras e, ao tentar abrir o número de posições, cujo volume total excedia 0,5 lotes, recebíamos uma mensagem indicando que não era possível abrir a posição, uma vez que era excedido o volume total máximo de posições e ordens numa direção.
Tudo isso e muito mais relacionado aos parâmetros do símbolo pode ser ensaiado na ver beta do testador do terminal, começando com a
compilação 2201.
Para obter a versão beta mais recente do terminal, basta conectar-se ao servidor MetaQuotes-Demo e selecionar
"Verificar a versão beta mais recente" no menu "Ajuda":
O que vem agora?
No próximo artigo, implementaremos ordens de negociação pendentes.
Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.
Artigos desta série:
Parte 1. Conceito, gerenciamento de dados e primeiros 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