Órdenes, Posiciones y Transacciones en MetaTrader 5

MetaQuotes | 20 diciembre, 2013


Términos de Trading

El objetivo final de un comerciante es obtener beneficios a través de operaciones comerciales en los mercados financieros. Este artículo describe los términos y procedimientos de la plataforma de trading MetaTrader 5. Es necesario saber todo esto para un entendimiento correcto del trabajo de las funciones de trading del lenguaje MQL5.

Nota: Las órdenes y posiciones activas siempre se muestran en la pestaña "Trade", y las transacciones y órdenes del historial se reflejan siempre en la pestaña "Historial". La orden activa de la pestaña "Trade" no se debería confundir con las órdenes de la pestaña "Historial".

 

Cómo el terminal recibe y almacena la información de trading del servidor

El terminal almacena el historial en una base especial, y recibe solo el historial que falta de las transacciones y órdenes completadas en la cuenta de trading en cada conexión al servidor de trading. Esto se hace para ahorrar tráfico. Al cerrar el terminal de cliente MetaTrader 5 o cambiar la cuenta activa actual, el historial entero se graba en el disco duro y se leerá en la próxima ejecución del terminal.

Todas las bases de datos se guardan en el disco de forma encriptada, y la clave del cifrado depende del ordenador en el que se haya instalado el terminal. Esto protege al usuario del terminal contra el acceso no autorizado a sus datos, en caso de copia.

Durante la conexión a la cuenta, el terminal carga la cuenta base guardada con el historial de la cuenta, y envía una solicitud al servidor de trading para sincronizar su propia base de datos de historia con el historial de la cuenta en el servidor de trading. Además, tras una conexión exitosa a la cuenta, el servidor de trading envía al terminal un informe sobre los eventos de trading actuales relativos a esta cuenta.

Los eventos de trading son los siguientes cambios en la cuenta:

En caso de interrupción de la conexión con el servidor de trading, el terminal intentará reconectarse periódicamente. Tras la reconexión con el servidor, el terminal solicita todos los cambios recientes en el historial de trading para mantener la integridad de los datos en su propia base de datos del historial.

El historial de trading, que se muestra en la pestaña "Historial" del terminal, se toma de la base del historial del terminal, y los cambios del período que se muestran en el historial del terminal solo pueden incrementar el alcance del historial, almacenado en esta base de datos. Reducir el período del historial mostrado no lleva a una eliminación física del historial en la base del servidor.

Instalación del intervalo del historial de trading expuesto

Esto significa que la instalación de un intervalo más corto del historial expuesto no reduce la profundidad del historial de trading guardado. Pero si especificamos que queremos ver un alcance de intervalo más amplio en la pestaña "Historial", tal acción podría llevar a una solicitud del servidor de trading de un historial más profundo, si el historial propio del servidor todavía no tiene los datos solicitados para ese período.

El esquema general de interacción entre el terminal y el servidor de trading del MetaTrader 5 se muestra en la siguiente Figura:


El terminal de cliente envía una solicitud de sincronización a su propia base de historial de trading durante el inicio del terminal, durante la reconexión con el servidor tras un fallo de conexión, durante un cambio de una cuenta a otra, y durante la solicitud directa del historial de trading que falta.

A cambio, el servidor de trading envía un mensaje al cliente, independientemente y sin solicitudes del terminal, sobre los eventos de trading que tienen lugar en la cuenta: los cambios de estado de órdenes y posiciones, la realización de transacciones basadas en órdenes, el cambio de comisiones, el saldo y retirada de efectivo, etcétera.


Acceso al historial de trading del programa MQL5

El terminal puede operar simultáneamente con un número de indicadores, scripts, y EAs, y todos estos programas pueden solicitar la información que necesitan sobre trading: órdenes, transacciones y posiciones. El trabajo directo del programa MQL5 con la base de datos del terminal está excluido a causa de las consideraciones generales de estabilidad, seguridad y rendimiento.

Cada programa MQL5 recibe a peticiónpor su trabajo un "modelo" del entorno de trading en su caché. Una caché es un área especial de memoria para un acceso rápido a la información. Por ejemplo, antes de comenzar a procesar una orden, la orden debe obtenerse en la caché del programa MQL5. Todo el trabajo subsiguiente, cuando hablamos de la orden, se hará en la copia de caché de esa orden.

El trabajo con las posiciones, transacciones y órdenes del historial se lleva a cabo de la misma forma. El esquema general de obtención de la información de trading del programa MQL5 se puestra en esta Figura:


Antes de que los datos sobre el historial de trading se hagan accesible para el procesamiento del programa MQL5, se deben solicitar desde la base de datos del terminal. Tras la solicitud, los datos obtenidos se colocarán en su propia caché del programa MQL5. 

Nota: los datos en la caché no se sincronizan automáticamente con la base de datos del terminal, y por tanto, se deben actualizar constantemente para mantener un estado correcto de los datos en la caché.

Puede que haya consecuencias si la caché se usa de modo inapropiado.

 

La función para trabajar con la caché

El historial de trading puede contener miles de órdenes y transacciones ejecutadas que no se necesitan para el trabajo actual en el programa MQL5. Por tanto, el trabajo con caché se basa en el principio de solicitudes. La caché siempre contiene la información que se subió en la última conexión a la base de datos del terminal. Si necesita obtener el historial entero de órdenes y transacciones, debe solicitarlo explícitamente especificando el intervalo deseado.

Para cada tipo de información, se forma una caché independiente. Los datos sobre las órdenes se guardan en la caché de la orden; la información sobre las posiciones se guarda en la caché de la posición; los datos sobre transacciones y órdenes se guardan en las respectivas instancias del historial de la caché.

Antes de solicitar la información de la caché, esta debe llernarse.

Nota: Cualquier solicitud para llenar la caché primero lleva a cabo una limpieza, independientemente del resultado de la ejecución de la solicitud.


Las funciones de trading se pueden separar en dos categorías: las funciones para llenar la caché y las funciones para leer la información de la caché.

 

La función para llenar la caché

Para procesar el historial de trading, primero se debe obtener y situar en la caché correcta. Las funciones que forman una caché se pueden dividir en dos subgrupos.

La función para llenar la caché (órdenes y posiciones activas):

La función para llenar la caché del historial:

Debemos considerar por separado las dos funciones que afectan el historial de trading en general accesible en la caché.


OrderSelect y OrderGetTicket

Las funciones generales OrderSelect(ticket) y OrderGetTicket() funcionan de la misma manera: llenan la caché con órdenes activas con una sola orden. La función OrderSelect(ticket) sirve para los casos en los que el ticket de una orden se conoce de antemano. La función OrderGetTicket(), en conjunción con OrdersTotal(), permite la revisión de todas las órdenes disponibles en la base de órdenes del terminal.

Tras una llamada a cualquiera de estas funcione, la caché de las órdenes activas contendrá la información solo de una orden, si la orden se seleccionó correctamente. De lo contrario, no habrá nada en la caché de órdenes activas. El resultado de la ejecución de la función OrdersTotal() no cambia: siempre devuelve el número real de órdenes activas en la base del terminal, independientemente de si la caché está llena o no.

 

PositionSelect y PositionGetSymbol

Al igual que con las órdenes, estas dos funciones también funcionan de forma similar para las posiciones: llenan la caché de posiciones con una sola posición. La función PositionGetSymbol(index) requiere el número en la lista de la base de posiciones, como parámetro, y la función PositionSelect(symbol) llena la caché basándose en el nombre del símbolo en el que se abrió la posición. El nombre del símbolo, a cambio, se puede obtener con la función  PositionGetSymbol(index).

Tras ejecutar cualquiera de estas funciones, la caché de posiciones contendrá datos solo de una posición, si la función se ejecutó corrrectamente. De lo contrario, no habrá nada en la caché de posiciones. El resultado de la ejecución de la función PositionsTotal() no depende de si la caché está llena o no: siempre devuelve el número real de posiciones abiertas en la base del terminal para todos los símbolos.

 

HistoryOrderSelect

La función HistoryOrderSelect(ticket) elige en la caché la orden del historial de la base del terminal por su ticket. La función sirve para los casos en los que el ticket de la orden solicitada se conoce de antemano.

Si la ejecución es correcta, la caché contendrá una sola orden, y la función HistoryOrdersTotal() devolverá una sola unidad. De lo contrario, la caché de órdenes del historial quedará vacía, y la función HistoryOrdersTotal() devolverá cero.


HistoryDealSelect

La función HistoryDealSelect(ticket) selecciona la transacción de la base del terminal por su ticket. La función sirve para los casos en los que el ticket de la transacción se conoce de antemano.

Si la ejecución es correcta, la caché contendrá una sola transacción, y la función HistoryDealsTotal() devolverá 1. De lo contrario, la caché de transacciones quedará vacía, y la función HistoryDealsTotal() devolverá cero.

 

La función para obtener información de la caché

Antes de solicitar información sobre las propiedades de la posición, transacción u orden, es necesario actualizar la caché del programa MQL5 correspondiente. Esto es así porque la información solicitada puede haber sido actualizada antes, y esto significa que la copia guardada en la caché ya está obsoleta.

Estas funciones obtienen todos los datos de la caché, y por tanto, para garantizar la obtención de datos precisos para la orden, se recomienda llamar a la función que llena la caché.

Puesto que estas funciones reciben todos sus datos de la caché, para garantizar la obtención de datos precisos para la orden, se recomienda llamar a la función que llena la caché de posiciones.<segment 1718>

El ticket de la orden del historial se puede encontrar usando la función HistoryOrderGetTicket(index) por su índice en la caché de órdenes del historial. Para tener un resguardo garantizado de datos correctos en la orden, se recomienda llamar a la función que llena la caché de órdenes del historial.

El ticket de las transacciones se puede obtener usando la función HistoryDealGetTicket(index) por su índice en la caché de transacciones. Para tener un resguardo garantizado de datos correctos sobre la transacción, se recomienda llamar a la función que llena la caché de transacciones.

La función para obtener el ticket del historial de la caché

La función HistoryOrderGetTicket (index) devuelve el ticket de la orden del historial por su índice de la caché de órdenes de historial (¡no de la base del terminal!). El ticket obtenido se puede usar en la función  HistoryOrderSelect (ticket), que limpia la caché y la rellena solo con una orden, en caso de que tenga éxito. Recuerde que el valor devuelto por la función HistoryOrdersTotal() depende del número de órdenes en la caché.

La función HistoryDealGetTicket(index) devuelve el ticket de la transacción por su índice de la caché de transacciones. El ticket de la transacción se puede usar en la función HistoryDealSelect(ticket), que limpia la caché y la rellena solo con <b6>una </b6>transacción, en caso de que tenga éxito. El valor devuelto por la función HistoryDealsTotal() depende del número de transacciones en la caché.

Nota: Antes de llamar a las funciones HistoryOrderGetTicket (index) y HistoryDealGetTicket (index), debe llenar la caché del historial de órdenes y transacciones con un volumen suficiente. Para hacer esto, utilice una de las siguientes funciones: HistorySelect (start, end), HistorySelectByPosition (position_ID), HistoryOrderSelect (ticket), y HistoryDealSelect (ticket).

 

Obtener información usando órdenes activas

Revisar las órdenes activas actuales es un procedimiento estándar. Si se necesita obtener información sobre una orden específica, se puede hacer con la función OrderSelect(ticket) si se conoce su ticket.

bool selected=OrderSelect(ticket);
if(selected)
  {
   double price_open=OrderGetDouble(ORDER_PRICE_OPEN);
   datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
   string symbol=OrderGetString(ORDER_SYMBOL);
   PrintFormat("Ордер #%d for %s was set at %s",ticket,symbol,TimeToString(time_setup));
  }
else
  {
   PrintFormat("Error al seleccionar la orden con el ticket %d. Error %d",ticket, GetLastError());
  }

En el ejemplo de arriba, se entiende que el ticket de la orden se conoce de antemano, y se obtiene con la variable global. En casos generales, no obstante, no dispondremos de la información del ticket, y por tanto debemos recurrir a la ayuda de la función OrderGetTicket(index), que también selecciona una orden y la coloca en la caché. Pero solo se debe especificar como parámetro el número de orden en la lista de órdenes actuales. 

El algoritmo general para trabajar con órdenes (análogamente con transacciones y posiciones) es el siguiente:

  1. Obtener el número total de órdenes con la función OrdersTotal();
  2. Organizar el bucle a través de una búsqueda de todas las órdenes por sus índices en la lista;
  3. Copiar las órdenes una a una en la caché usando la función OrderGetTicket();
  4. Obtener los datos correctos de la caché usando las funciones OrderGetDouble(), OrderGetInteger(), y OrderGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.

Este es un breve ejemplo de tal algoritmo:

input long my_magic=555;
//+------------------------------------------------------------------+
//| Función de inicio de programa de Script                          |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtener el número total de órdenes
   int orders=OrdersTotal();
//--- revisar la lista de órdenes
   for(int i=0;i<orders;i++)
     {
      ResetLastError();
      //--- copiar en la caché la orden por su número en la lista
      ulong ticket=OrderGetTicket(i);
      if(ticket!=0)// si la orden se copió en la caché con éxito, trabaje con ella
        {
         double price_open  =OrderGetDouble(ORDER_PRICE_OPEN);
         datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP);
         string symbol      =OrderGetString(ORDER_SYMBOL);
         long magic_number  =OrderGetInteger(ORDER_MAGIC);
         if(magic_number==my_magic)
           {
            //  procesar la orden con el ORDER_MAGIC especificado
           }
         PrintFormat("La orden #%d para %s se ejecuto %s, ORDER_MAGIC=%d",ticket,symbol,TimeToString(time_setup),magic_number);
        }
      else         // la llamada a OrderGetTicket() se completó sin éxito 
        {
         PrintFormat("Error al obtener una orden de la lista de la caché. Error code: %d",GetLastError());
        }
     }
  }

 

Obtener información sobre posiciones abiertas

El monitoreo constante de posiciones abiertas no es solo un proceso estándar, sino que es recomendable implementarlo en cada instancia. Para obtener información sobre posiciones específicas, es suficiente saber el nombre del instrumento por el cual se abrió. Para hacer esto, use la función PositionSelect(symbol). Para los casos en los que el EA funciona solo en un símbolo (en el símbolo del gráfico al que está adjunto), el nombre del símbolo se pude obtener con una función Symbol() o de una variable predefinida _Symbol.

//--- buscaremos la posición por el símbolo del gráfico en el que trabaja el EA
   string symbol=Symbol();
//--- intentar obtener la posición
   bool selected=PositionSelect(symbol);
   if(selected) // si la posición está seleccionada
     {
      long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
      double price           =PositionGetDouble(POSITION_PRICE_OPEN);
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      long pos_magic         =PositionGetInteger(POSITION_MAGIC);
      string comment         =PositionGetString(POSITION_COMMENT);
      PrintFormat("Posición #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                 pos_id, symbol, pos_magic, price,EnumToString(type), comment);
     }

   else        // si no se tuvo éxito al seleccionar la posición
     {
      PrintFormat("La selección de la posición por el símbolo %s no tuvo éxito. Error",symbol,GetLastError());
     }
  }

En casos generales, la información sobre el símbolo se puede obtener usando la función PositionGetSymbol (index), que selecciona una posición y la coloca en la caché. Como parámetro, es necesario especificar el índice de posición en la lista de posiciones abiertas. Esto se puede hacer mejor a través de una búsqueda de todas las posiciones en el bucle.

El algoritmo general para las posiciones abiertas:

  1. Obtener el número total de posiciones con la función PositionsTotal();
  2. Organizar el bucle a través de una búsqueda de todas las posiciones por sus índices en la lista;
  3. Copiar las posiciones una por una en la caché usando la función PositionGetSymbol();
  4. Obtener los datos sobre posición requeridos de la caché usando las funciones PositionGetDouble(), PositionGetInteger(), y PositionGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.

Un ejemplo de tal algoritmo:

#property script_show_inputs

input long my_magic=555;
//+------------------------------------------------------------------+
//| Función de inicio de programa de Script                |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtener el número total de posiciones
   int positions=PositionsTotal();
//--- revisar la lista de posiciones
   for(int i=0;i<positions;i++)
     {
      ResetLastError();
      //--- copiar en la caché la posición por su número en la lista
      string symbol=PositionGetSymbol(i); //  obtener el nombre del símbolo con el que abrió la posición
      if(symbol!="") // la orden se copió en la caché con éxito, trabaje con ella
        {
         long pos_id            =PositionGetInteger(POSITION_IDENTIFIER);
         double price           =PositionGetDouble(POSITION_PRICE_OPEN);
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         long pos_magic         =PositionGetInteger(POSITION_MAGIC);
         string comment         =PositionGetString(POSITION_COMMENT);
         if(pos_magic==my_magic)
           {
           //  procesar la posición con el POSITION_MAGIC especificado
           }
         PrintFormat("Posición #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                     pos_id,symbol,pos_magic,price,EnumToString(type),comment);
        }
      else           // la llamada a PositionGetSymbol() se completó sin éxito
        {
         PrintFormat("Error al recibir en la caché la posición con el índice %d."+
                     " Error code: %d", i, GetLastError());
        }
     }
  }

Reglas para trabajar con la caché del historial

A menudo, el programador escribe el código para trabajar con el historial de la caché de forma que funcione bien solo si el historial contiene de 5 a 10 transacciones y órdenes. Un ejemplo típico de enfoque incorrecto: cargar el historial de trading entero en la caché y procesarlo en un bucle, buscando a través de todas las órdenes y operaciones.

//---
   datetime start=0;           // fecha inicial establecida en el año 1970
   datetime end=TimeCurrent();  // la fecha final establecida al tiempo presente del servidor
//--- solicitar a la caché del programa el historial de trading entero
   HistorySelect(start,end);
//--- obtener el número de todas las órdenes del historial
   int history_orders=HistoryOrdersTotal();
//--- ahora, revisar todas las órdenes
   for(int i=0;i<history_orders;i++)
     {
     //  procesar cada orden en el historial
     }   
 
    ...
       
//--- obtener el número de todas las transacciones del historial
   int deals=HistoryDealsTotal();
//--- ahora revisar todas las transacciones
   for(int i=0;i<deals;i++)
     {
     //  procesar cada transacción en el historial
     }

El intento de manejar todo el historial de trading es, en la mayoría de los casos, un movimiento incorrecto. Cuando el número de órdenes/transacciones procesadas se mueve en torno a los miles o decenas de miles, el trabajo del programa se ralentiza drásticamente.

Nota: ¡Siempre proceda con cautela a la hora de llamar a la función HistorySelect()! Una carga excesiva e irresponsable de todo el historial de trading disponible en la caché del programa MQL5 podría perjudicar su rendimiento.

Esto es importante, principalmente, para las simulaciones: el usuario descubrirá que el Probador de pronto se vuelve lento, y comienza a buscar las razones de ello en el terminal de cliente. Por tanto, siempre piense primero en optimizar el código del programa MQL5 (EA e indicadores, que se llaman desde el EA). No dependa del hecho de que el ordenador está hecho de hierro y tiene varios núcleos.

Para un rendimiento correcto del EA y el indicador online, esto es igual de importante. Un código del programa no óptimo puede paralizar el trabajo del ordenador más potente. 

El algoritmo correcto para trabajar con el historial de trading:

  1. Determine la necesidad de solicitar el historial de trading en la caché. Si no es necesario, no realice las siguientes accione:
  2. Determinar la fecha final del historial de trading (quizás el historial hasta el momento presente no es necesario);
  3. Calcular la fecha inicial del historial de trading, empezando por la fecha de finalización. Generalmente, los EAs no requieren más de un día o una semana en el historial de trading;
  4. Obtener los tickets de transacciones y órdenes del historial para obtener las propiedades por los tickets conocidos:
    • HistoryOrderGetDouble()
    • HistoryOrderGetInteger()
    • HistoryOrderGetString()
    • HistoryDealGetDouble()
    • HistoryDealGetInteger()
    • HistoryDealGetString()
  5. Si se desconocen los tickets, organice un ciclo por distribución;
  6. En el bucle, obtenga el ticket para cada transacción/orden de la caché del historial de trading por el índice (HistoryOrderGetTicket(Index) y HistoryDealGetTicket(Index));
  7. Obtenga las propiedades necesarias de órdenes y transacciones por los tickets conocidos (vea el punto 4).

Un ejemplo de código para este algoritmo:

//--- la variable que se establece como “true” durante el cambio en el historial de trading
   bool TradeHistoryChanged=false;
//--- aquí revisamos los cambios en el historial y utilizamos el TradeHistoryChanged=true si es necesario
//... el código necesario

//--- comprobar si hay cambios en el historial de trading o no
   if(!TradeHistoryChanged) return;

//--- si el historial ha cambiado, tiene sentido cargarlo en la caché 
//--- fecha final: tiempo actual del servidor
   datetime end=TimeCurrent();
//--- fecha de comienzo: hace tres días
   datetime start=end-3*PeriodSeconds(PERIOD_D1);
//--- solicitar en la caché del programa el historial de trading de los últimos tres días
   HistorySelect(start,end);
//--- obtener el número de órdenes en la caché del historial
   int history_orders=HistoryOrdersTotal();
//--- ahora revisar las órdenes
   for(int i=0;i<history_orders;i++)
     {
      //--- obtener el ticket de la orden del historial
      ulong ticket=HistoryOrderGetTicket(i);
      //--- trabajar con esta orden – recibir sus problemas
      long order_magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC);
      // obtener el resto de las propiedades de la orden por el ticket
      // ...
     }

La idea básica que se presenta en este ejemplo es que, primero, debe verificar el hecho de que se han dado cambios en el historial de trading. Una de las opciones es, dentro de la función OnTrade(), configurar en la variable global TradeHistoryChanged el valor "true", puesto que el evento Trade siempre vuelve con algún tipo de evento comercial.

Si el historial de trading no ha cambiado, entonces no hay necesidad de cargar el historial de trading en la caché de nuevo y malgastar recursos de la CPU. Esto es lógico, y no requiere explicación alguna. Si el historial de trading ha cambiado, entonces cargaremos solo la parte necesaria y trataremos cada transacción/orden solo una vez. Evite ciclos de repetición innecesarios.

Nota: cada solicitud a la caché del historial de trading entero realizada por la función HistorySelect() se debe hacer por una razón, al igual que cada ciclo de procesamiento de transacciones y cada orden del historial. De lo contrario, estará utilizando los recursos de su ordenador de forma ineficiente.

Puede ver ejemplos de uso correcto e incorrecto del historial de trading en los archivos adjuntos a este artículo, WrongWorkWithHistory.mq5 y RightWorkWithHistory.mq5.

 

Obtener información por órdenes del historial

Trabajar con órdenes del historial apenas se diferencia de trabajar con órdenes activas, pero hay una excepción. Si el número de órdenes activas en la caché del programa MQL5 no puede ser más de uno, entonces el resultado de HistoryOrdersTotal() y el número de órdenes del historial en la caché dependerá de cuánto historial de trading se haya cargado con las funciones HistorySelect(start, end), HistorySelectByPosition() o HistoryOrderSelection().

Nota: Si el historial de trading no se ha cargado en la caché del programa MQL5 por una de las siguientes funciones, HistorySelect(), HistorySelectByPosition() o HistoryOrderSelect(), entonces trabajar con órdenes del historial será imposible. Asegúrese de solicitar el historial requerido de transacciones y órdenes antes de recibir los datos en el historial de trading.

Por ejemplo, facilitaremos un script que busca la última orden del último día, y muestra su información.

// --- determinar los intervalos de tiempo y el historial de trading requerido
   datetime end=TimeCurrent();                // tiempo actual del servidor
   datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás
//--- solicitar en la caché del programa el historial de trading de un día
   HistorySelect(start,end);
//--- recibir el número de órdenes en el historial
   int history_orders=HistoryOrdersTotal();
//--- obtener el ticket de la orden, con el último índice en la lista, del historial
   ulong order_ticket=HistoryOrderGetTicket(history_orders-1);
   if(order_ticket>0) // obtener la orden del historial en la caché, trabajar con ella
     {
      //--- estado de orden
      ENUM_ORDER_STATE state=(ENUM_ORDER_STATE)HistoryOrderGetInteger(order_ticket,ORDER_STATE);
      long order_magic      =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
      long pos_ID           =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
      PrintFormat("Order #%d: ORDER_MAGIC=#%d, ORDER_STATE=%d, ORDER_POSITION_ID=%d",
                  order_ticket,order_magic,EnumToString(state),pos_ID);
     }
   else              // intento de obtener la orden sin éxito
     {
      PrintFormat("En total, en el historial de %d órdenes, no pudimos seleccionar la orden"+
                  " with the index %d. Error %d",history_orders,history_orders-1,GetLastError());
     }
 

En casos más generales, se deben organizar las órdenes en el bucle desde la caché y analizarlas. El algoritmo general será así:

  1. Determinar los intervalos de tiempo del historial suficiente. Si el historial se carga con a función HistorySelect() no es recomendable cargar el historial de trading entero en la caché;
  2. Cargar en la caché del programa las funciones de historial de trading HistorySelect(), HistorySelectByPosition() o HistoryOrderSelect (ticket);
  3. Obtener el número total de órdenes en la caché con la función HistoryOrdersTotal();
  4. Organizar el ciclo a través de una búsqueda de todas las órdenes por sus índices en la lista;
  5. Obtener un ticket de las órdenes de la caché usando la función HistoryOrderGetTicket() ;
  6. Obtener los datos de las órdenes de la caché usando las funciones HistoryOrderGetDouble(), HistoryOrderGetInteger() y HistoryOrderGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.

Un ejemplo de tal algoritmo:

#property script_show_inputs

input long my_magic=999;
//+------------------------------------------------------------------+
//| Función de inicio de programa de Script                          |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- determinar los intervalos de tiempo del historial de trading requerido
   datetime end=TimeCurrent();                // tiempo actual del servidor
   datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás
//--- solicitar en la caché del programa el intervalo de historial de trading necesario
   HistorySelect(start,end);
//--- recibir el número de órdenes en el historial
   int history_orders=HistoryOrdersTotal();
//--- revisar todas las órdenes
   for(int i=0;i<history_orders;i++)
     {
      //--- obtener el ticket de la orden del historial por su número en la lista
      ulong order_ticket=HistoryOrderGetTicket(i);
      if(order_ticket>0) //  obtener la orden del historial y trabajar con ella
        {
         //--- tiempo de ejecución
         datetime time_done=HistoryOrderGetInteger(order_ticket,ORDER_TIME_DONE);
         long order_magic  =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC);
         long pos_ID       =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID);
         if(order_magic==my_magic)
           {
           //  procesar la orden con el ORDER_MAGIC especificado
           }
         PrintFormat("Order #%d: ORDER_MAGIC=#%d, time_done %s, ORDER_POSITION_ID=%d",
                     order_ticket,order_magic,TimeToString(time_done),pos_ID);
        }
      else               // intento de obtener la orden del historial sin éxito
        {
         PrintFormat("no pudimos seleccionar la orden con el índice %d. Error %d",
                     i,GetLastError());
        }
     }
  }
Nota: ¡Siempre proceda con cautela a la hora de llamar a la función HistorySelect()! Una carga excesiva e irresponsable de todo el historial de trading disponible en la caché del programa MQL5 podría perjudicar su rendimiento.

 

Obtener información de transacciones del historial

El procesamiento de transacciones tiene los mismos elementos que el procesamiento de órdenes del historial. El número de transacciones en el historial de trading y el resultado de la ejecución de HistoryDealsTotal() dependerán de cuánto historial de trading se haya cargado con las funciones HistorySelect(start, end), HistorySelectByPosition() o HistoryOrderSelection().

Para llenar la caché solo con una transacción por su ticket, use la función HistoryDealSelect(ticket).

// --- determinar los intervalos de tiempo del historial de trading requerido
   datetime end=TimeCurrent();                // tiempo actual del servidor
   datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás
//--- solicitar en la caché del programa el historial de trading de un día
   HistorySelect(start,end);
//--- recibir el número de transacciones en el historial
   int deals=HistoryDealsTotal();
//--- obtener el ticket de la orden, con el último índice en la lista, del historial
   ulong deal_ticket=HistoryDealGetTicket(deals-1);
   if(deal_ticket>0) // obtener la transacción del historial en la caché, trabajar con ella
     {
      //--- el ticket de la orden, basado en la transacción que se realizó
      ulong order     =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
      long order_magic=HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
      long pos_ID     =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
      PrintFormat("Deal #%d for the order #%d with the ORDER_MAGIC=%d  that participated in the position",
                  deals-1,order,order_magic,pos_ID);
     }
   else              // intento de obtener la transacción sin éxito
     {
      PrintFormat("En total, en el historial %d de transacciones, no pudimos seleccionar una transacción"+
                  " with the index %d. Error %d",deals,deals-1,GetLastError());
     }

En casos más generales, se necesita buscar las transacciones en el bucle desde la caché y analizarlas. El algoritmo general será así:

  1. Determinar las barreras del historial suficiente . Si el historial se carga con la función HistorySelect(start, end), no es recomendable cargar el historial entero de trading en la caché;
  2. Cargar en la caché del programa el historial de trading de las funciones HistorySelect() o HistorySelectByPosition();
  3. Obtener el número total de transacciones con la función HistoryDealsTotal();
  4. Organizar el ciclo a través de una búsqueda de todas las transacciones por sus índices en la lista;
  5. Copiar las transacciones una por una en la caché usando la función HistoryDealGetTicket();
  6. Obtener los datos sobre la transacción requeridos de la caché usando las funciones HistoryDealGetDouble(), HistoryDealGetInteger(), y HistoryDealGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.

Un ejemplo de algoritmo para calcular los beneficios y pérdidas:

input long my_magic=111;
//+------------------------------------------------------------------+
//|  Función de inicio de programa de Script                         |
//+------------------------------------------------------------------+
void OnStart()
  {
// --- determinar los intervalos de tiempo del historial de trading requerido
   datetime end=TimeCurrent();                 // tiempo actual del servidor
   datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás

//--- solicitar en la caché del programa el intervalo necesario del historial de trading
   HistorySelect(start,end);
//--- recibir el número de transacciones en el historial
   int deals=HistoryDealsTotal();

   int returns=0;
   double profit=0;
   double loss=0;
//--- revisar todas las transacciones en el historial
   for(int i=0;i<deals;i++)
     {
      //--- obtener el ticket de la transacción por su índice en la lista
      ulong deal_ticket=HistoryDealGetTicket(i);
      if(deal_ticket>0) // obtener la transacción del historial en la caché, trabajar con ella
        {
         string symbol             =HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         datetime time             =HistoryDealGetInteger(deal_ticket,DEAL_TIME);
         ulong order               =HistoryDealGetInteger(deal_ticket,DEAL_ORDER);
         long order_magic          =HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
         long pos_ID               =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
         ENUM_DEAL_ENTRY entry_type=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);

         //--- procesar las transacciones con el DEAL_MAGIC especificado
         if(order_magic==my_magic)
           {
            //... acciones necesarias
           }

         //--- calcular las pérdidas y beneficios con resultados fijos
         if(entry_type==DEAL_ENTRY_OUT)
          {
            //--- incrementar el número de transacciones 
            returns++;
            //--- resultado de la fijación
            double result=HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
            //--- introducir los resultados positivos en los beneficios resumidos
            if(result>0) profit+=result;
            //--- introducir los resultados negativos en las pérdidas resumidas
            if(result<0) loss+=result;
           }
        }
      else // intento de obtener la transacción sin éxito
        {
         PrintFormat("No pudimos seleccionar una transacción con el índice %d. Error %d",
                     i,GetLastError());
        }
     }
   //--- imprimir los resultados de los cálculos
   PrintFormat("El número total de transacciones %d con un resultado financiero. Profit=%.2f , Loss= %.2f",
               returns,profit,loss);
  }
Nota: ¡Siempre proceda con cautela a la hora de llamar a la función HistorySelect()! Una carga excesiva e irresponsable de todo el historial de trading disponible en la caché del programa MQL5 podría perjudicar su rendimiento.

 

Obtener una posición en la caché del historial por el identificador de la posición (POSITION_IDENTIFIER)

La función HistorySelectByPosition (position_ID), al igual que la función HistorySelect (start, end), llena la caché con transacciones y órdenes del historial, pero solo con una condición: deben tener el identificador específico de la posición (POSITION_IDENTIFIER). El identificador de la posición es un número único que se asigna automáticamente a cada posición reabierta, y no cambia durante su vida. Mientras tanto... se debe tener en cuenta que el cambio de la posición (cambio del tipo de posición de POSITION_TYPE_BUY a POSITION_TYPE_SELL) no cambia el identificador de la posición.

Cada posición abierta es el resultado de una o más transacciones en ese instrumento. Por tanto, para analizar cambios de posición durante su vida, cada transacción y orden basada en la transacción que se realizó, se le asigna un identificador de la posición en la que la transacción tuvo lugar. Por tanto, sabiendo el identificador de las posiciones abiertas actuales, podemos reconstruir el historial entero, y encontrar todas las órdenes y transacciones que lo han cambiado.

La función HistorySelectByPosition(position_ID) sirve para ahorrarle al programador el tener que escribir su código propio de iteración a través del historial de trading entero en busca de tal información. Un algoritmo típico para trabajar con esta función:

  1. Obtener el identificador de la posición;
  2. Obtener todas las órdenes y transacciones cuyos identificadores se corresponden con el identificador de las posiciones actuales en la caché del historial de trading usando la función HistorySelectByPosition();
  3. Procesar el historial de trading según el algoritmo,

 

Conclusión

La plataforma del subsistema de trading MetaTrader 5 entera está bien pensada y es fácil de usar. Además, la abundancia de funciones de trading permite resolver cada problema específico de la forma más eficiente.

Pero a pesar de que las clases de trading especializadas de la Biblioteca estándar nos evitan tener que preocuparnos demasiado sobre los detalles y escribir programas en un nivel avanzado sin meternos en la implementación, entender los conceptos básicos nos permitirá crear EAs de trading más seguros y eficientes.

Todos los ejemplos expuestos se pueden encontrar en los archivos adjuntos a este artículo.