Discusión sobre el artículo "Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction"

 

Artículo publicado Cómo construir un EA que opere automáticamente (Parte 08): OnTradeTransaction:

En este artículo, te mostraré cómo puedes utilizar el sistema de manejo de eventos para poder procesar con más agilidad y de mejor manera las cuestiones relacionadas con el sistema de órdenes, para que el EA sea más rápido. Así, éste no tendrá que estar buscando información todo el tiempo.

Pero ahora en una forma mucho más completa, finalmente tenemos la clase C_Manager y EA siendo amigables entre sí. Hasta el punto de que ambos puedan trabajar y asegurarse de que no son agresivos ni antipáticos. De esta forma, el flujo de mensajes entre el EA y la clase C_Manager pasa a ser el siguiente, como se ve en la Figura 02:

Figura 02


Figura 02 - Flujo de mensajes con las nuevas rutinas implementadas

Autor: Daniel Jose

 

Tengo un problema que me confunde.

Gracias por dedicar un artículo tan maravilloso. Tengo un problema que me confunde. Escribí un código para abrir una posición.

Puede devolver el valor correcto cuando se ejecuta en la cuenta demo, pero devuelve 0 cuando se ejecuta en una cuenta real, ¿puede ayudarme a aconsejar dónde está el problema?

Mi propósito es devolver su billete de acuerdo después de abrir una posición.
//+------------------------------------------------------------------+
//|& nbsp; &nbsp Prueba1.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;
//+------------------------------------------------------------------+
//| Funciones de inicialización de EA Trade & 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();
  }
//+------------------------------------------------------------------+
//| Función de cita EA & 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é

Gracias por dedicarme un artículo tan maravilloso. Tengo un problema que me confunde. He creado un código para abrir una posición.

Puede devolver el valor correcto cuando se ejecuta en una cuenta demo, pero devuelve 0 cuando se ejecuta en una cuenta real. ¿Cuál es el 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, mi propósito es devolver un ticket de operación

Puede devolver el deal ticket de la posición en la cuenta Demo, pero devolver el valor cero en la cuenta Real, ¿para qué?

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 #:

No, mi objetivo es devolver un bilhete de acuerdo

el puede devolver ticket de negociacion de posicion en cuenta Demo, pero devolver valor cero en cuenta Real, isso é para quê?

La línea que le pide que añada NO ES... vou repetir. No es para devolver ningún valor ... ELLA SERVE PARA VERIFICAR O ERRO que está dando ao tentar abrir uma posição ... usted no debe usar el valor devuelto... debe verificar el error en la tabla de errores... Ver la DOCUMENTACIÓN

 

Hola, estoy siguiendo esta serie de artículos. Mi respeto. Es realmente útil.

Estoy probando el resultado y después de crear un pedido lo elimino. Pero EA ya no permite crear ningún pedido nuevo.

Este fragmento de código:

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

Utiliza trans.position que, en caso de eliminación de pedidos, siempre es 0. Entonces, ese es el valor que se pasa a UpdatePosition. Su primera condición lo envía directamente a declaración de devolución.

Intenté pasar el boleto trans.order pero el código lo ignora porque m_position tiene cero datos, por lo que el boleto es 0 (! = m_Position.Ticket):

Después de este. Cuando trato de crear un nuevo pedido, m_ticketPending tiene el valor de primer pedido, por lo que no se crea.

EraseTicketPending no se llama porque cuando llega TRADE_ACTION_REMOVE , esta condición

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

siempre es falso, ya que request.magic es 0.

Investigaré si esto se soluciona en próximos capítulos.

Saludos

 

Supongo que encontré el problema.

Cuando la eliminación del pedido se realiza desde la interfaz de usuario de MT5, el número mágico es 0. Por lo tanto, he modificado la condición para

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

y parece funcionar como se esperaba.

Archivos adjuntos:
 
Yang Wang #:

Hola, Daniel José.

Gracias por dedicar tan maravilloso artículo. Tengo un problema que me confunde. Escribí un código para abrir una posición.

Puede devolver el valor correcto cuando se ejecuta en una cuenta demo, pero devuelve 0 cuando se ejecuta en una cuenta real, ¿puedes ayudarme a saber dónde está el problema?

Mi propósito es devolver su billete de acuerdo después de abrir una posición.


Esta función tiene una posición abierta y devuelve DEAL TICKET :

//+------------------------------------------------------------------+
//|& nbsp; &nbsp Prueba1.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;
//+------------------------------------------------------------------+
//| Funciones de inicialización de EA Trade & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   if(!m_symbol.Name(_Symbol)) // establece el nombre del 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; |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Función de cita EA & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
bool RefreshRates(void)
  {
//--- frecuencias de actualización
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- protección contra el valor de retorno de "cero"
   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 es genial pero tengo un problema real.
Empecé a usarlo para capturar eventos (apertura de una posición, cierre, modificación) como hacía antes en MT4 (comprobando constantemente las posiciones para adivinar qué había pasado). En ese aspecto, el enfoque de MT5 es muy limpio.
PERO QUÉ PASA SI EL EA ESTÁ CAIDO O APAGADO DURANTE UNA HORA : no recibirá ningún evento por supuesto pero cuando se reinicie no recibirá los eventos que se produjeron durante 1 hora. Así que para adivinar lo que pasó tendrá que hacer el viejo MT4 manera mediante el análisis de las posiciones de adivinar lo que pasó. Para resolver este problema debo mantener las 2 formas de detectar eventos: la forma MT5 y la forma MT4 como copia de seguridad.
 
OnTradeTransaction está tardando hasta 5 segundos en detectar eventos en órdenes pendientes (modificación y eliminación). ¿Es esto normal?
Para las posiciones de mercado es inmediato.
 
Gad Benisty OnTradeTransaction es genial pero tengo un verdadero problema. Empecé a usarlo para capturar eventos (apertura de una posición, cierre, modificación) como hacía antes en MT4 (comprobando constantemente las posiciones para adivinar qué había pasado). En ese aspecto, el enfoque de MT5 es muy limpio. PERO QUÉ PASA SI EL EA ESTÁ CAIDO O APAGADO DURANTE UNA HORA: no recibirá ningún evento, por supuesto, pero cuando se reinicie no recibirá los eventos que se produjeron durante 1 hora. Así que para adivinar lo que pasó tendrá que hacer el viejo MT4 manera mediante el análisis de las posiciones de adivinar lo que pasó. Para resolver este problema debo mantener las 2 formas de detectar eventos: la forma MT5 y la forma MT4 como copia de seguridad.

Estoy de acuerdo... por eso durante el arranque del Expert Advisor se hace una comprobación de posiciones u órdenes pendientes. Pero esto se ve en un artículo un poco más adelante en esta misma secuencia.

Traducción automática aplicada por moderador