Descargar MetaTrader 5

Limitaciones y verificaciones en Asesores Expertos

18 diciembre 2013, 08:09
MetaQuotes Software Corp.
0
759

Introducción

Al crear un algoritmo para un sistema de trading automatizado, debería ser capaz no solo de procesar los precios para hacer señales de trading, sino también de obtener mucha información auxiliar sobre límites impuestos en las operaciones del Asesor Experto. Este artículo le enseñará cómo...

  • ... obtener información sobre sesiones de trading;
  • ... comprobar si tiene liquidez suficiente para abrir una posición;
  • ... imponer una límite en el volumen total de trading con un símbolo;
  • ... imponer un límite en el número total de órdenes;
  • ... calcular las pérdidas potenciales entre el precio de entrada y Stop Loss;
  • ... comprobar si hay una nueva barra.

Sesiones de Trading y Cotización

Para recibir información sobre sesiones de trading, debería usar la función SymbolInfoSessionTrade(). Para sesiones de cotización, use la correspondiente función SymbolInfoSessionQuote(). Ambas funciones actúan de la misma forma: si hay una sesión con el índice especificado para el día específico de la semana (la indexación de sesiones empieza de cero), la función devuelve un valor true. El momento de comienzo y final de una sesión se escribe en los parámetros cuarto y quinto pasados por el link.

//--- check if there is a quotation session with the number session_index
bool session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);

Para ver todas las sesiones del día especificado, llame a esta función en un bucle hasta que devuelva false.

//+------------------------------------------------------------------+
//|  Display information about quotation sessions                    |
//+------------------------------------------------------------------+
void PrintInfoForQuoteSessions(string symbol,ENUM_DAY_OF_WEEK day)
  {
//--- start and end of session
   datetime start,finish;
   uint session_index=0;
   bool session_exist=true;

//--- go over all sessions of this day
   while(session_exist)
     {
      //--- check if there is a quotation session with the number session_index
      session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);

      //--- if there is such session
      if(session_exist)
        {
         //--- display the day of week, the session number and the time of start and end
         Print(DayToString(day),": session index=",session_index,"  start=",
               TimeToString(start,TIME_MINUTES),"    finish=",TimeToString(finish-1,TIME_MINUTES|TIME_SECONDS));
        }
      //--- increase the counter of sessions
      session_index++;
     } 
  }

El día de la semana se muestra en formato de cadena de caracteres usando la función personalizada DayToString(), que recibe el valor de la enumeración ENUM_DAY_OF_WEEK como parámetro.

//+------------------------------------------------------------------+
//| Receive the string representation of a day of week               |
//+------------------------------------------------------------------+
string DayToString(ENUM_DAY_OF_WEEK day)
  {
   switch(day)
     {
      case SUNDAY:    return "Sunday";
      case MONDAY:    return "Monday";
      case TUESDAY:   return "Tuesday";
      case WEDNESDAY: return "Wednesday";
      case THURSDAY:  return "Thursday";
      case FRIDAY:    return "Friday";
      case SATURDAY:  return "Saturday";
      default:        return "Unknown day of week";
     }
   return ""  }

El código final del script SymbolInfoSession.mq5 está adjunto al final del artículo. Aquí mostraremos solo la parte principal.

void OnStart()
  {
//--- the array where the days of week are stored
   ENUM_DAY_OF_WEEK days[]={SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY};
   int size=ArraySize(days);

//---
   Print("Quotation sessions");
//--- go over all the days of week
   for(int d=0;d<size;d++)
     {
      PrintInfoForQuoteSessions(Symbol(),days[d]);
     }

//---
   Print("Trading sessions");
//--- go over all the days of week
   for(int d=0;d<size;d++)
     {
      PrintInfoForTradeSessions(Symbol(),days[d]);
     } 
  }

Comprobar el Margen

Para descubrir la cantidad de margen requerida para abrir o aumentar una posición, puede usar la función OrderCalcMargin(); el primer parámetro que le llegue será un valor de la enumeración ENUM_ORDER_TYPE. Para una operación de compra, debería llamarla con el parámetro ORDER_TYPE_BUY; para una venta, use el parámetro ORDER_TYPE_SELL . La función devuelve la cantidad de margen dependiendo del número de lotes y el precio de apertura.

void OnStart()
  {
//--- the variable to receive the value of margin
   double margin;
//--- to receive information about the last tick
   MqlTick last_tick;
//--- try to receive the value from the last tick
   if(SymbolInfoTick(Symbol(),last_tick))
     {
      //--- reset the last error code
      ResetLastError();
      //--- calculate margin value
      bool check=OrderCalcMargin(type,Symbol(),lots,last_tick.ask,margin);
      if(check)
        {
         PrintFormat("For the operation %s  %s %.2f lot at %G required margin is %.2f %s",OrderTypeToString(type),
                     Symbol(),lots,last_tick.ask,margin,AccountInfoString(ACCOUNT_CURRENCY));
        }
     }
   else
     {
      Print("Unsuccessful execution of the SymbolInfoTick() function, error ",GetLastError());
     } 
  }

Debería tener en cuenta que la función OrderCalcMargin() permite calcular el valor de margen no solo para las órdenes de mercado, sino también para órdenes pendientes. Puede comprobar los valores que se devuelven para todos los tipos de órdenes usando el script Check_Money.mq5 script.

La función OrderCalcMargin() sirve para calcular el tamaño de margen para órdenes pendientes, puesto que podría requerirse una reserva financiera en algunos sistemas de trading también para órdenes pendientes. Generalmente, el tamaño de margen para órdenes pendientes se calcula a través de un coeficiente del tamaño del margen para las posiciones largas y cortas.

Identificador

Descripción

Tipo de propiedad

SYMBOL_MARGIN_LONG

Porcentaje de cobro de margen en posiciones largas

doble

SYMBOL_MARGIN_SHORT

Porcentaje de cobro de margen en posiciones cortas

doble

SYMBOL_MARGIN_LIMIT

Porcentaje de cobro de margen en órdenes Limit

doble

SYMBOL_MARGIN_STOP

Porcentaje de cobro de margen en órdenes Stop

doble

SYMBOL_MARGIN_STOPLIMIT

Porcentaje de cobro de margen en órdenes Stop Limit

doble


Puede obtener los valores de esos coeficientes usando este sencillo código:

//--- Calculate the rates of margin charging for different types of orders   
   PrintFormat("Rate of margin charging on long positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LONG));
   PrintFormat("Rate of margin charging on short positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_SHORT));
   PrintFormat("Rate of margin charging on Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LIMIT));
   PrintFormat("Rate of margin charging on Stop orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOP));
   PrintFormat("Rate of margin charging on Stop Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOPLIMIT));

Para símbolos Forex, los porcentajes de cobro de margen para órdenes pendientes suelen ser igual a 0, es decir, no hay requisitos de margen para ellos.

Resultados de la ejecución del script Check_Money.mq5.

Resultados de la ejecución del script Check_Money.mq5.

Dependiendo de la forma de cobro por margen, el sistema de gestión de dinero podría cambiar, y el sistema de trading en sí mismo podría experimentar algunas limitaciones si se requiere un margen para órdenes pendientes. Por eso, estos parámetros podrían ser también limitaciones naturales de las operaciones de un Asesor Experto.

Tener en Cuenta Posibles Beneficios y Pérdidas

Al establecer un nivel de detención protector, debería estar preparado para su activación. El riesgo de pérdidas potenciales se debería tener en cuenta en términos de dinero, y la función OrderCalcProfit() sirve para este propósito. Es muy similar a la función, OrderCalcMargin(), de la que ya hemos hablado, pero requiere tanto precios de apertura como de cierre para sus cálculos.

Especifique uno de los dos valores de la enumeración ENUM_ORDER_TYPE como primer parámetro - ORDER_TYPE_BUY o ORDER_TYPE_SELL; otros tipos de órdenes le darán error. En el último parámetro debería pasar una variable usando la referencia en la que la función OrderCalcProfit() escribirá el valor de los beneficios/pérdidas en caso de que la ejecución tenga éxito.

Ejemplo de uso de la función CalculateProfitOneLot(), que calcula los beneficios o las pérdidas al cerrar una posición larga con niveles específicos de entrada y salida:

//+------------------------------------------------------------------+
//| Calculate potential profit/loss for buying 1 lot                 |
//+------------------------------------------------------------------+
double CalculateProfitOneLot(double entry_price,double exit_price)
  {
//--- receive the value of profit to this variable
   double profit=0;
   if(!OrderCalcProfit(ORDER_TYPE_BUY,Symbol(),1.0,entry_price,exit_price,profit))
     {
      Print(__FUNCTION__,"  Failed to calculate OrderCalcProfit(). Error ",GetLastError());
     }
//---
   return(profit); 
  }

El resultado del cálculo de esta función se muestra en la figura.

Ejemplo del cálculo y muestra de las pérdidas potenciales en el gráfico usando la función OrderCalcProfit().

El código entero se puede encontrar en el archivo adjunto Asesor Experto CalculateProfit_EA.mq5.

Comprobar si hay una Nueva Barra

Con el desarrollo de varios sistemas de trading se da por hecho que las señales de trading se calculan solo cuando aparece una nueva barra, y todas las acciones de trading se realizan solo una vez. El modo "Solo precios de Apertura" ("Only open prices") del Probador de Estrategias en el terminal de cliente MetaTrader 5 es bueno para simular estos sistemas de trading automatizados.

En el modo "Solo precios de apertura", todos los cálculos de indicadores y la llamada de la función OnTick() en el Asesor Experto se realizan solo una vez por barra durante la simulación. Es el modo más rápido de trading, y por regla general, el más tolerante con fallos en oscilaciones de precios insignificantes a la hora de crear sistemas de trading. Al mismo tiempo, por supuesto, los indicadores usados en el Asesor Experto deben estar escritos correctamente, y no deben distorsionar sus valores cuando viene una nueva barra.

El Probador de Estrategias en modo "Solo precios de Apertura" le ahorra tener que ocuparse de que el Asesor Experto se ejecute solo una vez por barra, y es muy conveniente. Pero al trabajar en modo de tiempo real en una simulación, o en una cuenta real, un comerciante debería controlar la actividad de su Asesor Experto para que solo realice una operación de trading por cada señal recibida. La forma más rápida para ello es rastrear la apertura de la actual barra sin formar.

Para obtener el momento en que se abrió la última barra, debe usar la función SeriesInfoInteger() , que especifica el nombre del símbolo, el intervalo y la propiedad SERIES_LASTBAR_DATE. Comparando constantemente el momento de apertura de la barra actual con una de las barras guardadas en una variable, podrá detectar fácilmente el momento en el que aparece una nueva barra. Nos permite crear la función personalizada isNewBar(), que tiene el siguiente aspecto:

//+------------------------------------------------------------------+
//| Return true if a new bar appears for the symbol/period pair      |
//+------------------------------------------------------------------+
bool isNewBar()
  {
//--- remember the time of opening of the last bar in the static variable
   static datetime last_time=0;
//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set time and exit
      last_time=lastbar_time;
      return(false);
     }

//--- if the time is different
   if(last_time!=lastbar_time)
     {
      //--- memorize time and return true
      last_time=lastbar_time;
      return(true);
     }
//--- if we pass to this line then the bar is not new, return false
   return(false); 
  }

Puede ver un ejemplo de uso de la función en el archivo adjunto Asesor Experto CheckLastBar.mq5.

Mensajes del Expert Advisor CheckLastBar Expert Advisor sobre la aparición de nuevas barras en intervalo M1.

Mensajes del Asesor Experto CheckLastBar sobre la aparición de nuevas barras en intervalo M1.

Limitar el Número de Órdenes Pendientes

Si necesita limitar el número de órdenes pendientes activas que se pueden poner simultáneamente en una cuenta, puede escribir su propia función personalizada. Llamémosla IsNewOrderAllowed(); comprobará si es posible crear otra orden pendiente. Escribámosla con las reglas del Automated Trading Championship (Campeaonato de Trading Automatizado).

//+------------------------------------------------------------------+
//| Checks if it is allowed to place another order                   |
//+------------------------------------------------------------------+
bool IsNewOrderAllowed()
  {
//--- get the allowed number of pending orders on an account
   int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);

//--- if there is no limitations, return true; you can send an order
   if(max_allowed_orders==0) return(true);

//--- if we pass to this line, then there are limitations; detect how many orders are already active
   int orders=OrdersTotal();

//--- return the result of comparing
   return(orders<max_allowed_orders); 
  }

La función es sencilla: obtiene el número permitido de órdenes de la variable max_allowed_orders, y si su valor no es igual a cero, la comparará con el número de órdenes actual. No obstante, esta función no tiene en cuenta otra posible limitación - el límite en el volumen total permitido de posiciones abiertas y órdenes pendientes con un símbolo específico.

Limitar el Número de Lotes con un Símbolo Específico

Para obtener el tamaño de una posición abierta por un símbolo específico, lo primero que debe hacer es seleccionar una posición usando la función PositionSelect(). Solo después de eso podrá solicitar el volumen de la posición abierta usando la función PositionGetDouble(); devolverá varias propiedades de la posición seleccionada que tienen el tipo doble. Escribamos la función the PostionVolume() para obtener el volumen de posición con un símbolo específico.

//+------------------------------------------------------------------+
//| Returns the size of position by a specific symbol                |
//+------------------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- try to select a positions by a symbol
   bool selected=PositionSelect(symbol);
//--- the position exists
   if(selected)
      //--- return the position volume
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- report about the unsuccessful attempt to select the position
      Print(__FUNCTION__," Failed to execute PositionSelect() for the symbol ",
            symbol," Error ",GetLastError());
      return(-1);
     } 
  }

Antes de hacer una solicitud comercial para crear una orden pendiente con un símbolo, debería comprobar el límite en el volumen total de posiciones abiertas y órdenes pendientes con este símbolo - SYMBOL_VOLUME_LIMIT. Si no hay un límite, entones el volumen de una orden pendiente no podrá exceder el volumen máximo permitido que se puede recibir usando la función SymbolInfoDouble().

double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);

Sin embargo, este enfoque no tiene en cuenta el volumen de las órdenes pendientes actuales con el símbolo especificado. Escribamos una función que calcule este valor:

//+------------------------------------------------------------------+
//| Returns the size of position by a specified symbol               |
//+------------------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- try to select a position by a symbol
   bool selected=PositionSelect(symbol);
//--- the position exist
   if(selected)
      //--- return the position volume
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- return zero if there is no position
      return(0);
     } 
  }

Teniendo en cuenta el volumen de posiciones abiertas y el volumen de órdenes pendientes, la simulación final será así:

//+------------------------------------------------------------------+
//|  Returns maximum allowed volume for an order by a symbol         |
//+------------------------------------------------------------------+
double NewOrderAllowedVolume(string symbol)
  {
   double allowed_volume=0;
//--- get the limitation on the maximum volume of an order
   double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
//--- get the limitation of volume by a symbol
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);

//--- get the volume of open position by a symbol
   double opened_volume=PositionVolume(symbol);
   if(opened_volume>=0)
     {
      //--- if we already used available volume
      if(max_volume-opened_volume<=0)
         return(0);

      //--- volume of the open position doen't exceed max_volume
      double orders_volume_on_symbol=PendingsVolume(symbol);
      allowed_volume=max_volume-opened_volume-orders_volume_on_symbol;
      if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume;
     }
   return(allowed_volume); 
  }

El código entero del Asesor Experto Check_Order_And_Volume_Limits.mq5 que contiene las funciones mencionadas en esta sección se encuentra adjunto a este artículo.

Ejemplo de simulación usando el Expert Advisor Check_Order_And_Volume_Limits en la cuenta de un participante del Automated Trading Championship 2010.

Ejemplo de simulación usando el Asesor Experto Check_Order_And_Volume_Limits en la cuenta de un participante del Automated Trading Championship 2010.

Comprobar la Corrección del Volumen

Una parte significativa de cualquier robot de trading es la capacidad de elegir un volumen correcto para ejecutar una operación de trading. Aquí no vamos a hablar sobre los sistemas de gestión financiera y de riesgos, sino del volumen correcto según las propiedades de un símbolo correspondientes.

Identificador

Descripción

Tipo de propiedad

SYMBOL_VOLUME_MIN

Volumen mínimo para una transacción

doble

SYMBOL_VOLUME_MAX

Volumen máximo para una transacción

doble

SYMBOL_VOLUME_STEP

Volumen mínimo para la ejecución de una transacción

doble


Para realizar tal verificación, podemos escribir la función personalizada CheckVolumeValue():

//+------------------------------------------------------------------+
//|  Check the correctness of volume of an order                     |
//+------------------------------------------------------------------+
bool CheckVolumeValue(double volume,string &description)
  {
//--- Minimum allowed volume for trade operations
   double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f",min_volume);
      return(false);
     }

//--- Maximum allowed volume for trade opertations
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f",max_volume);
      return(false);
     }

//--- get the minimal volume change step
   double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);

   int ratio=(int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>0.0000001)
     {
      description=StringFormat("Volume is not a multiple of minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f",
                               volume_step,ratio*volume_step);
      return(false);
     }
   description="Correct value of volume ";
   return(true); 
  }

Puede comprobar el funcionamiento de esta función usando el script CheckVolumeValue.mq5, adjunto en este artículo.

Mensajes del script CheckVolumeValue.mq5 que comprueban que el volumen es correcto.

Mensajes del script CheckVolumeValue.mq5 que comprueban que el volumen es correcto.

Conclusión

Este artículo describe las verificaciones básicas para posibles limitaciones al trabajar con un Asesor Experto a las que se puede enfrentar al crear su propio sistema de trading automatizado. Estos ejemplos no cubren todas las condiciones posibles que se deberían comprobar durante la ejecución de un Asesor Experto en una cuenta comercial. Pero espero que estos ejemplos les ayuden a los principiantes a entender cómo implementar las verificaciones más populares en el lenguaje MQL5.

Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/22

Aplicar un Indicador a Otro Aplicar un Indicador a Otro

Al escribir un indicador que usa la forma corta de la llamada de función OnCalculate(), puede que no se dé cuenta del hecho de que un indicador se puede calcular no solo por datos de precio, sino también por datos de otro indicador (independientemente de si viene incorporado o es personalizado). ¿Desea mejorar un indicador para su correcta aplicación a los datos del otro indicador? En este artículo, revisaremos todos los pasos para realizar tal modificación.

MQL5: Crea tu propio indicador MQL5: Crea tu propio indicador

¿Qué es un indicador? Se trata de un conjunto de valores calculados y que queremos que se muestren en la pantalla de manera cómoda para nosotros. Los conjuntos de valores se representan en los programas en forma de matrices. De este modo, la creación del indicador consiste en escribir un algoritmo que maneja algunas matrices (matrices de precios) y graba los resultados del procesamiento de otras matrices (valores del indicador). Mediante la descripción de la creación de True Strength Index (Índice de fuerza verdadera), el autor muestra cómo escribir indicadores en MQL5.

La Orden de Creación y Destrucción de Objetos en MQL5 La Orden de Creación y Destrucción de Objetos en MQL5

Cada objeto, ya sea un objeto personalizado, un array dinámico o un array de objetos, se crea y elimina en el programa MQL5 de una forma particular. A menudo, algunos objetos son parte de otros objetos, y el orden de eliminación de objetos durante la desinicialización se vuelve especialmente importante. Este artículo facilita algunos ejemplos que muestran los mecanismos para trabajar con objetos.

Creación de un panel de información mediante las clases de la Librería estándar y Google Chart API Creación de un panel de información mediante las clases de la Librería estándar y Google Chart API

El primer objetivo del lenguaje de programación MQL5 es la creación de sistemas de trading automatizados e instrumentos complejos para el análisis técnico. Pero por otro lado, nos permite crear sistemas de información interesantes para el seguimiento del estado de los mercados, y nos proporciona una conexión de retorno con el trader. El artículo describe los componentes de la Librería estándar de MQL5 y muestra ejemplos sobre su utilización en la práctica para alcanzar estos objetivos. Además, se muestra un ejemplo de uso de Google Chart API para la creación de gráficos.