Discussion of article "Creating an EA that works automatically (Part 08): OnTradeTransaction"

 

New article Creating an EA that works automatically (Part 08): OnTradeTransaction has been published:

In this article, we will see how to use the event handling system to quickly and efficiently process issues related to the order system. With this system the EA will work faster, so that it will not have to constantly search for the required data.

Now, in a much more complete way, we finally have the C_Manager class and the EA which are friendly with each other. Both can work and make sure they don't become aggressive or unfriendly. Thus, the message flow between the EA and the C_Manager class becomes as the one in Figure 02:

Figure 02

Figure 02. Message flow with new functions


This flow may seem too complicated or completely non-functional, but this is exactly what has been implemented so far.

Looking at Figure 02, you might think that the EA code is very complex. But it is much simpler than what many people consider to be a necessary code for an EA. Especially when it comes to an automated EA. Remember the following: The EA does not actually generate any trades. It is just a means or tool for communicating with the trade server. So it actually just reacts to triggers which are applied to it.

Author: Daniel Jose

 

I have a problem that confuses me.

Thank you for dedicating such a wonderful article. I have a problem that confuses me. I wrote a code to open a position.

It can return the correct value when it is run on demo account, but it returns 0 when it is run on a real account, can you help me to advise where is the problem?

My purpose is to return its deal ticket after opening a position.
//+------------------------------------------------------------------+
//|& 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;
//+------------------------------------------------------------------+
//| EA Trade Initialisation Functions & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   OpenPosition(ORDER_TYPE_BUY, 0.01, 123456);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|& 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; |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& 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?

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.

No, my purpose is to return a dealticket

It can return deal ticket of the position on Demo account , but return zero value on Real account, this is for what ?

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

 

Hello, I am following this series of articles. My respect. It is really helpful.

I am testing the result and after creating an order I delete it. But EA does not allow to create any new order anymore.

This code snippet:

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

It uses trans.position which, in case of order deletion, is always 0. So, that is the value passed to UpdatePosition. Its first condition sends it directly to return statement.

I tried to pass the trans.order ticket but the code ignores it because m_position has zero data, so the ticket is 0 (! = m_Position.Ticket):

After this one. When I try to create a new order, m_ticketPending has the value of first order, so it is not created.

EraseTicketPending is not called because when TRADE_ACTION_REMOVE arrives, this condition is always false, because request. request.TRADE_ACTION_REMOVE is always false.

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

is always false, since request.magic is 0.

I will investigate if this is fixed in next chapters.

Regards

 

I guess I found the problem.

When the order deletion is done from the MT5 user interface, the magic number is 0. So, I have modified the condition to

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

and it seems to work as expected.

Files:
 
Yang Wang #:

Hi, Daniel Jose.

Thank you for dedicating such a wonderful article. I have a problem that confuses me. I wrote a code to open a position.

It can return the correct value when it is run on demo account, but it returns 0 when it is run on a real account, can you help me to advise where is the problem?

My purpose is to return its deal ticket after opening a position.


This function has open position and return 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;
//+------------------------------------------------------------------+
//| EA Trade Initialisation Functions & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   if(!m_symbol.Name(_Symbol)) // sets symbol name
      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; |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| EA Quotation Function & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
bool RefreshRates(void)
  {
//--- refresh rates
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- protection against the return value of "zero"
   if(m_symbol.Ask() == 0 || m_symbol.Bid() == 0)
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|& 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 is great but I have a real issue.
I started to use it to catch events (opening a position, closing, modifying) as I did before in MT4 (checking constantly the positions to guess what happened). In that aspect, MT5 approach is very clean.
BUT WHAT HAPPENS IF THE EA IS DOWN or OFF DURING ONE HOUR : it will not receive any event of course but when it restart it will not get the events it messed during 1 hour. So to guess what happened it will need to do the old MT4 way by analysing the positions to guess what happened. To solve this issue I must keep the 2 ways to detect events : the MT5 way and the MT4 way as backup.
 
OnTradeTransaction is taking up to 5 seconds to detect events on pending orders (modification and deletion). Is this normal ?
For market positions it's immediate.
 
Gad Benisty #: OnTradeTransaction is great but I have a real issue. I started to use it to catch events (opening a position, closing, modifying) as I did before in MT4 (checking constantly the positions to guess what happened). In that aspect, MT5 approach is very clean. BUT WHAT HAPPENS IF THE EA IS DOWN or OFF DURING ONE HOUR : it will not receive any event of course but when it restart it will not get the events it messed during 1 hour. So to guess what happened it will need to do the old MT4 way by analysing the positions to guess what happened. To solve this issue I must keep the 2 ways to detect events : the MT5 way and the MT4 way as backup.

I agree... that's why during Expert Advisor startup, a check of positions or pending orders is done. But this is seen in an article a little further on in this same sequence.

Automated translation applied by moderator