![Limitaciones y verificaciones en Asesores Expertos](https://c.mql5.com/i/articles/overlay.png)
Limitaciones y verificaciones en Asesores Expertos
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.
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 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 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.
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 Ltd.
Artículo original: https://www.mql5.com/ru/articles/22
![La Orden de Creación y Destrucción de Objetos en MQL5](https://c.mql5.com/2/0/recycle_ava__1.png)
![Aplicar un Indicador a Otro](https://c.mql5.com/2/0/indikators_001.png)
![Creación de un panel de información mediante las clases de la Librería estándar y Google Chart API](https://c.mql5.com/2/0/info_panel_MQL5.png)
![MQL5: Crea tu propio indicador](https://c.mql5.com/2/0/indikator__1.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso