
Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXV): processamento de erros retornados pelo servidor de negociação
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
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7365





- 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
Sobre as informações registradas no valor do Número Mágico:
você pode usar uma mágica diferente para cada grupo para criar grupos diferentes. Por exemplo, se o número mágico do consultor for 123, o número mágico do primeiro grupo será 124, o número mágico do segundo grupo será 125, o número mágico do terceiro grupo será 126 e assim por diante.
A biblioteca oferece uma maneira diferente de criar grupos diferentes - o número de cada subgrupo é armazenado diretamente no valor do número mágico. Em seguida, o número mágico do EA também é um identificador de grupo, mas é colocado em um grupo independente chamado MagicID - o identificador do número mágico do EA. E há mais dois grupos. Cada um deles tem 15 subgrupos. E cada um dos subgrupos pode ter seu próprio identificador.
Isso proporcionarámais flexibilidade ao trabalhar com grupos.
Por exemplo: queremos mover uma grade de ordens pendentes para trás do preço - adicione-as ao grupo 1 no subgrupo 1. O grupo 1 se move atrás do preço. O subgrupo 1 se move ao longo da MA. Agora, queremos mover algumas dessas ordens que se movem atrás do preço (grupo 1) pelo SAR Parabólico. Damos a elas o subgrupo 2. Então, o grupo 1 se move atrás do preço, mas o subgrupo 1 se move pela MA e o subgrupo 2 se move pela SAR Parabólica.
As ordens são acionadas, transformando-se em posições - você pode definir seus próprios grupos para modificação de stoplosses e seus próprios subgrupos nesse grupo para modificação por valores diferentes. Os algoritmos de modificação são escritos em subgrupos.
Em geral, um voo de fantasia. Também é possível usar uma mágica simples, mas você mesmo terá de inventar a lógica de rastreamento de diferentes grupos.
Sobre a segunda pergunta:
Há uma classe CSelect. Ela está disponível no programa e fornece métodos de seleção e pesquisa sobre os quais você está escrevendo em todas as coleções existentes: Account, Event, Order, Symbol.
Você pode selecionar os objetos de cada coleção em uma lista com base em todos os critérios. Na lista criada, você pode selecionar novamente, refinando os critérios, e pode encontrar os valores máximo e mínimo para o critério de seleção.
No entanto, mais adiante haverá funções personalizadas (muito mais tarde) para acesso rápido e conveniente a todas as propriedades de todas as coleções e pesquisa nelas.
Mas, por enquanto, somente por meio do CSelect, e quando você precisar. A classe é estática, portanto, o acesso a seus métodos é feito por meio de ":::" Por exemplo, CSelect::ByOrderProperty().
Sim, a propósito, há um exemplo de seu uso no programa bem no EA de teste - por exemplo, em suas funções finais:
Descobri o CSelect, mas acontece que tenho de fazer uma escolha em cada lugar em que precisamos de valores agregados: digamos que eu precise calcular o lucro total de uma grade de posições para fazer o trailing, essa é uma função. Em outra, digamos que eu esteja ajustando o lucro total de uma grade após abrir ou fechar uma das ordens - e para isso preciso do lucro total e do volume agregado.
E em outro lugar, preciso tomar a decisão de abrir uma nova grade ou continuar com a atual - preciso saber quantas ordens já estão abertas nesse grupo.
Todos esses valores são calculados por meio de uma execução na lista, que pode ser selecionada por uma série de CSelects. Mas como preciso deles em lugares diferentes, teria que iniciar uma agregação a cada vez, recalcular no início de um tick e usá-los em todos os lugares.
Achei que seria bom adicionar essa construção à biblioteca, com a capacidade de alocá-la por um conjunto de critérios de seleção, por exemplo, símbolo, magia (com grupos), tipo de ordem.
E que ele colete estatísticas simples: número de ordens, volume total, lucro total... uma coleção de ordens, a propósito, já que já a selecionamos.
Caso contrário, será necessário criar uma estrutura auxiliar a cada vez, ou CSelects e iteração em cada local.
Descobri o CSelect, mas acontece que tenho de fazer uma escolha em cada lugar em que precisamos de valores agregados: digamos que eu precise calcular o lucro total de uma grade de posições para poder arrastá-la, essa é uma função. E em outra, digamos, estou ajustando o total de uma grade após abrir ou fechar uma das ordens - e para isso preciso do lucro total e do volume agregado.
Em outro lugar, preciso tomar a decisão de abrir uma nova grade ou continuar com a atual - preciso saber quantas ordens já estão abertas nesse grupo.
Todos esses valores são calculados por meio de uma execução na lista, que pode ser selecionada por uma série de CSelects. Porém, como eles são necessários em locais diferentes, tenho que iniciar uma construção agregada a cada vez, recalculá-la no início de um tick e usá-la em todos os lugares.
Achei que seria bom adicionar essa construção à biblioteca, com a possibilidade de alocá-la por um conjunto de critérios de seleção, por exemplo, símbolo, magia (com grupos), tipo de ordem.
E permitir que ele colete estatísticas simples: número de ordens, volume total, lucro total... uma coleção de ordens, a propósito, já que já fizemos uma amostragem.
Caso contrário, será necessário criar uma estrutura auxiliar a cada vez, ou CSelects e iteração em cada local.
E você propõe tornar os cálculos na própria biblioteca mais pesados? Já há muitas coisas a serem calculadas lá.
Por que não fazer o que você sugere diretamente no EA em vez de na biblioteca? Não há diferença onde calcular tudo isso. Mas alguém precisa disso (você) e alguém não precisa. E por que ele precisa de cálculos desnecessários?
Para não contar sempre a mesma coisa em funções diferentes, você deve criar listas globais no Expert Advisor, onde poderá colocar as informações de que precisa em qualquer lugar. E em funções separadas, e somente quando necessário, pegue essas listas públicas e, a partir delas, obtenha os dados necessários somente dentro da função e retorne-os.
A biblioteca fornece um conjunto de dados, oferece a possibilidade de selecionar esses dados em qualquer combinação e fornece ferramentas para resolver tarefas que não são suficientemente rápidas para serem feitas do zero.
E você está sugerindo que os cálculos na própria biblioteca sejam ponderados? Já há muita contagem acontecendo lá.
Por que não fazer o que você sugere diretamente no EA em vez de na biblioteca? Não há diferença onde calcular tudo isso. Mas alguém precisa disso (você) e alguém não precisa. E por que ele precisa de cálculos adicionais?
Para não contar sempre a mesma coisa em funções diferentes, você deve criar listas globais no Expert Advisor, onde poderá colocar as informações de que precisa em qualquer lugar. E em funções separadas, e somente quando necessário, pegue essas listas públicas e, a partir delas, obtenha os dados necessários somente dentro da função e retorne-os.
A biblioteca fornece um conjunto de dados, oferece a possibilidade de selecionar esses dados em qualquer combinação e fornece ferramentas para resolver tarefas que não são suficientemente rápidas para serem feitas do zero.
Bem, proponho torná-la opcional.
Iniciar uma "assinatura".
Mas se essa ideia não lhe parecer digna de consideração, eu mesmo escreverei o wrapper.
Mais uma vez, obrigado pelo que já foi feito e por tudo o que ainda está nos planos (aqui e ali eu encontro reservas sobre os planos para continuar o desenvolvimento da biblioteca).
Bem, eu sugiro contar opcionalmente.
Faça uma "assinatura".
Mas se você não achar essa ideia digna de consideração, eu mesmo escreverei o wrapper.
Mais uma vez, obrigado pelo que já foi feito - e adiante para o que mais estiver em andamento (vi reservas aqui e ali sobre os planos de continuar desenvolvendo a biblioteca)