Eventos no MetaTrader 4
Introdução
O artigo trata do monitoramento programado de eventos no terminal do cliente do MetaTrader 4, como a abertura/fechamento/modificação de pedidos, e é destinado a um usuário que possua habilidades básicas no trabalho com o terminal e em programação em MQL4.
O que são eventos, e por que eles devem ser monitorados?
Para realizar a implementação de algumas estratégias, não basta simplesmente saber se há uma posição aberta pelo Expert Advisor. Às vezes é preciso "capturar" o momento de abertura/fechamento/modificação de uma posição ou de ativação de um pedido pendente. Não há funções integradas no MQL4 que possam resolver este problema de forma independente, mas há tudo que é necessário para criar uma ferramenta do tipo. É isso o que faremos.
Princípio de definição de evento
Como é possível saber se um evento ocorreu? O que é, em linhas gerais, um evento? Após tentar responder a essas perguntas, nós chegamos à seguinte conclusão: um evento é uma mudança de estado de um pedido/uma posição aberta. Para os fins da nossa tarefa, isso consiste, por exemplo, na alteração da quantidade de posições abertas ou no nível de Stop Loss de uma posição.
Como é possível detectar se um evento está ocorrendo no momento? É muito simples. Para fazer isso, é necessário memorizar o valor a ser monitorado (neste caso, a quantidade de posições) e, então, no próximo tick, por exemplo, compará-lo ao novo valor obtido. Vamos escrever um Expert Advisor simples capaz de nos informar a respeito de mudanças na quantidade de posições.
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // If this is the first launch of the Expert, we don't know the amount of orders on the previous tick. // So just remeber it, mark that the first launch has been happened, and exit. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } // Compare the amount of positions on the previous tick with the current amount // If it has been changed, display message if ( _OrdersTotal > pre_OrdersTotal ) Alert( "The amount of positions increased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "The amount of positions decreased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); // Memorize the amount of positions pre_OrdersTotal = _OrdersTotal; return(0); }
É necessário destacar algumas características especiais deste Expert Advisor:
- As variáveis primeira e pre_OrdersTotal são declaradas estáticas. Portanto, os seus valores não são "zeroizados" ao deixar a função start(). Uma alternativa às variáveis estáticas são as variáveis globais (declaradas mais que funções), mas uma grande quantidade delas pode causar confusão de nomes (pode-se acidentalmente declarar uma variável de mesmo nome dentro da função, o que pode causar conflitos). Então nós iremos declarar todas as variáveis no corpo da função.
- O Expert irá nos informar a respeito de mudanças na quantidade de posições abertas e de pedidos pendentes (a função OrdersTotal() fornece a sua quantidade total).
- O Expert não irá nos informar a respeito do acionamento de um pedido pendente, pois, neste caso, o valor do OrdersTotal() não será alterado.
- O Expert não será capaz de detectar mudanças na quantidade de pedidos na primeira inicialização, pois ele não 'sabe' quantos eles eram no tick anterior.
- A mensagem irá aparecer apenas quando um novo tick chegar para o símbolo, no gráfico a partir do qual o Expert está trabalhando. O Expert não possui nenhum outro evento de inicialização.
O último problema pode ser resolvido colocando-se o corpo da função de início em um ciclo. Portanto, a verificação não irá ocorrer a cada tick, mas a certos intervalos de tempo:
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // If it is the first launch of the Expert Advisor, we do not know the amount of orders on the previous tick. // So we will just memorize it, check that the first launch has already happened, and exit. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); // Compare the amount of positions on the previous tick to the current amount. // If it has been changed, display he message if ( _OrdersTotal > pre_OrdersTotal ) Alert( "The amount of positions increased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "The amount of positions decreased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); // Memorize the amount of positions pre_OrdersTotal = _OrdersTotal; Sleep(100); } return(0); }
Na versão acima, a mensagem a respeito de mudanças na quantidade de posição irá
aparecer imediatamente: Você pode verificar!
Filtragem de eventos: Critérios
Assim como na sua realização atual, o nosso Expert Advisor irá nos informar a respeito de novas posições surgidas em todos os símbolos. Mas é mais comum que nós precisemos apenas de informações a respeito de mudanças em quantidades de pedidos no símbolo atual. Além disso, pedidos gerenciados por um Expert Advisor são comumente marcados com um MagicNumber. Vamos filtrar os eventos usando esses dois critérios, ou seja, nós iremos informar a respeito de mudanças nas quantidades de pedidos apenas em relação ao símbolo atual, e apenas em relação ao MagicNumber fornecido.
extern int MagicNumber = 0; int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = 0, now_OrdersTotal = 0, _GetLastError = 0; while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); now_OrdersTotal = 0; for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count the amount of orders by the current symbol and with the specified MagicNumber if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) now_OrdersTotal ++; } // Display data only if this is not the first launch of the Expert if ( !first ) { // Compare the amount of positions on the prevous tick with the current amount // If it has changed, display the message if ( now_OrdersTotal > pre_OrdersTotal ) Alert( Symbol(), ": amount of positions having MagicNumber ", MagicNumber, " increased! there were - ", pre_OrdersTotal, ", there are now - ", now_OrdersTotal ); if ( now_OrdersTotal < pre_OrdersTotal ) Alert( Symbol(), ": amount of positions having MagicNumber ", MagicNumber, " decreased! There were - ", pre_OrdersTotal, ", there are now - ", now_OrdersTotal ); } else { first = false; } //---- Remember the amount of positions pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); }
Refinamento
Encontrar a quantidade total de pedidos é, claro, muito bom. Mas às vezes algumas informações mais detalhadas são necessárias. Por exemplo, "uma posição de compra ou de venda foi aberta?", "um pedido pendente foi acionado?", "a posição foi fechada por Stop Loss, por Take Profit, ou manualmente?". Vamos tentar criar a lista de eventos a serem monitorados, de forma tão completa quanto possível, e dividi-la em grupos.
- Abertura de uma posição
- "Posição de mercado"
- Compra
- Venda
- Pedido pendente
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- "Posição de mercado"
- Acionamento de pedido
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- Fechamento de uma posição
- "Posição de mercado"
- Compra
- Stop Loss
- Take Profit
- Manualmente (nem por Stop Loss e nem por Take Profit)
- Venda
- Stop Loss
- Take Profit
- Manualmente
- Compra
- Pedido pendente (deleção)
- Buy Limit
- Expiração
- Manualmente
- Sell Limit
- Tempo de expiração
- Manualmente
- Buy Stop
- Expiração
- Manualmente
- Sell Stop
- Expiração
- Manualmente
- Buy Limit
- "Posição de mercado"
- Modificação de uma posição
- "Posição de mercado"
- Compra
- Stop Loss
- Take Profit
- Venda
- Stop Loss
- Take Profit
- Compra
- Pedido pendente
- Buy Limit
- Preço aberto
- Stop Loss
- Take Profit
- Expiração
- Sell Limit
- Preço aberto
- Stop Loss
- Take Profit
- Expiração
- Buy Stop
- Preço aberto
- Stop Loss
- Take Profit
- Expiração
- Sell Stop
- Preço aberto
- Stop Loss
- Take Profit
- Expiração
- Buy Limit
- "Posição de mercado"
Antes de realizarmos o algoritmo, vamos verificar se todos os eventos supracitados são realmente necessários. Se nós vamos criar um Expert Advisor para nos informar ou reportar a respeito de todas as mudanças em todas as posições, a resposta é sim, todos esses eventos devem ser levados em consideração. Mas o nosso objetivo é mais humilde: Nós queremos auxiliar o Expert Advisor realizando transações a 'entender' o que acontece com as posições com as quais ele está trabalhando. Nesse caso, a lista pode ser significativamente mais curta: a abertura de posições, a feitura de pedidos pendentes, todos os itens de modificação e as posições de fechamento manual podem ser removidas da lista. Esses eventos são gerados pelo próprio Expert (elas não podem ocorrer sem o Expert). Portanto, é isso que nós teremos:
- Acionamento de pedido
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- Fechamento de posição
- "Posição de mercado"
- Compra
- Stop Loss
- Take Profit
- Venda
- Stop Loss
- Take Profit
- Compra
- Pedido pendente (expiração)
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- "Posição de mercado"
Deste modo, a lista fica bem menos intimidadora, e nós podemos começar a escrever o código. Devemos fazer apenas uma pequena observação: há vários modos de definir o método de fechamento de posições (SL, TP):
- Caso a quantidade total de posições for reduzida, busque no histórico a posição mais recentemente fechada e determine, a partir dos seus parâmetros, a forma com que ela foi fechada, ou
- memorize os bilhetes de todas as posições abertas e então busque pela posição 'desaparecida' através do seu bilhete no histórico.
O primeiro modo possui uma implementação mais simples, mas pode resultar em dados errados. Caso duas posições forem fechadas durante o mesmo tick, um manualmente e um por Stop Loss, o Expert irá gerar 2 eventos idênticos ao encontrar a posição com o tempo de fechamento mais recente (caso a última posição tiver sido fechada manualmente, ambos eventos serão considerados como fechados manualmente). O EA não irá 'saber' que uma das posições foi fechada por Stop Loss.
Portanto, de modo a evitar esse tipo de problema, vamos escrever um código tão literal quanto o possível.
extern int MagicNumber = 0; // open positions array as on the previous tick int pre_OrdersArray[][2]; // [amount of positions][ticket #, position type] int start() { // first launch flag static bool first = true; // last error code int _GetLastError = 0; // total amount of positions int _OrdersTotal = 0; // the amount of positions that meet the criteria (the current symbol and the MagicNumber), // as on the current tick int now_OrdersTotal = 0; // the amount of positions that meet the criteria (the current symbol and the specified MagicNumber), // as on the previous tick static int pre_OrdersTotal = 0; // open positions array as on the current tick int now_OrdersArray[][2]; // [# in the list][ticket #, position type] // the current number of the position in the array now_OrdersArray (for search) int now_CurOrder = 0; // the current number of the position in the array pre_OrdersArray (for search) int pre_CurOrder = 0; // array for storing the amount of closed positions of each type int now_ClosedOrdersArray[6][3]; // [order type][closing type] // array for storing the amount of triggered pending orders int now_OpenedPendingOrders[4]; // [order type] (there are only 4 types of pending orders totally) // temporary flags bool OrderClosed = true, PendingOrderOpened = false; // temporary variables int ticket = 0, type = -1, close_type = -1; //+------------------------------------------------------------------+ //| Infinite loop //+------------------------------------------------------------------+ while ( !IsStopped() ) { // memorize the total amount of positions _OrdersTotal = OrdersTotal(); // change the open positions array size for the current amount ArrayResize( now_OrdersArray, _OrdersTotal ); // zeroize the array ArrayInitialize( now_OrdersArray, 0.0 ); // zeroize the amount of positions met the criteria now_OrdersTotal = 0; // zeroize the arrays of closed positions and triggered orders ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------+ //| Search in all positions and write only those in the array that //| meet the criteria //+------------------------------------------------------------------+ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count orders for the current symbol and with the specified MagicNumber if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // change the open positions array size for the amount of positions met the criteria ArrayResize( now_OrdersArray, now_OrdersTotal ); //+------------------------------------------------------------------+ //| Search in the list of positions on the previous tick and count //| how many positions have been closed and pending orders triggered //+------------------------------------------------------------------+ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // memorize the ticket and the order type ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // suppose that, if it is a position, it has been closed OrderClosed = true; // suppose that, if it is a pending order, it has not triggered PendingOrderOpened = false; // search in all positions from the current list of open positions for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // if a position with this ticket is in the list, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // the position has not been closed (the order has not been cancelled) OrderClosed = false; // if its type has changed, if ( type != now_OrdersArray[now_CurOrder][1] ) { // it is a pending order that has triggered PendingOrderOpened = true; } break; } } // if a position has not been closed (the order has not been cancelled), if ( OrderClosed ) { // select it if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // and check HOW the position has been closed (the order has been cancelled): if ( type < 2 ) { // Buy and Sell: 0 - manually, 1 - by SL, 2 - by TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending orders: 0 - manually, 1 - expiration close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // and write in the closed orders array that the order of type 'type' // was cancelled as close_type now_ClosedOrdersArray[type][close_type] ++; continue; } // if a pending order has triggered, if ( PendingOrderOpened ) { // write in the triggered orders array that the order of type 'type' has triggered now_OpenedPendingOrders[type-2] ++; continue; } } //+------------------------------------------------------------------+ //| Collected all necessary information - display it //+------------------------------------------------------------------+ // if it is not the first launch of the Expert Advisor if ( !first ) { // search in all elements of the triggered pending orders array for ( type = 2; type < 6; type ++ ) { // if the element is not empty (an order of the type has triggered), display information if ( now_OpenedPendingOrders[type-2] > 0 ) Alert( Symbol(), ": triggered ", _OrderType_str( type ), " order!" ); } // search in all elements of the closed positions array for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // if the element is not empty (the position has been closed), display information if ( now_ClosedOrdersArray[type][close_type] > 0 ) CloseAlert( type, close_type ); } } } else { first = false; } //---- save the current positions array in the previous positions array ArrayResize( pre_OrdersArray, now_OrdersTotal ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); } void CloseAlert( int alert_type, int alert_close_type ) { string action = ""; if ( alert_type < 2 ) { switch ( alert_close_type ) { case 1: action = " by StopLoss!"; break; case 2: action = " by TakeProfit!"; break; default: action = " manually!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-position closed", action ); } else { switch ( alert_close_type ) { case 1: action = " by expiration!"; break; default: action = " manually!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-order cancelled", action ); } } // returns OrderType as a text string _OrderType_str( int _OrderType ) { switch ( _OrderType ) { case OP_BUY: return("Buy"); case OP_SELL: return("Sell"); case OP_BUYLIMIT: return("BuyLimit"); case OP_BUYSTOP: return("BuyStop"); case OP_SELLLIMIT: return("SellLimit"); case OP_SELLSTOP: return("SellStop"); default: return("UnknownOrderType"); } }
Integração em Expert Advisors e utilização
Para usar convenientemente a 'retenção de eventos' a partir de qualquer Expert Advisor, vamos localizar o código no arquivo Events.mq4 apenas para incluí-lo nos EAs com o diretório #include. Para fazer isso:
- forme o código como uma função a ser chamada a partir dos Expert Advisors futuramente;
- remova a variável global MagicNumber e adicione o parâmetro da função "magic" (eles irão desempenhar a mesma função, nós fazemos isso apenas para não bloquear a lista das variáveis externas do Expert);
- adicione uma variável global por evento - isso irá tornar o seu uso mais confortável (nós também devemos inserir a "zeroização" dessas variáveis no início da função);
- remova o ciclo infinito - agora a 'amostragem' será feita entre as chamadas de função (ou seja, ao chamar uma função, nós receberemos apenas uma lista de alterações em comparação com a chamada anterior da função);
- remova os alertas, eles podem ser adicionados ao Expert, se necessário;
- refine o código de acordo com as diretrizes acima.
Isto é o que devemos obter como resultado:
// array of open positions as it was on the previous tick int pre_OrdersArray[][2]; // [amount of positions][ticket #, positions type] // variables of events int eventBuyClosed_SL = 0, eventBuyClosed_TP = 0; int eventSellClosed_SL = 0, eventSellClosed_TP = 0; int eventBuyLimitDeleted_Exp = 0, eventBuyStopDeleted_Exp = 0; int eventSellLimitDeleted_Exp = 0, eventSellStopDeleted_Exp = 0; int eventBuyLimitOpened = 0, eventBuyStopOpened = 0; int eventSellLimitOpened = 0, eventSellStopOpened = 0; void CheckEvents( int magic = 0 ) { // flag of the first launch static bool first = true; // the last error code int _GetLastError = 0; // total amount of positions int _OrdersTotal = OrdersTotal(); // the amount of positions met the criteria (the current symbol and the specified MagicNumber), // as it is on the current tick int now_OrdersTotal = 0; // the amount of positions met the criteria as on the previous tick static int pre_OrdersTotal = 0; // array of open positions as of the current tick int now_OrdersArray[][2]; // [# in the list][ticket #, position type] // the current number of the position in array now_OrdersArray (for searching) int now_CurOrder = 0; // the current number of the position in array pre_OrdersArray (for searching) int pre_CurOrder = 0; // array for storing the amount of closed positions of each type int now_ClosedOrdersArray[6][3]; // [order type][closing type] // array for storing the amount of triggered pending orders int now_OpenedPendingOrders[4]; // [order type] // temporary flags bool OrderClosed = true, PendingOrderOpened = false; // temporary variables int ticket = 0, type = -1, close_type = -1; //zeroize the variables of events eventBuyClosed_SL = 0; eventBuyClosed_TP = 0; eventSellClosed_SL = 0; eventSellClosed_TP = 0; eventBuyLimitDeleted_Exp = 0; eventBuyStopDeleted_Exp = 0; eventSellLimitDeleted_Exp = 0; eventSellStopDeleted_Exp = 0; eventBuyLimitOpened = 0; eventBuyStopOpened = 0; eventSellLimitOpened = 0; eventSellStopOpened = 0; // change the open positions array size for the current amount ArrayResize( now_OrdersArray, MathMax( _OrdersTotal, 1 ) ); // zeroize the array ArrayInitialize( now_OrdersArray, 0.0 ); // zeroize arrays of closed positions and triggered orders ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------+ //| Search in all positions and write in the array only those //| meeting the criteria //+------------------------------------------------------------------+ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count the amount of orders on the current symbol with the specified MagicNumber if ( OrderMagicNumber() == magic && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // change the open positions array size for the amount of positions meeting the criteria ArrayResize( now_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); //+-------------------------------------------------------------------------------------------------+ //| Search in the list of the previous tick positions and count how many positions have been closed //| and pending orders triggered //+-------------------------------------------------------------------------------------------------+ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // memorize the ticket number and the order type ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // assume that, if it is a position, it has been closed OrderClosed = true; // assume that, if it is a pending order, it has not triggered PendingOrderOpened = false; // search in all positions from the current list of open positions for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // if there is a position with such a ticket number in the list, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // it means that the position has not been closed (the order has not been cancelled) OrderClosed = false; // if its type has changed, if ( type != now_OrdersArray[now_CurOrder][1] ) { // it means that it was a pending order and it triggered PendingOrderOpened = true; } break; } } // if a position has been closed (an order has been cancelled), if ( OrderClosed ) { // select it if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // and check HOW the position has been closed (the order has been cancelled): if ( type < 2 ) { // Buy and Sell: 0 - manually, 1 - by SL, 2 - by TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending orders: 0 - manually, 1 - expiration close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // and write in the closed orders array that the order of the type 'type' // was closed by close_type now_ClosedOrdersArray[type][close_type] ++; continue; } // if a pending order has triggered, if ( PendingOrderOpened ) { // write in the triggered orders array that the order of type 'type' triggered now_OpenedPendingOrders[type-2] ++; continue; } } //+--------------------------------------------------------------------------------------------------+ //| All necessary information has been collected - assign necessary values to the variables of events //+--------------------------------------------------------------------------------------------------+ // if it is not the first launch of the Expert Advisor if ( !first ) { // search in all elements of the triggered pending orders array for ( type = 2; type < 6; type ++ ) { // if the element is not empty (an order of the type has not triggered), change the variable value if ( now_OpenedPendingOrders[type-2] > 0 ) SetOpenEvent( type ); } // search in all elements of the closed positions array for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // if the element is not empty (a position has been closed), change the variable value if ( now_ClosedOrdersArray[type][close_type] > 0 ) SetCloseEvent( type, close_type ); } } } else { first = false; } //---- save the current positions array in the previous positions array ArrayResize( pre_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; } void SetOpenEvent( int SetOpenEvent_type ) { switch ( SetOpenEvent_type ) { case OP_BUYLIMIT: eventBuyLimitOpened ++; return(0); case OP_BUYSTOP: eventBuyStopOpened ++; return(0); case OP_SELLLIMIT: eventSellLimitOpened ++; return(0); case OP_SELLSTOP: eventSellStopOpened ++; return(0); } } void SetCloseEvent( int SetCloseEvent_type, int SetCloseEvent_close_type ) { switch ( SetCloseEvent_type ) { case OP_BUY: { if ( SetCloseEvent_close_type == 1 ) eventBuyClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventBuyClosed_TP ++; return(0); } case OP_SELL: { if ( SetCloseEvent_close_type == 1 ) eventSellClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventSellClosed_TP ++; return(0); } case OP_BUYLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventBuyLimitDeleted_Exp ++; return(0); } case OP_BUYSTOP: { if ( SetCloseEvent_close_type == 1 ) eventBuyStopDeleted_Exp ++; return(0); } case OP_SELLLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventSellLimitDeleted_Exp ++; return(0); } case OP_SELLSTOP: { if ( SetCloseEvent_close_type == 1 ) eventSellStopDeleted_Exp ++; return(0); } } }
Agora os eventos podem ser monitorados a partir de qualquer Expert Advisor: basta habilitar uma biblioteca. Um exemplo de um Expert Advisor do tipo (EventsExpert.mq4) é apresentado abaixo:
extern int MagicNumber = 0; #include <Events.mq4> int start() { CheckEvents( MagicNumber ); if ( eventBuyClosed_SL > 0 ) Alert( Symbol(), ": Buy position was closed by StopLoss!" ); if ( eventBuyClosed_TP > 0 ) Alert( Symbol(), ": Buy position was closed by TakeProfit!" ); if ( eventBuyLimitOpened > 0 || eventBuyStopOpened > 0 || eventSellLimitOpened > 0 || eventSellStopOpened > 0 ) Alert( Symbol(), ": pending order triggered!" ); return(0); }
6. Conclusão
Neste artigo nós consideramos as formas de monitoramento programado de eventos no MetaTrader 4 através do MQL4. Nós dividimos todos os eventos em 3 grupos e os filtramos de acordo com os critérios pré-definidos. Nós também criamos uma biblioteca que permite o monitoramento fácil de alguns eventos a partir de qualquer Expert Advisor.A função CheckEvents() pode ser completada (ou usada como um modelo) para monitorar outros eventos que não tenham sido considerados no artigo.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1399
- 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