¡Los proyectos ayudan a crear robots rentables! O eso parece
La creación de un robot comercial comienza siempre por un pequeño archivo, que a su vez va creciendo y llenándose con multitud de funciones adicionales y objetos de usuario. La mayoría de los programadores de MQL5 afronta este problema con la ayuda de archivos de inclusión (MQH). Pero resulta mejor comenzar a escribir cualquier programa para trading directamente en un proyecto: es más rentable en todos los sentidos.
Ventajas de trabajar utilizando un proyecto
Un proyecto es un archivo aparte con la extensión "MQPROJ", en el que se guardan los ajustes del programa, los parámetros de compilación y la información sobre todos los archivos utilizados. Para trabajar cómodamente con un proyecto, se ha pensado una pestaña aparte en el Navegador. En esta, se representan por categorías todos los archivos utilizados: de inclusión, de recursos, de encabezado, etcétera.
De esta forma, un proyecto no supone simplemente un conjunto de archivos y carpetas ubicadas en un directorio aparte, sino también la posibilidad de descomponer un programa complejo en una estructura calculada en la que se perciban las interacciones y todos los datos necesarios se encuentren a mano, concretamente, nos referimos a:
- archivos set con parámetros de entrada para la simulación y optimización,
- códigos fuente de programas OpenCL,
- archivos multimedia con imágenes y sonidos,
- recursos, etcétera.
Por eso, trabajando en un proyecto, usted siempre podrá saber dónde se encuentra cualquier parte de su programa, y también desplazarse por todos los archivos utilizados. Y, naturalmente, los proyectos le permitirán realizar desarrollos de forma conjunta con la ayuda del repositorio incorporadoMQL5 Storage.
Crear un proyecto
Los nuevos proyectos se crean como cualquier programa MQL5 con ayuda del Wizard MQL5. Pulse "Nuevo proyecto", y ejecute después todos los pasos: indique el nombre del programa, añada los parámetros de entrada y establezca los manejadores de eventos utilizados. Una vez el funcionamiento del Wizard MQL5 haya finalizado, se abrirá un archivo MQPROJ para gestionar las propiedades del proyecto.
>
Aquí, usted podrá especificar la versión, indicar la descripción del programa, añadir un icono, y también gestionar las opciones adicionales:
- Maximum optimization — optimización del archivo EX5 ejecutable para su máxima velocidad de ejecución. La desactivación de esta opción acelerará la compilación del código fuente, pero el archivo EX5 obtenido podría funcionar a una velocidad significativamente menor.
- Check floating point dividers — comprobar si los números reales de tipo double y float no son iguales a cero en las operaciones de división. La desactivación de esta opción puede acelerar el funcionamiento, pero debemos hacer esto de forma consciente.
- Use tester optimization cache — por defecto, la opción está activada y el simulador guarda todos los resultados de las pasadas realizadas en la caché de optimización que se usan en los recálculos. En caso necesario, la caché puede ser desactivada con la ayuda de la propiedad tester_no_cache, pero en el proyecto, esto se realiza desmarcando la casilla.
Si el archivo del proyecto está cerrado, siempre podrá abrirlo de nuevo con la ayuda del comando del menú contextual "Propiedades". Para comprender mejor el contenido del archivo MQPROJ, ábralo en formato de texto con la ayuda del comando "Abrir". Esto le ayudará a entender la construcción interna del proyecto.
{ "platform" :"mt5", "program_type":"expert", "copyright" :"Copyright 2019, MetaQuotes Software Corp.", "link" :"https:\/\/www.mql5.com", "version" :"1.00", "description" :"The mean reversion strategy: the price breaks the channel border outwards and reverts back towards the average. The channel is represented by Bollinger Bands. The Expert Advisor enters the market using limit orders, which can only be opened in the trend direction.", "icon" :"Mean Reversion.ico", "optimize" :"1", "fpzerocheck" :"1", "tester_no_cache":"0", "tester_everytick_calculate":"0", "files": [ { "path":".\\Mean Reversion.mq5", "compile":"true", "relative_to_project":"true" }, { "path":"MQL5\\Include\\Trade\\Trade.mqh", "compile":"false", "relative_to_project":"false" }, ....
Reglas comerciales
Vamos a seguir unas reglas clásicas: entraremos en el mercado cuando el precio toque la banda de Bollinger. Se trata de una de las variedades de comercio basadas en el retorno a la media.
Entraremos solo con órdenes límite pendientes. Además, añadiremos una regla adicional: vamos a comerciar solo en la dirección de la tendencia. Esto significa que, si la tendencia es ascendente, colocaremos solo Buy Limit en el borde inferior del canal, y si la tendencia es descendente, colocaremos Sell Limit en el borde superior del mismo.
La dirección de la tendencia se puede determinar de muchas maneras, así que elegiremos la más sencilla: según la ubicación de dos medias móviles una respecto a otra. Si la media móvil rápida (Fast EMA) es superior a la lenta, tendremos una tendencia ascendente. Y, en el caso contrario, descendente.
No obstante, esta regla tan sencilla presenta una desventaja: siempre tendremos una tendencia o bien ascendente, o bien descendente. Y este sistema indicará muchas entradas falsas durante el mercado plano. Por eso, vamos a añadir una última mejora, a saber, solo será posible colocar órdenes pendientes en el caso de que la distancia entre las bandas de Bollinger sea lo suficientemente grande. Lo más adecuado será medir la anchura del canal en magnitudes relativas, y no en puntos. Para ello, tomaremos el indicador ATR, que mide la volatilidad en puntos.
- Si la anchura del canal es inferior a k*ATR, tendremos un mercado plano, por lo que estará prohibido colocar órdenes.
- Si la anchura del canal es superior a k*ATR, colocaremos una orden límite pendiente en el borde del canal en la dirección de la tendencia.
Aquí, k es un cierto coeficiente que debe ser seleccionado.
De esta forma, al crear un proyecto, deberemos indicar 8 parámetros de entrada para determinar las señales comerciales. El asesor comerciará siempre con un lote fijo, cuyo tamaño se determinará con el parámetro InpLot. Asimismo, existe otro parámetro no optimizable de servicio, InpMagicNumber, para que el asesor trabaje solo con sus propias órdenes y posiciones.
//--- Parámetros del canal input int InpBBPeriod =20; // periodo del indicador de Bollinger input double InpBBDeviation=2.0; // desviación de las bandas respecto a la media //-- periodos de EMA para calcular la tendencia input int InpFastEMA =12; // periodo de Fast EMA input int InpSlowEMA =26; // periodo de Slow EMA //-- ATR parameters input int InpATRPeriod =14; // periodo del indicador ATR input double InpATRCoeff =1.0; // coeficiente de ATR para determinar el mercado plano //--- gestión de capital input double InpLot =0.1; // volumen comercial en lotes //--- parámetros de los marcos temporales input ENUM_TIMEFRAMES InpBBTF =PERIOD_M15; // marco temporal en el que se toman los valores del indicador de Bollinger input ENUM_TIMEFRAMES InpMATF =PERIOD_M15; // marco temporal en el que se determina la tendencia //--- identificador del asesor para las transacciones comerciales input long InpMagicNumber=245600; // Magic Number
Para no seleccionar manualmente el marco temporal para determinar la tendencia y la anchura del canal, se han añadido los parámetros de entrada InpBBTF y InpMATF. Esto permitirá buscar los marcos temporales adecuados directamente durante la optimización, evitando así operaciones innecesarias. Es decir, podemos iniciar el asesor en el marco temporal M1, mientras que usa el indicador Bollinger Bands en M15, y una media móvil con M30. En cuanto al indicador ATR, no hemos introducido un parámetro de entrada aparte para el marco temporal, para no aumentar el número de parámetros.
Escribiendo las funciones
Bien, ya hemos creado el proyecto: ahora es el momento de escribir el asesor propiamente dicho. Vamos a mostrar solo las 3 funciones principales encargadas de describir las reglas.
El cálculo de la anchura del canal de Bollinger es sencillo: solo tenemos que copiar los valores de los búferes de indicador.
//+------------------------------------------------------------------+ //| Obtiene los valores de los bordes del canal | //+------------------------------------------------------------------+ bool ChannelBoundsCalculate(double &up, double &low) { //--- obtenemos los valores del indicador Bollinger Bands double bbup_buffer[]; double bblow_buffer[]; if(CopyBuffer(ExtBBHandle, 1, 1, 1, bbup_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bbup_buffer), code=%d", __FILE__, GetLastError()); return(false); } if((CopyBuffer(ExtBBHandle, 2, 1, 1, bblow_buffer)==-1)) { PrintFormat("%s: Failed CopyBuffer(ExtBBHandle,0,1,2,bblow_buffer), code=%d", __FILE__, GetLastError()); return(false); } low=bblow_buffer[0]; up =bbup_buffer[0]; //--- con éxito return(true); }
Determinar el mercado plano tampoco supone un problema. Primero obtenemos los valores de los bordes del canal, después calculamos la anchura y comparamos con el valor del indicador ATR multiplicado por el coeficiente InpATRCoeff.
//+------------------------------------------------------------------+ //| Retorna true si el canal es demasiado estrecho | //| (indica mercado plano) | //+------------------------------------------------------------------+ int IsRange() { //--- obtenemos el valor de ATR en la última barra finalizada double atr_buffer[]; if(CopyBuffer(ExtATRHandle, 0, 1, 1, atr_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } double atr=atr_buffer[0]; //--- obtenemos los bordes del canal if(!ChannelBoundsCalculate(ExtUpChannel, ExtLowChannel)) return(NO_VALUE); ExtChannelRange=ExtUpChannel-ExtLowChannel; //--- si la anchura del canal es inferior al coeficiente ATR*, tendremos mercado plano if(ExtChannelRange<InpATRCoeff*atr) return(true); //--- mercado plano no detectado return(false); }
Como podemos ver por el código, en ciertos casos, se retorna el valor de la macro NO_VALUE, que nos indicará que no se ha logrado calcular cierto parámetro.
#define NO_VALUE INT_MAX // valor no válido al calcular la Señal o Tendencia
La función encargada de determinar la tendencia es la más voluminosa.
//+------------------------------------------------------------------+ //| Retorna 1 para UpTrend o -1 para DownTrend (0 = no hay tendencia)| //+------------------------------------------------------------------+ int TrendCalculate() { //--- primero, comprobamos si tenemos mercado plano int is_range=IsRange(); //--- comprobamos el resultado if(is_range==NO_VALUE) { //--- si la comprobación no ha tenido éxito, salimos antes con el valor "no value" return(NO_VALUE); } //--- durante el mercado plano, no calculamos la tendencia if(is_range==true) // intervalo estrecho, retornamos "flat" return(0); //--- obtenemos el valor de ATR en la última barra finalizada double atr_buffer[]; if(CopyBuffer(ExtBBHandle, 0, 1, 1, atr_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtATRHandle,0,1,2,atr_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- obtenemos el valor de Fast EMA en la última barra finalizada double fastma_buffer[]; if(CopyBuffer(ExtFastMAHandle, 0, 1, 1, fastma_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtFastMAHandle,0,1,2,fastma_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- obtenemos el valor de Slow EMA en la última barra finalizada double slowma_buffer[]; if(CopyBuffer(ExtSlowMAHandle, 0, 1, 1, slowma_buffer)==-1) { PrintFormat("%s: Failed CopyBuffer(ExtSlowMAHandle,0,1,2,slowma_buffer), code=%d", __FILE__, GetLastError()); return(NO_VALUE); } //--- por defecto, la tendencia no está definida int trend=0; //--- si la EMA rápida es superior a la lenta if(fastma_buffer[0]>slowma_buffer[0]) trend=1; // tendencia ascendente (uptrend) //--- si la EMA rápida es inferior a la lenta if(fastma_buffer[0]<slowma_buffer[0]) trend=-1; // tendencia descendente (downtrend) //--- retornamos la dirección de la tendencia return(trend); }
La última función del algoritmo comercial se encarga de determinar una nueva barra. Se ha escrito de forma que calcule la dirección de la tendencia solo cuando aparezca una nueva barra.
//+------------------------------------------------------------------+ //| Checks the emergence of a new bar on the current timeframe, | //| also calculates the trend and the signal | //+------------------------------------------------------------------+ bool IsNewBar(int &trend) { //--- aquí se guarda de forma constante la hora de apertura de la barra actual entre las llamadas de la función static datetime timeopen=0; //--- obtenemos la hora de apertura de la barra actual datetime time=iTime(NULL, InpMATF, 0); //--- si la hora no ha cambiado, significará que la barra no es nueva, así que salimos con el valor false if(time==timeopen) return(false); //--- la barra es nueva, así que tenemos que calcular la dirección de la tendencia trend=TrendCalculate(); //--- si no hemos conseguido la dirección de la tendencia, salimos e intentamos obtenerla en la próxima llamada if(trend==NO_VALUE) return(false); //--- todas las comprobaciones han tenido éxito: la barra es nueva, y también hemos obtenido la dirección de la tendencia timeopen=time; //recordamos la hora de apertura de la barra actual para las siguientes llamadas. //--- return(true); }
Esto permite organizar el funcionamiento del asesor de forma que todas las operaciones comerciales se realicen solo una vez durante toda la barra. Por eso, los resultados de la simulación dependerán del modo de generación de ticks.
El algoritmo comercial entero se muestra en el manejador OnTick():
- Primero se determina la aparición de una nueva barra y la dirección de la tendencia.
- Si no hay tendencia o se ha abierto una posición, se realizará un intento de eliminar las órdenes pendientes y salir del manejador.
- Si existe una tendencia direccional y no se han colocado órdenes pendientes, se realizará un intento de colocar una orden límite en el borde del canal.
- Si se ha colocado una orden, y esta no ha sido modificada todavía en la nueva barra, se realizará un intento de desplazarla al siguiente borde del canal.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { static bool order_sent =false; // no se ha logrado colocar una orden límite en la barra actual static bool order_deleted =false; // no se ha logrado eliminar la orden límite en la barra actual static bool order_modified=false; // no se ha logrado modificar la orden límite en la barra actual //--- si los parámetros de entrada no son válidos, interrumpimos la simulación en el primer tick if(!ExtInputsValidated) TesterStop(); //--- comprobamos la aparición de una nueva barra y la dirección de la tendencia if(IsNewBar(ExtTrend)) { //--- redefinimos los valores de las variables estáticas a su estado original order_sent =false; order_deleted =false; order_modified=false; } //--- creamos las variables auxiliares para que las comprobaciones sean llamadas solo una vez en la barra actual bool order_exist =OrderExist(); bool trend_detected=TrendDetected(ExtTrend); //--- si no hay tendencia o hay una posición abierta, eliminamos las órdenes pendientes if(!trend_detected || PositionExist()) if(!order_deleted) { order_deleted=DeleteLimitOrders(); //--- si las órdenes se han eliminado con éxito, no será necesario realizar ninguna operación más en la barra actual if(order_deleted) { //--- prohibimos la colocación y modificación de órdenes order_sent =true; order_modified=true; return; } } //--- hay tendencia if(trend_detected) { //--- colocamos una orden en el borde del canal, si todavía no la hay if(!order_exist && !order_sent) { order_sent=SendLimitOrder(ExtTrend); if(order_sent) order_modified=true; } //--- intentamos desplazar la orden al borde del canal, si todavía no se ha hecho esto en la barra actual if(order_exist && !order_modified) order_modified=ModifyLimitOrder(ExtTrend); } //--- }
Las demás funciones comerciales del asesor son estándar, por lo que no serán descritas. Los códigos fuente del proyecto están incluidos en el paquete estándar del terminal MetaTrader 5 y se encuentran el directorio MQL5\Experts\Examples.
Optimizando parámetros y añadiendo archivos Set
Después de escribir el asesor, deberemos encontrar los valores óptimos para los parámetros de entrada en el simulador de estrategias. Pocos saben que el simulador permite copiar fácilmente los valores de las pestañas "Ajustes" y "Parámetros" en el portapapeles con la ayuda de la combinación estándar Ctr+C. Esto le permitirá transmitir rápidamente sus ajustes a otra persona, por ejemplo, a un cliente, a través del chat de freelance, sin necesidad de guardarlos en un archivo set. El cliente podrá copiar estos datos en el portapapeles y pegarlos en la pestaña "Ajustes" del simulador con la ayuda de la operación inversa Ctr+V.
Naturalmente, el guardado de ajustes en un archivo set sigue funcionando, y muchos vendedores en el Mercado acompañan sus asesores con estos archivos para mayor comodidad de sus clientes, que pueden cargar de inmediato los conjuntos correctos de parámetros para la simulación y optimización en el instrumento necesario. Para cada instrumento, es necesario crear un archivo set aparte, y si hay muchos asesores y archivos de este tipo, resulta fácil confundirese. Los proyectos permiten a los compradores de sus robots trabajar solo con sus propios conjuntos de parámetros de entrada, evitando tener que buscar estos en el disco al cambiar de símbolo.
Aquí tenemos un ejemplo de cómo los proyectos permiten añadir los conjuntos de parámetros necesarios directamente al archivo EX5 del asesor. Seleccione el instrumento en el que se va a realizar la optimización, por ejemplo, EURUSD. Establezca los valores Start, Step y Stop para los parámetros a optimizar e inicie la optimización. Una vez haya finalizado, en la pestaña "Optimización", clique dos veces en la que usted considere la mejor pasada: los valores de los parámetros de entrada de esta pasada se colocarán en la pestaña "Parámetros", iniciándose a continuación una simulación individual. Ahora, usted podrá guardar los parámetros encontrados en un archivo set, pero no tendrá que transferirlo por separado. Guarde el conjunto de parámetros, digamos, con el nombre EURUSD.set: esto significará que los parámetros deberán aplicarse precisamente a este símbolo, y no a GBPJPY.
Realice esta operación para cada símbolo en el que pueda trabajar este asesor. De esta forma, usted tendrá un conjunto de, digamos, 9 archivos set. Ahora, solo tendrá que añadir estos archivos a su proyecto: cree la carpeta "Settings and files\Set" correspondiente, para que queden guardados aparte de los códigos fuente. Los proyectos le permitirán mantener el orden con la ayuda de una estructura de archivos correcta.
Ahora, compile el proyecto y abra el simulador de estrategias con el asesor MeanReversion. En el menú contextual de la pestaña Parámetros, aparecerá el nuevo punto "Cargar desde un asesor", donde se encontrarán todas las variables de su conjunto de archivos set.
De esta forma, el archivo EX5 compilado del experto ha resultado un producto completamente finalizado: en él se encuentran los conjuntos de parámetros preparados, para poder comprobar la estrategia sin tener que pensar en los límites y el salto de cambio para cada símbolo. Los usuarios y compradores de sus asesores comerciales valorarán esta comodidad.
Comprobamos estrategias usando datos reales
El asesor MeanReversion fue lanzado en septiembre de 2019 en una cuenta demo para comprobar sus posibles errores programáticos y comerciales en tiempo real. En esta caso, además, el asesor comenzó en el modo de portafolio, comerciando en multitud de símbolos, como se pensó durante la optimización. Asimismo, se alquiló un VPS incorporado, creando para el monitoreo online la señal privadaMany MeanReversion Optimized.
Tras solo un mes de trabajo, el asesor mostró resultados positivos; después, tuvo pérdidas durante 5 meses. Gracias a que el hosting virtual incluía la prolongación automática de contrato, el asesor iniciado no molestaba a nadie, manteniendo un rumbo decidido hacia la pérdida total del depósito. Sin embargo, en marzo sucedieron ciertos cambios en el mercado de divisas, y el asesor mostró de repente un beneficio récord. Los 2 meses siguientes, los resultados fueron contradictorios, posiblemente no volvamos a ver un crecimiento semejante.
El análisis de las transacciones y los resultados desde el punto de vista de los símbolos, muestra que las tres parejas del yen y AUDUSD no son rentables. El asesor no ha mostrado resultados impresionantes, pero incluso con una lógica comercial tan sencilla, ha podido aguantar 9 meses gracias al trabajo en el modo de portafolio, cuando las pérdidas en unos instrumentos se ven compensadas por el beneficio en otros.
Debemos destacar que, desde el momento en que el asesor comenzó a comerciar, sus parámetros de entrada no se modificaron nunca, ni se realizó una sola vez la migración. Es decir, un asesor compilado hace 9 meses comenzó a comerciar en 8 gráficos en un VPS incorporado y ha funcionado hasta ahora sin intervención alguna. Ahora, ya no podemos recordar por qué de los 9 conjuntos set solo se tomaron 8 para el trabajo, al igual que los parámetros que fueron utilizados. Sin embargo, el proyecto de asesor de prueba MeanReversion, creado con fines educativos, sigue activo, mostrando beneficios a día 10 junio de 2020.
Comience a utilizar proyectos: ¡es rentable!
Los proyectos permiten crear programas de cualquier nivel de complejidad y realizar desarrollos conjuntos. El trabajo en equipo con compañeros afines agiliza el desarrollo de aplicaciones, permite intercambiar ideas y habilidades útiles, y también aumenta la calidad del código.
Las reglas comerciales de este asesor son muy sencillas, pero se pueden utilizar como plantilla para crear multitud de robots comerciales distintos. Bastará con cambiar las funciones que determinan la dirección de la tendencia, el estado del mercado plano, los niveles y los métodos de entrada: por ejemplo, no usar órdenes límite, sino de mercado. Posiblemente, resultaría más exitoso comerciar solo en los momentos de mercado plano, ya que no hay función de trailing stop, ni hay ajuste aparte para gestionar los niveles de StopLoss y TakeProfit. En resumen, hay mucho margen para investigar.
Con la ayuda del asesor MeanReversion del paquete estándar MetaTrader 5, usted podrá estudiar y valorar todas las ventajas del trabajo con proyectos. Solo tiene que crearlo o copiarlo en una nueva carpeta, y comenzar experimentar. ¡Use proyectos, es cómodo y rentable en muchos aspectos!
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7863
- 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