Discussão do artigo "Aprendendo a construindo um EA que opera de forma automática (Parte 08): OnTradeTransaction"

 

Novo artigo Aprendendo a construindo um EA que opera de forma automática (Parte 08): OnTradeTransaction foi publicado:

Neste artigo, mostrei como você pode usar o sistema de tratamento de eventos, a fim de conseguir lidar com mais agilidade, e de uma forma melhor com questões envolvendo o sistema de ordens, a fim de deixar o EA mais rápido. Assim ele não precisará, ficar procurando informações a todo o momento.

Mas agora, de uma forma bem mais completa, temos finalmente a classe C_Manager e o EA, sendo amistosos um com o outro. A ponto que ambos consigam trabalhar, e garantir que não sejam agressivos, ou pouco cordiais. Desta forma, o fluxo de mensagens entre o EA e a classe C_Manager, passa a ser da seguinte maneira, vista na figura 02:

Figura 02

Figura 02: Fluxo de mensagens com as novas rotinas implementadas.


Você pode achar que este fluxo de mensagens, é muito complicado, ou não é nada funcional. Mas no entanto, ele é exatamente o que esta sendo implementado.

Mas então com base na figura 02, você deve estar pensando que o código do EA deve ser muito complicado. Mas no entanto, ele é bem mais simples do que muitos consideram ser um código necessário, para de fato ser usado em um EA. Ainda mais um que estará sendo automatizado. Lembre-se do seguinte: O EA, não gera de fato nenhuma operação. Ele apenas é um meio, ou ferramenta para se comunicar com o servidor de negociação. Então ele na verdade, apenas reage aos gatilhos que são aplicados a ele.

Autor: Daniel Jose

 

Tenho um problema que me confunde.

Obrigado por dedicar um artigo tão maravilhoso. Tenho um problema que me confunde. Escrevi um código para abrir uma posição.

Ele pode retornar o valor correto quando executado em uma conta demo, mas retorna 0 quando executado em uma conta real.

Meu objetivo é retornar o ticket da transação depois de abrir uma posição.
//+------------------------------------------------------------------+
//|& nbsp; &nbsp Test1.mq5
//|& nbsp; Copyright 2023, MetaQuotes Ltd.
//|& nbsp; &nbsp ; https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\Trade.mqh>
CTrade    m_trade;
CDealInfo m_deal;
//+------------------------------------------------------------------+
//| Funções de inicialização de comércio da EA & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   OpenPosition(ORDER_TYPE_BUY, 0.01, 123456);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| EA Quotation Function & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   m_trade.SetExpertMagicNumber(magic);
   if(m_trade.PositionOpen(_Symbol, type, lot, (type == ORDER_TYPE_BUY) ? ask : bid, 0, 0))
     {
      ulong deal_ticket = m_trade.ResultDeal();
      if(HistoryDealSelect(deal_ticket))
         m_deal.Ticket(deal_ticket);
      string flag = (m_deal.DealType() == DEAL_TYPE_BUY) ? "buy" : "sell";
      printf(StringFormat("open deal ticket= #%%d order ticket= #%%d %%s %%.2f %%s at %%.%df", _Digits),
             deal_ticket, m_deal.Order(), flag, m_deal.Volume(), _Symbol, m_deal.Price());
      return(deal_ticket);
     }
   return(0);
  }


 
Yang Wang #:

Olá, Daniel José

Obrigado por dedicar um artigo tão maravilhoso. Estou com um problema que me confunde. Eu escrevi um código para abrir uma posição.

Ele pode retornar o valor correto quando é executado em conta demo, mas retorna 0 quando é executado em uma conta real, você pode me ajudar a informar onde está o problema? á o problema?

Meu objetivo é devolver seu ticket de negociação após abrir uma posição.


Tente verificar o erro que está sendo retornado, adicionando a linha destacada logo abaixo.

ulong OpenPosition( const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble ( _Symbol , SYMBOL_ASK );
   double bid = SymbolInfoDouble ( _Symbol , SYMBOL_BID );
   m_trade.SetExpertMagicNumber(magic);
   if (m_trade.PositionOpen( _Symbol , type, lot, (type == ORDER_TYPE_BUY ) ? ask : bid, 0 , 0 ))
     {
       ulong deal_ticket = m_trade.ResultDeal();
       if ( HistoryDealSelect (deal_ticket))
         m_deal.Ticket(deal_ticket);
       string flag = (m_deal.DealType() == DEAL_TYPE_BUY ) ? "buy" : "sell" ;
       printf ( StringFormat ( "open deal ticket= #%%d order ticket= #%%d %%s %%.2f %%s at %%.%df" , _Digits ),
             deal_ticket, m_deal.Order(), flag, m_deal.Volume(), _Symbol , m_deal.Price());
       return (deal_ticket);
     }
   Print("Error: ", m_trade.ResultRetcode());
   return ( 0 );
  }
 
Daniel Jose #:

Tente verificar o erro que está sendo retornado, adicionando a linha destacada logo abaixo.

Não, meu objetivo é retornar um ticket de transação

Ele pode retornar o ticket de transação da posição na conta Demo, mas retorna valor zero na conta Real.

ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   m_trade.SetExpertMagicNumber(magic);
   if(m_trade.PositionOpen(_Symbol, type, lot, (type == ORDER_TYPE_BUY) ? ask : bid, 0, 0))
      return(m_trade.ResultDeal());
   return(0);
  }
 
Yang Wang #:

Não, meu objetivo é devolver um bilhete de acordo

ele pode retornar ticket de negociação da posição na conta Demo, mas retornar valor zero na conta Real, isso é para quê?

A linha que mandei você adicionar NÃO É... vou repetir. NÃO É para retornar nenhum valor ... ELA SERVE PARA VERIFICAR O ERRO que está dando ao tentar abrir uma posição ... você nÃO deve usar o valor retornado... você deve verificar o ERRO na tabela de erros... Veja a DOCUMENTAÇÃO

 

Olá, estou acompanhando esta série de artigos. Meus respeitos. É realmente útil.

Estou testando o resultado e, depois de criar uma ordem, eu a excluo. Mas a EA não permite mais criar nenhuma nova ordem.

Este trecho de código:

 case TRADE_TRANSACTION_ORDER_DELETE :
                         if (trans.order == trans.position) (*manager).PendingToPosition();
                         else (*manager).UpdatePosition(trans.position); // ORDER?
                         break ;

Ele usa trans.position que, no caso de exclusão da ordem, é sempre 0. Portanto, esse é o valor passado para UpdatePosition. Sua primeira condição o envia diretamente para a instrução return.

Tentei passar o ticket trans.order, mas o código o ignora porque m_position tem dados zero, portanto o ticket é 0 (! = m_Position.Ticket):

Depois dessa. Quando tento criar um novo pedido, m_ticketPending tem o valor do primeiro pedido, portanto, ele não é criado.

EraseTicketPending não é chamado porque quando TRADE_ACTION_REMOVE chega, essa condição é sempre falsa, porque request .request.TRADE_ACTION_REMOVE é sempre falsa.

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && (request.magic == def_MAGIC_NUMBER))

é sempre falsa, pois request.magic é 0.

Investigarei se isso foi corrigido nos próximos capítulos.

Atenciosamente

 

Acho que encontrei o problema.

Quando a exclusão da ordem é feita na interface do usuário do MT5, o número mágico é 0. Então, modifiquei a condição para

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && ((request.magic == def_MAGIC_NUMBER) || request.magic == 0 ))

e ela parece funcionar como esperado.

Arquivos anexados:
 
Yang Wang #:

Oi, Daniel José.

Obrigado por dedicar um artigo tão maravilhoso. Tenho um problema que me confunde. Escrevi um código para abrir uma posição.

Ele pode retornar o valor correto quando executado em uma conta demo, mas retorna 0 quando executado em uma conta real.

Meu objetivo é retornar o ticket da transação depois de abrir uma posição.


Essa função abre a posição e retorna o DEAL TICKET:

//+------------------------------------------------------------------+
//|& nbsp; &nbsp Test1.mq5
//|& nbsp; Copyright 2023, MetaQuotes Ltd.
//|& nbsp; &nbsp ; https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
CTrade      m_trade;
CDealInfo   m_deal;
CSymbolInfo m_symbol;
input double TakeProfit = 0;
input double StopLoss   = 0;
input string PositionComment = "";
double m_point;
//+------------------------------------------------------------------+
//| Funções de inicialização de comércio da EA & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   if(!m_symbol.Name(_Symbol)) // define o nome do símbolo
      return(INIT_FAILED);
   RefreshRates();
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(m_symbol.Name());
   m_trade.SetDeviationInPoints(INT_MAX);
   int adjust = (m_symbol.Digits() == 3 || m_symbol.Digits() == 5) ? 10 : 1;
   m_point = m_symbol.Point() * adjust;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| EA Quotation Function & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
bool RefreshRates(void)
  {
//--- taxas de atualização
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- proteção contra o valor de retorno "zero"
   if(m_symbol.Ask() == 0 || m_symbol.Bid() == 0)
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; &nbsp; &nbsp
//+------------------------------------------------------------------+
ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double volume,
                   const long magic)
  {
   ulong deal_ticket = 0;
   double sl = 0;
   double tp = 0;
   m_trade.SetExpertMagicNumber(magic);
   RefreshRates();
   double ask = m_symbol.Ask();
   double bid = m_symbol.Bid();
   switch(type)
     {
      case ORDER_TYPE_BUY:
         if(TakeProfit > 0)
            tp = m_symbol.NormalizePrice(ask + TakeProfit * m_point);
         if(StopLoss > 0)
            sl = m_symbol.NormalizePrice(bid - StopLoss * m_point);
         break;
      case ORDER_TYPE_SELL:
         if(TakeProfit > 0)
            tp = m_symbol.NormalizePrice(bid - TakeProfit * m_point);
         if(StopLoss > 0)
            sl = m_symbol.NormalizePrice(ask + StopLoss * m_point);
         break;
     }
   static datetime from_date = iTime(m_symbol.Name(), PERIOD_D1, 0);
   ulong time = GetTickCount64();
   if(m_trade.PositionOpen(m_symbol.Name(), type, volume, (type == ORDER_TYPE_BUY) ? ask : bid, sl, tp, PositionComment))
     {
      ulong open_execution = GetTickCount64() - time;
      do
        {
         ulong order_ticket = m_trade.ResultOrder();
         HistorySelect(from_date, TimeTradeServer());
         int total = HistoryDealsTotal();
         for(int i = total - 1; i >= 0; i--)
           {
            if(!m_deal.SelectByIndex(i))
               continue;
            if(m_deal.Symbol() != m_symbol.Name())
               continue;
            if(m_deal.Entry() != DEAL_ENTRY_IN)
               continue;
            if(m_deal.Order() != order_ticket)
               continue;
            printf(StringFormat("open  #%%I64u %%s %%.2f %%s at %%.%df done in %%I64u ms", m_symbol.Digits()),
                   m_deal.Order(), (type == ORDER_TYPE_BUY) ? "buy" : "sell", m_deal.Volume(), m_deal.Symbol(), m_deal.Price(), open_execution);
            from_date = m_deal.Time();
            deal_ticket = m_deal.Ticket();
            break;
           }
        }
      while(deal_ticket == 0);
     }
   return(deal_ticket);
  }
//+------------------------------------------------------------------+
 
OnTradeTransaction é ótimo, mas tenho um problema real.
Comecei a usá-lo para capturar eventos (abrir uma posição, fechar, modificar) como fazia antes no MT4 (verificando constantemente as posições para adivinhar o que aconteceu). Nesse aspecto, a abordagem do MT5 é muito limpa.
MAS O QUE ACONTECE SE O EA estiver PARADO ou DESLIGADO DURANTE UMA HORA: ele não receberá nenhum evento, é claro, mas quando for reiniciado, não receberá os eventos que foram bagunçados durante uma hora. Portanto, para adivinhar o que aconteceu, ele precisará fazer como no antigo MT4, analisando as posições para adivinhar o que aconteceu. Para resolver esse problema, preciso manter as duas formas de detectar eventos: a forma MT5 e a forma MT4 como backup.
 
OnTradeTransaction está levando até 5 segundos para detectar eventos em ordens pendentes (modificação e exclusão). Isso é normal?
Para posições de mercado, é imediato.
 
Gad Benisty OnTradeTransaction é ótimo, mas tenho um problema real. Comecei a usá-lo para capturar eventos (abrir uma posição, fechar, modificar) como fazia antes no MT4 (verificando constantemente as posições para adivinhar o que aconteceu). Nesse aspecto, a abordagem do MT5 é muito limpa. MAS O QUE ACONTECE SE O EA estiver desligado durante uma hora: ele não receberá nenhum evento, é claro, mas quando for reiniciado, não receberá os eventos que foram bagunçados durante uma hora. Portanto, para adivinhar o que aconteceu, ele precisará fazer como no antigo MT4, analisando as posições para adivinhar o que aconteceu. Para resolver esse problema, preciso manter as duas formas de detectar eventos: a forma MT5 e a forma MT4 como backup.

Concordo... é por isso que, durante a inicialização do Expert Advisor, é feita uma verificação das posições ou ordens pendentes. Mas isso é visto em um artigo um pouco mais adiante nesta mesma sequência.

Tradução automatizada aplicada pelo moderador