
Creación de un Asesor Experto MQL5 basado en la estrategia PIRANHA utilizando las Bandas de Bollinger
Introducción
En este artículo, exploraremos cómo crear un Asesor Experto (EA) en MetaQuotes Language 5 (MQL5) basado en la estrategia PIRANHA, centrándonos en la integración de las Bandas de Bollinger. A medida que los traders buscan soluciones comerciales automatizadas efectivas, la estrategia PIRANHA ha surgido como un enfoque sistemático que aprovecha las fluctuaciones del mercado, lo que la convierte en una opción atractiva para muchos entusiastas de Forex.
Comenzaremos describiendo los principios fundamentales de la estrategia PIRANHA, proporcionando una base sólida para su implementación en el trading automatizado. A continuación, profundizaremos en las Bandas de Bollinger, un indicador técnico popular que ayuda a identificar posibles puntos de entrada y salida midiendo la volatilidad del mercado.
A continuación, lo guiaremos a través del proceso de codificación en MQL5, destacando las funciones esenciales y la lógica que impulsan la estrategia. Además, analizaremos cómo probar el rendimiento del EA, optimizar los parámetros y las mejores prácticas para implementarlo en un entorno comercial en vivo. Los temas que cubriremos en este artículo incluyen:
- Descripción general de la estrategia PIRANHA
- Comprensión de las Bandas de Bollinger
- Plan estratégico
- Implementación en MQL5
- Pruebas
- Conclusión
Al final de este artículo, usted estará equipado con los conocimientos necesarios para desarrollar un Asesor Experto MQL5 que utilice eficazmente la estrategia PIRANHA y las Bandas de Bollinger, mejorando su enfoque de trading. Empecemos.
Descripción general de la estrategia PIRANHA
La estrategia PIRANHA es un sistema de negociación dinámico que puede sacar partido de los movimientos de los precios en el mercado de divisas. Esta estrategia se define por un tipo de comercio rápido y oportunista, bautizado con el nombre del ágil pez depredador al que puede asemejarse este tipo de pesca por su rapidez y precisión. Conjunto de habilidades: La estrategia PIRANHA es una estrategia basada en la volatilidad desarrollada para ayudar a los operadores a determinar con precisión los puntos de entrada y salida del mercado a través de su enfoque altamente relativo.
Un componente de la estrategia PIRANHA que está habilitado es la aplicación de las Bandas de Bollinger, que son indicadores comunes para ayudar a los operadores a ver la volatilidad en su mercado. Para nuestro método, utilizaremos una Banda de Bollinger de 12 períodos, simplemente porque es una gran herramienta y con el tiempo tiene una topología que nos brinda una buena perspectiva del comportamiento de los precios. También estableceremos una desviación estándar de 2, lo que esencialmente equivale a capturar los principales movimientos de precios y filtrar el ruido de las fluctuaciones menores. Estos canales crean un techo y un suelo, que representan posibles condiciones de sobrecompra o sobreventa en el mercado. Si el precio cae por debajo de la banda inferior, se considera una gran oportunidad de compra, mientras que un aumento por encima de la banda superior indica que podría ser conveniente vender. A continuación se muestra una ilustración:
La gestión de riesgos es otro elemento vital de la estrategia PIRANHA. Se enfatiza la importancia de proteger el capital a través de niveles de stop-loss y take-profit bien definidos. Para nuestra estrategia, colocaremos el stop-loss 100 puntos por debajo del precio de entrada para operaciones de compra y estableceremos un nivel de take-profit 50 puntos por encima de la entrada. Este enfoque disciplinado garantiza que podamos mitigar pérdidas potenciales y al mismo tiempo asegurar ganancias, fomentando una metodología comercial más sostenible.
En resumen, la estrategia PIRANHA combina el análisis técnico con énfasis en la volatilidad y la gestión de riesgos. Al comprender estos principios y configuraciones, los operadores pueden navegar en el mercado Forex de manera más efectiva y tomar decisiones informadas que se alineen con sus objetivos comerciales. A medida que avancemos, exploraremos cómo implementar esta estrategia en MQL5, dando vida a la estrategia PIRANHA en un sistema de trading automatizado.
Comprensión de las Bandas de Bollinger
Los operadores pueden utilizar las Bandas de Bollinger, una sólida herramienta de análisis técnico, para determinar los posibles movimientos de los precios en un mercado volátil. John Bollinger desarrolló el indicador en la década de 1980. Consta de tres componentes: una media móvil, una banda superior y una banda inferior. Los cálculos que realizan permiten a los traders evaluar si un precio está lejos de su promedio en cualquier dirección.
Para comenzar, calculamos la banda media (la media móvil), que suele ser una media móvil simple de 20 períodos, o SMA. La fórmula para la media móvil simple (SMA) es:
Donde 𝑃𝑖 representa el precio de cierre en cada período y 𝑛 es el número de períodos (en este caso, 20). Por ejemplo, si tenemos los siguientes precios de cierre durante los últimos 20 períodos:
Período | Precio de cierre (𝑃𝑖) |
---|---|
1 | 1.1050 |
2 | 1.1070 |
3 | 1.1030 |
4 | 1.1080 |
5 | 1.1040 |
6 | 1.1100 |
7 | 1.1120 |
8 | 1.1150 |
9 | 1.1090 |
10 | 1.1060 |
11 | 1.1085 |
12 | 1.1105 |
13 | 1.1130 |
14 | 1.1110 |
15 | 1.1075 |
16 | 1.1055 |
17 | 1.1080 |
18 | 1.1095 |
19 | 1.1115 |
20 | 1.1120 |
Sumamos estos precios y los dividimos por 20:
A continuación, calculamos la desviación estándar, que mide la dispersión de los precios de cierre con respecto a la SMA. La fórmula para la desviación estándar (𝜎) es:
Utilizando nuestra SMA calculada de 1,1080, calculamos las diferencias al cuadrado para cada precio de cierre, luego tomamos su media y, por último, sacamos la raíz cuadrada. Por ejemplo, las primeras diferencias al cuadrado son:
Después de calcular las 20 diferencias al cuadrado, encontramos:
- Banda superior = SMA + (k × σ)
- Banda inferior = SMA − (k × σ)
Aquí normalmente establecemos k=2 (que representa dos desviaciones estándar). Conectando nuestros valores:
- Banda superior = 1,1080 + (2 × 0,0030) = 1,1140
- Banda inferior = 1,1080 − (2 × 0,0030) = 1,1020
Las Bandas de Bollinger resultantes son las siguientes:
- Banda media (SMA): 1,1080
- Banda superior: 1.1140
- Banda inferior: 1,1020
Estas tres bandas son las que se ilustran a continuación:
El espacio entre estas bandas cambia según las condiciones del mercado. Cuando se amplían, indican una volatilidad creciente, y los mercados en movimiento tienden a ser volátiles. Cuando las bandas se estrechan, sugiere que el mercado se está consolidando. A los traders les gusta buscar interacciones entre los precios y las bandas para generar señales comerciales. Podrían tender a interpretar una interacción de precios con la banda superior como un mercado que está sobrecomprado y una interacción de precios con la banda inferior como un mercado que está sobrevendido.
En resumen, los cálculos de las Bandas de Bollinger implican la inversión de la media móvil simple (SMA), la desviación estándar y las bandas superior e inferior. Comprender estos cálculos no es solo un ejercicio de lectura cuantitativa, sino que proporciona a los traders la munición necesaria para tomar decisiones comerciales informadas, en particular al aplicar la estrategia PIRANHA en sus esfuerzos comerciales.
Plan estratégico
Plano de la banda superior: Condición de venta
Cuando el precio cruza y cierra por encima de la banda superior de Bollinger, indica que el mercado puede estar sobrecomprado. Esta condición sugiere que los precios han subido excesivamente y es probable que experimenten una corrección a la baja. En consecuencia, consideramos este escenario una señal de venta. De esta forma, abrimos una posición de venta cuando el precio de cierre de la barra actual se mantiene por encima de la banda superior. El objetivo es aprovechar una posible reversión o retroceso.
Plano de la banda inferior: Condición de compra
Por el contrario, cuando el precio cruza y cierra por debajo de la Banda de Bollinger inferior, indica que el mercado puede estar sobrevendido. Este escenario sugiere que los precios han caído significativamente y podrían estar a punto de repuntar. Por lo tanto, se considera una señal de compra. Así, abrimos una posición de compra cuando el precio de cierre de la barra actual está por debajo de la banda inferior, anticipando un posible cambio al alza.
Estas representaciones visuales del anteproyecto de la estrategia serán de gran ayuda cuando estemos implementando estas condiciones de trading en MQL5, sirviendo como referencia para codificar reglas precisas de entrada y salida.
Implementación en MQL5
Después de aprender todas las teorías sobre la estrategia de trading Piranha, vamos a automatizar la teoría y crear un Asesor Experto (EA) en MetaQuotes Language 5 (MQL5) para MetaTrader 5.
Para crear un Asesor Experto (EA), en su terminal MetaTrader 5, haga clic en la pestaña Herramientas y marque MetaQuotes Language Editor, o simplemente pulse F4 en su teclado. Alternativamente, puede hacer clic en el icono IDE (Integrated Development Environment) en la barra de herramientas. Esto abrirá el entorno MetaQuotes Language Editor, que permite escribir robots de trading, indicadores técnicos, scripts y librerías de funciones.
Una vez abierto el MetaEditor, en la barra de herramientas, vaya a la pestaña Archivo y marque Nuevo archivo, o simplemente presione CTRL + N, para crear un nuevo documento. Alternativamente, puede hacer clic en el icono Nuevo en la pestaña de herramientas. Esto generará una ventana emergente del Asistente MQL (MQL Wizard).
En el asistente que aparece, marque Asesor experto (plantilla) y haga clic en Siguiente.
En las propiedades generales del Asesor Experto, en la sección nombre, indique el nombre de archivo de su experto. Tenga en cuenta que para especificar o crear una carpeta si no existe, utilice la barra invertida antes del nombre del EA. Por ejemplo, aquí tenemos "Experts\" por defecto. Esto significa que nuestro EA se creará en la carpeta "Experts" y podremos encontrarlo allí. Las demás secciones son bastante sencillas, pero puedes seguir el enlace en la parte inferior del Asistente para saber cómo realizar el proceso con precisión.
Después de proporcionar el nombre de archivo del Asesor Experto deseado, haga clic en Siguiente, luego en Siguiente y, por último, en Finalizar. Después de hacer todo esto, ahora estamos listos para codificar y programar nuestra estrategia.
Primero, comenzamos definiendo algunos metadatos sobre el Asesor Experto (EA). Esto incluye el nombre del EA, la información de derechos de autor y un enlace al sitio web de MetaQuotes. También especificamos la versión del EA, que se establece en "1.00".
//+------------------------------------------------------------------+ //| PIRANHA.mq5 | //| Allan Munene Mutiiria, Forex Algo-Trader. | //| https://forexalgo-trader.com | //+------------------------------------------------------------------+ //--- Properties to define metadata about the Expert Advisor (EA) #property copyright "Allan Munene Mutiiria, Forex Algo-Trader." //--- Copyright information #property link "https://forexalgo-trader.com" //--- Link to the creator's website #property version "1.00" //--- Version number of the EA
Al cargar el programa se realiza una información como la que se muestra a continuación.
En primer lugar, incluimos una instancia de comercio utilizando #include al principio del código fuente. Esto nos da acceso a la clase CTrade, que usaremos para crear un objeto comercial. Esto es crucial porque lo necesitamos para abrir operaciones.
//--- Including the MQL5 trading library #include <Trade/Trade.mqh> //--- Import trading functionalities CTrade obj_Trade; //--- Creating an object of the CTrade class to handle trading operations
El preprocesador sustituirá la línea #include <Trade/Trade.mqh> por el contenido del archivo Trade.mqh. Los corchetes indican que el archivo Trade.mqh se tomará del directorio estándar (normalmente es directorio_de_instalación_del_terminal\MQL5\Include). El directorio actual no está incluido en la búsqueda. La línea se puede colocar en cualquier parte del programa, pero normalmente todas las inclusiones se colocan al comienzo del código fuente, para una mejor estructura del código y una referencia más fácil. La declaración del objeto obj_Trade de la clase CTrade nos dará acceso a los métodos contenidos en dicha clase fácilmente, gracias a los desarrolladores de MQL5.
Necesitaremos crear indicadores de manejo para poder incluir los indicadores necesarios en la estrategia.
//--- Defining variables for Bollinger Bands indicator and price arrays int handleBB = INVALID_HANDLE; //--- Store Bollinger Bands handle; initialized as invalid double bb_upper[], bb_lower[]; //--- Arrays to store upper and lower Bollinger Bands values
Aquí, declaramos e inicializamos una única variable integer, «handleBB», que servirá como el manejador para el indicador de Bandas de Bollinger en nuestro Asesor Experto. En MQL5, un identificador es un identificador único asignado a un indicador, lo que facilita hacer referencia a ese indicador en todo el código. Al establecer «handleBB» como INVALID_HANDLE inicialmente, nos aseguramos de que el programa no hará referencia a un manejador de indicador no válido antes de que se cree correctamente, evitando así errores inesperados. Junto al asa, definimos también dos matrices dinámicas, «bb_upper» y «bb_lower», que almacenarán los valores superior e inferior de las Bandas de Bollinger, respectivamente. Estas matrices nos ayudarán a capturar y analizar el estado actual del indicador, proporcionando una base confiable para ejecutar nuestra estrategia comercial basada en las condiciones de la Banda de Bollinger. Nuevamente, tendremos que asegurarnos de abrir solo una posición en una dirección.
//--- Flags to track if the last trade was a buy or sell bool isPrevTradeBuy = false, isPrevTradeSell = false; //--- Prevent consecutive trades in the same direction
Aquí, declaramos e inicializamos dos banderas booleanas, "isPrevTradeBuy" y "isPrevTradeSell", para realizar un seguimiento de la dirección de la última operación ejecutada. Ambos están configurados inicialmente como falsos, lo que indica que aún no se han realizado operaciones. Estas banderas desempeñarán un papel fundamental en la gestión de nuestra lógica comercial al garantizar que el Asesor Experto no abra operaciones consecutivas en la misma dirección. Por ejemplo, si la operación anterior fue una compra, "isPrevTradeBuy" se establecerá como verdadero, lo que evitará otra operación de compra hasta que se produzca una operación de venta. Este mecanismo ayudará a evitar operaciones redundantes y a mantener una estrategia comercial equilibrada.
A continuación, necesitamos el manejador de eventos OnInit. El manejador de eventos es esencial porque se llama automáticamente cuando el Asesor Experto (EA) se inicializa en un gráfico. Esta función es responsable de configurar el EA, incluida la creación de los indicadores necesarios, la inicialización de variables y la preparación de recursos. En otras palabras, OnInit es una función incorporada que garantiza que todo esté configurado correctamente antes de que el EA comience a procesar datos del mercado. Es como sigue.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ // OnInit is called when the EA is initialized on the chart //... }
En el manejador de eventos OnInit, necesitamos inicializar el controlador del indicador para que se le asignen valores de datos.
//--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2 handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE);
Aquí, creamos el manejador para el indicador de Bandas de Bollinger llamando a la función iBands, que genera el indicador basándose en los parámetros especificados. A esta función le pasamos varios argumentos: _Symbol se refiere al par de divisas que estamos analizando, y _Period denota el marco temporal para el indicador, que puede ser desde minutos hasta horas o días. Los parámetros de las Bandas de Bollinger incluyen un período de 12, que indica el número de barras utilizadas para calcular el indicador, un desplazamiento de 0, que significa que no se aplica ningún ajuste a las bandas, y una desviación estándar de 2, que determina qué tan lejos estarán las bandas del promedio móvil. El uso de PRICE_CLOSE indica que basaremos nuestros cálculos en los precios de cierre de las barras. Una vez que esto se ejecuta con éxito, nuestra variable handle «handleBB» almacenará un identificador válido para el indicador de Bandas de Bollinger, permitiéndonos referenciarlo para la recuperación y análisis de datos. Por lo tanto, debemos verificar si el identificador se creó correctamente antes de continuar.
//--- Check if the Bollinger Bands handle was created successfully if (handleBB == INVALID_HANDLE){ Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING"); //--- Print error if handle creation fails return (INIT_FAILED); //--- Return initialization failed }
Aquí, verificamos si el manejador para el indicador Bandas de Bollinger se creó correctamente comprobando si es igual a INVALID_HANDLE. Si el manejador no es válido, se imprime un mensaje de error que dice «ERROR: UNABLE TO CREATE THE BB HANDLE REVERTING», que ayuda a identificar cualquier problema durante el proceso de inicialización. Entonces devolvemos INIT_FAILED, indicando que el Asesor Experto no pudo inicializarse correctamente. Si esto pasa, continuamos configurando las matrices de datos como series de tiempo.
//--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0) ArraySetAsSeries(bb_upper, true); //--- Set upper band array as series ArraySetAsSeries(bb_lower, true); //--- Set lower band array as series return(INIT_SUCCEEDED); //--- Initialization successful
Aquí, configuramos las matrices para las Bandas de Bollinger, «bb_upper» y «bb_lower», para tratarlas como datos de series temporales llamando a la función ArraySetAsSeries y estableciendo el segundo parámetro en true. De este modo, los datos más recientes se almacenan en el índice 0, lo que facilita el acceso a los últimos valores al analizar las condiciones del mercado. Al organizar las matrices de esta manera, alineamos nuestra estructura de datos con el uso típico en los algoritmos comerciales, donde la información más actual suele ser la más relevante. Finalmente, devolvemos INIT_SUCCEED, indicando que el proceso de inicialización se ha completado con éxito, permitiendo al Asesor Experto proceder con sus operaciones.
Hasta este punto, todo en la sección de inicialización funcionó correctamente. El código fuente completo responsable de la inicialización del programa es el siguiente:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2 handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE); //--- Check if the Bollinger Bands handle was created successfully if (handleBB == INVALID_HANDLE){ Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING"); //--- Print error if handle creation fails return (INIT_FAILED); //--- Return initialization failed } //--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0) ArraySetAsSeries(bb_upper, true); //--- Set upper band array as series ArraySetAsSeries(bb_lower, true); //--- Set lower band array as series return(INIT_SUCCEEDED); //--- Initialization successful }
A continuación, pasamos al manejador de eventos OnDeinit, que es una función que se llama cuando se desinicializa el programa.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ // OnDeinit is called when the EA is removed from the chart or terminated //... }
La función OnDeinit se invoca cuando el Asesor Experto (EA) se elimina del gráfico o cuando el terminal se apaga. Debemos utilizar este controlador de eventos para garantizar el correcto mantenimiento y la gestión de recursos. Cuando finaliza el EA, debemos liberar todos los controladores de los indicadores que creamos en la fase de inicialización. Si no hiciéramos esto, podríamos estar dejando atrás ubicaciones de memoria que usamos, lo que sería ineficiente; ciertamente no queríamos correr el riesgo de dejar atrás recursos que no necesitábamos. Es por esto que OnDeinit es importante y por qué los pasos de limpieza son fundamentales en cualquier entorno de programación.
IndicatorRelease(handleBB); //--- Release the indicator handle
Aquí, simplemente llamamos a la función «IndicatorRelease» con el argumento «handleBB» para liberar el manejador del indicador de Bandas de Bollinger que creamos previamente. La limpieza es crucial para mantener el rendimiento de la plataforma, especialmente si utiliza varios asesores expertos o ejecuta la plataforma durante períodos prolongados. Así, el código fuente completo para la liberación de recursos es el siguiente:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Function to handle cleanup when the EA is removed from the chart IndicatorRelease(handleBB); //--- Release the indicator handle }
A continuación, debemos buscar oportunidades comerciales siempre que haya actualizaciones de precios. Esto se consigue en el manejador de eventos OnTick.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ // OnTick is called whenever there is a new market tick (price update) //... }
La función controladora de eventos, OnTick, ejecuta y procesa información de precios recientes cada vez que hay un nuevo tick o un cambio en las condiciones del mercado. Es una parte esencial del funcionamiento de nuestro Asesor Experto (EA) porque es donde ejecutamos nuestra lógica comercial, cuyas condiciones comerciales están, esperamos, estructuradas para producir operaciones rentables. Cuando los datos del mercado cambian, evaluamos el estado actual del mercado y tomamos decisiones sobre si abrir o cerrar una posición. La función se ejecuta tan a menudo como cambian las condiciones del mercado, lo que garantiza que nuestra estrategia opere en tiempo real y responda a los precios actuales y a los cambios en los valores de nuestros indicadores de mercado.
Para mantenernos actualizados con las condiciones actuales del mercado, necesitamos obtener los valores de las cotizaciones de precios actuales.
//--- Get current Ask and Bid prices double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); //--- Normalize Ask price to correct digits double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); //--- Normalize Bid price to correct digits
Aquí obtenemos los precios de oferta y demanda más actuales para el símbolo negociado. Para obtener estos precios, utilizamos la función SymbolInfoDouble. Para el precio de venta, especificamos SYMBOL_ASK, y para el precio de compra, especificamos SYMBOL_BID. Después de obtener los precios, utilizamos la función NormalizeDouble para redondear los precios al número de decimales definido por _Digits. Este paso es crucial porque garantiza que nuestras operaciones comerciales se realicen utilizando precios estandarizados y precisos. Si no redondeamos los precios, las imprecisiones de punto flotante podrían producir resultados engañosos en los cálculos de precios de operación. Luego copiamos los valores del indicador para utilizarlos en análisis y operaciones comerciales.
//--- Retrieve the most recent Bollinger Bands values (3 data points) if (CopyBuffer(handleBB, UPPER_BAND, 0, 3, bb_upper) < 3){ Print("UNABLE TO GET UPPER BAND REQUESTED DATA. REVERTING NOW!"); //--- Error if data fetch fails return; } if (CopyBuffer(handleBB, LOWER_BAND, 0, 3, bb_lower) < 3){ Print("UNABLE TO GET LOWER BAND REQUESTED DATA. REVERTING NOW!"); //--- Error if data fetch fails return; }
Aquí, utilizamos la función CopyBuffer para recuperar los valores más recientes de las Bandas de Bollinger, concretamente tres puntos de datos para las bandas superior e inferior. La primera llamada a CopyBuffer solicita datos de la banda superior, comenzando en el índice 0, y los almacena en la matriz "bb_upper". Si la función devuelve un valor menor que 3, indica que la recuperación de datos no fue exitosa, y nos solicita que imprimamos un mensaje de error: "UNABLE TO GET UPPER BAND REQUESTED DATA REVERTING NOW!". A continuación, salimos de la función para evitar que se siga ejecutando. Se sigue un proceso similar para la banda inferior, garantizando que también gestionemos cualquier error al recuperar sus datos. Tenga en cuenta que al hacer referencia a los índices de búfer, utilizamos identificadores de líneas de indicador permitidos al copiar valores del indicador de Bandas de Bollinger en lugar de números de búfer. Es la forma más fácil de hacerlo para evitar confusiones, pero la lógica permanece. Aquí se muestra una representación visual de los números de búfer.
Dado que necesitaremos hacer una comparación entre los valores del indicador y los precios, necesitamos obtener los precios de barra que son relevantes para nosotros, en este caso, los precios máximos y mínimos.
//--- Get the low and high prices of the current bar double low0 = iLow(_Symbol, _Period, 0); //--- Lowest price of the current bar double high0 = iHigh(_Symbol, _Period, 0); //--- Highest price of the current bar
Aquí, obtenemos los precios bajo y alto de la barra actual llamando a las funciones iLow e iHigh. La función iLow recupera el precio más bajo de la barra actual (índice 0) para el símbolo (_Symbol) y el marco temporal (_Period) especificados, almacenando este valor en la variable «low0». De manera similar, iHigh obtiene el precio más alto de la barra actual y lo asigna a la variable "high0". Todavía debemos asegurarnos de ejecutar una sola señal en una barra. Aquí está la lógica empleada.
//--- Get the timestamp of the current bar datetime currTimeBar0 = iTime(_Symbol, _Period, 0); //--- Time of the current bar static datetime signalTime = currTimeBar0; //--- Static variable to store the signal time
Aquí, recuperamos la marca de tiempo de la barra actual utilizando la función iTime, que devuelve el tiempo de la barra especificada (índice 0) para el símbolo (_Symbol) y el marco temporal (_Period) dados. Esta marca de tiempo se almacena en la variable «currTimeBar0». Además, declaramos una variable estática llamada «signalTime» y la inicializamos con el valor de «currTimeBar0». Al hacer «signalTime» estático, nos aseguramos de que su valor persiste entre llamadas a funciones, lo que nos permite rastrear la última vez que se generó una señal de negociación. Esto es crucial para nuestra estrategia, ya que nos ayuda a evitar que se activen múltiples señales en la misma barra, asegurándonos de que sólo actuamos sobre una señal por periodo. Después de hacer todo esto, ahora podemos empezar a buscar las señales. Lo primero que hacemos es comprobar si hay una señal de compra.
//--- Check for a buy signal when price crosses below the lower Bollinger Band if (low0 < bb_lower[0]){ Print("BUY SIGNAL @ ", TimeCurrent()); //--- Log the buy signal with the current time }
Aquí, comprobamos una potencial señal de compra evaluando si el precio más bajo de la barra actual, almacenado en la variable «low0», es menor que el valor de la Banda de Bollinger inferior más reciente, que se almacena en el array «bb_lower» en el índice 0. Si «low0» es menor que «bb_lower[0]», indica que el precio ha cruzado por debajo de la banda inferior, lo que sugiere una posible condición de sobreventa y una posible oportunidad de compra. Cuando se cumple esta condición, el programa registra un mensaje utilizando la función Print para mostrar «BUY SIGNAL @» junto con la hora actual, obtenida utilizando la función TimeCurrent. Esta alerta nos ayuda a rastrear cuándo se detectan señales de compra, proporcionando transparencia y trazabilidad para el proceso de toma de decisiones del EA. Cuando ejecutamos esto, obtenemos el siguiente resultado.
A partir de la salida proporcionada, podemos ver que imprimimos las señales en cada tick en que se cumplen las condiciones alcistas. Queremos imprimir la señal una vez por barra en cada instancia en la que se cumplan las condiciones. Para lograr esto, utilizamos la siguiente lógica.
//--- Check for a buy signal when price crosses below the lower Bollinger Band if (low0 < bb_lower[0] && signalTime != currTimeBar0){ Print("BUY SIGNAL @ ", TimeCurrent()); //--- Log the buy signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades }
Aquí, refinamos nuestra condición de señal de compra agregando una verificación adicional para garantizar que no generemos operaciones duplicadas dentro de la misma barra. Inicialmente, sólo verificamos si el precio más bajo de la barra actual, almacenado en la variable «low0», estaba por debajo del valor más reciente de la banda inferior de Bollinger («bb_lower[0]»). Ahora, incorporamos una condición secundaria «signalTime != currTimeBar0«, que garantiza que la marca de tiempo de la barra actual (»currTimeBar0«) es diferente de la última hora de señal registrada (»signalTime«). A continuación, actualizamos «signalTime» para que coincida con «currTimeBar0» para confirmar que sólo consideramos una señal de compra por barra, incluso si el precio cruza por debajo de la banda varias veces. Cuando ejecutamos la actualización, obtenemos el siguiente resultado.
Eso fue un éxito. Ahora podemos ver que imprimimos las señales una vez por barra. Luego podremos continuar tomando acción sobre las señales generadas abriendo posiciones de compra.
if (PositionsTotal() == 0 && !isPrevTradeBuy){ obj_Trade.Buy(0.01, _Symbol, Ask, Ask - 100 * _Point, Ask + 50 * _Point); //--- Open a buy position with predefined parameters isPrevTradeBuy = true; isPrevTradeSell = false; //--- Update trade flags }
Aquí, agregamos condiciones para garantizar que una operación de compra solo se ejecute en circunstancias específicas. En primer lugar, comprobamos si el número total de posiciones abiertas es cero mediante la función PositionsTotal, que garantiza que no hay otras operaciones activas en ese momento. A continuación, verificamos que la última operación ejecutada no fue una compra evaluando «!isPrevTradeBuy». Esto evita órdenes de compra consecutivas y garantiza que nuestro EA no abra una nueva posición de compra si la operación anterior ya fue una compra.
Si se cumplen ambas condiciones, procedemos a abrir una posición de compra utilizando "obj_Trade.Buy". Especificamos el volumen de la orden como «0.01» lotes, con el símbolo de negociación actual (_Symbol) y el precio «Ask». Los niveles de stop loss y take profit se establecen en 100 y 50 puntos por debajo y por encima del precio de venta respectivamente, definiendo nuestras reglas de gestión de riesgos. Después de abrir con éxito una operación de compra, actualizamos los indicadores de operación: "isPrevTradeBuy" se establece en "verdadero" y "isPrevTradeSell" se establece en "falso", lo que indica que la última operación fue una compra y evita otra compra hasta que se active una señal de venta. Para la lógica de venta, se utiliza un enfoque similar al siguiente.
//--- Check for a sell signal when price crosses above the upper Bollinger Band else if (high0 > bb_upper[0] && signalTime != currTimeBar0){ Print("SELL SIGNAL @ ", TimeCurrent()); //--- Log the sell signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades if (PositionsTotal() == 0 && !isPrevTradeSell){ obj_Trade.Sell(0.01, _Symbol, Bid, Bid + 100 * _Point, Bid - 50 * _Point); //--- Open a sell position with predefined parameters isPrevTradeBuy = false; isPrevTradeSell = true; //--- Update trade flags } }
Una vez que compilamos y ejecutamos el programa, obtenemos el siguiente resultado.
Podemos ver que ejecutamos con éxito la posición de compra. Con la implementación completada, hemos integrado la estrategia PIRANHA utilizando Bandas de Bollinger y configurado el programa para responder a señales de compra y venta en función de condiciones definidas. En la siguiente sección, nos centraremos en probar el programa para evaluar su rendimiento y ajustar los parámetros para obtener resultados óptimos.
Pruebas
Después de completar la implementación, el siguiente paso crítico es probar exhaustivamente el Asesor Experto (EA) para evaluar su rendimiento y optimizar sus parámetros. Las pruebas efectivas garantizan que la estrategia se comporte como se espera en diversas condiciones del mercado, minimizando el riesgo de problemas imprevistos durante la negociación. Aquí, utilizaremos el Probador de estrategias de MetaTrader 5 para realizar pruebas retrospectivas y optimización para encontrar los mejores valores de entrada posibles para nuestra estrategia.
Comenzaremos configurando nuestros parámetros de entrada iniciales para los valores de Stop Loss (SL) y Take Profit (TP), que impactan significativamente en la gestión de riesgos de la estrategia. En la implementación original, SL y TP se definieron utilizando valores en puntos fijos. Sin embargo, para darle a la estrategia suficiente espacio para respirar y capturar mejor los movimientos del mercado, modificaremos los parámetros de entrada para que sean más flexibles y optimizados durante las pruebas. Actualicemos el código de la siguiente manera:
//--- INPUTS input int sl_points = 500; input int tp_points = 250; //--- //--- Check for a buy signal when price crosses below the lower Bollinger Band if (low0 < bb_lower[0] && signalTime != currTimeBar0){ Print("BUY SIGNAL @ ", TimeCurrent()); //--- Log the buy signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades if (PositionsTotal() == 0 && !isPrevTradeBuy){ obj_Trade.Buy(0.01, _Symbol, Ask, Ask - sl_points * _Point, Ask + tp_points * _Point); //--- Open a buy position with predefined parameters isPrevTradeBuy = true; isPrevTradeSell = false; //--- Update trade flags } } //--- Check for a sell signal when price crosses above the upper Bollinger Band else if (high0 > bb_upper[0] && signalTime != currTimeBar0){ Print("SELL SIGNAL @ ", TimeCurrent()); //--- Log the sell signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades if (PositionsTotal() == 0 && !isPrevTradeSell){ obj_Trade.Sell(0.01, _Symbol, Bid, Bid + sl_points * _Point, Bid - tp_points * _Point); //--- Open a sell position with predefined parameters isPrevTradeBuy = false; isPrevTradeSell = true; //--- Update trade flags } }
Las entradas nos permiten realizar una optimización dinámica en diferentes símbolos y materias primas comerciales. Una vez que ejecutamos esto, obtenemos el siguiente resultado.
¡Eso fue un éxito! Podemos concluir que el programa funcionó como se esperaba. El fragmento de código fuente final responsable de la creación e implementación de la estrategia Piranha es el siguiente:
//+------------------------------------------------------------------+ //| PIRANHA.mq5 | //| Allan Munene Mutiiria, Forex Algo-Trader. | //| https://forexalgo-trader.com | //+------------------------------------------------------------------+ //--- Properties to define metadata about the Expert Advisor (EA) #property copyright "Allan Munene Mutiiria, Forex Algo-Trader." //--- Copyright information #property link "https://forexalgo-trader.com" //--- Link to the creator's website #property version "1.00" //--- Version number of the EA //--- Including the MQL5 trading library #include <Trade/Trade.mqh> //--- Import trading functionalities CTrade obj_Trade; //--- Creating an object of the CTrade class to handle trading operations input int sl_points = 500; input int tp_points = 250; //--- Defining variables for Bollinger Bands indicator and price arrays int handleBB = INVALID_HANDLE; //--- Store Bollinger Bands handle; initialized as invalid double bb_upper[], bb_lower[]; //--- Arrays to store upper and lower Bollinger Bands values //--- Flags to track if the last trade was a buy or sell bool isPrevTradeBuy = false, isPrevTradeSell = false; //--- Prevent consecutive trades in the same direction //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2 handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE); //--- Check if the Bollinger Bands handle was created successfully if (handleBB == INVALID_HANDLE){ Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING"); //--- Print error if handle creation fails return (INIT_FAILED); //--- Return initialization failed } //--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0) ArraySetAsSeries(bb_upper, true); //--- Set upper band array as series ArraySetAsSeries(bb_lower, true); //--- Set lower band array as series return(INIT_SUCCEEDED); //--- Initialization successful } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Function to handle cleanup when the EA is removed from the chart IndicatorRelease(handleBB); //--- Release the indicator handle } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Retrieve the most recent Bollinger Bands values (3 data points) if (CopyBuffer(handleBB, UPPER_BAND, 0, 3, bb_upper) < 3){ Print("UNABLE TO GET UPPER BAND REQUESTED DATA. REVERTING NOW!"); //--- Error if data fetch fails return; } if (CopyBuffer(handleBB, LOWER_BAND, 0, 3, bb_lower) < 3){ Print("UNABLE TO GET LOWER BAND REQUESTED DATA. REVERTING NOW!"); //--- Error if data fetch fails return; } //--- Get current Ask and Bid prices double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); //--- Normalize Ask price to correct digits double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); //--- Normalize Bid price to correct digits //--- Get the low and high prices of the current bar double low0 = iLow(_Symbol, _Period, 0); //--- Lowest price of the current bar double high0 = iHigh(_Symbol, _Period, 0); //--- Highest price of the current bar //--- Get the timestamp of the current bar datetime currTimeBar0 = iTime(_Symbol, _Period, 0); //--- Time of the current bar static datetime signalTime = currTimeBar0; //--- Static variable to store the signal time //--- Check for a buy signal when price crosses below the lower Bollinger Band if (low0 < bb_lower[0] && signalTime != currTimeBar0){ Print("BUY SIGNAL @ ", TimeCurrent()); //--- Log the buy signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades if (PositionsTotal() == 0 && !isPrevTradeBuy){ obj_Trade.Buy(0.01, _Symbol, Ask, Ask - sl_points * _Point, Ask + tp_points * _Point); //--- Open a buy position with predefined parameters isPrevTradeBuy = true; isPrevTradeSell = false; //--- Update trade flags } } //--- Check for a sell signal when price crosses above the upper Bollinger Band else if (high0 > bb_upper[0] && signalTime != currTimeBar0){ Print("SELL SIGNAL @ ", TimeCurrent()); //--- Log the sell signal with the current time signalTime = currTimeBar0; //--- Update signal time to avoid duplicate trades if (PositionsTotal() == 0 && !isPrevTradeSell){ obj_Trade.Sell(0.01, _Symbol, Bid, Bid + sl_points * _Point, Bid - tp_points * _Point); //--- Open a sell position with predefined parameters isPrevTradeBuy = false; isPrevTradeSell = true; //--- Update trade flags } } } //+------------------------------------------------------------------+
Resultados de la prueba retrospectiva:
Gráfico de la pruebas retrospectiva:
Optimizamos los parámetros de entrada y verificamos el rendimiento de la estrategia con el probador de estrategias durante esta fase de prueba. Los ajustes que realizamos en los valores de Stop Loss y Take Profit le dieron a la estrategia PIRANHA más flexibilidad. Ahora puede manejar las fluctuaciones del mercado. Hemos confirmado que la estrategia funciona según lo previsto y logra resultados favorables cuando la probamos y la optimizamos.
Conclusión
En este artículo, exploramos el desarrollo de un asesor experto en MetaQuotes Language 5 (MQL5) basado en la estrategia PIRANHA, utilizando Bandas de Bollinger para identificar posibles señales de compra y venta. Comenzamos comprendiendo los fundamentos de la estrategia PIRANHA, seguido de una descripción detallada de las Bandas de Bollinger, destacando su papel en la detección de la volatilidad del mercado y la configuración de entradas y salidas comerciales.
A lo largo de la implementación, ilustramos el proceso de codificación paso a paso, configuramos los indicadores e implementamos la lógica comercial. Para garantizar un rendimiento óptimo, ajustamos las entradas críticas y probamos el programa utilizando el Probador de estrategias de MetaTrader 5, validando la eficacia de la estrategia en diversas condiciones de mercado.
Descargo de responsabilidad: La información presentada en este artículo es sólo para fines educativos. Su objetivo es proporcionar información sobre la creación de un Asesor Experto (EA) basado en la estrategia PIRANHA y debería servir como base para desarrollar sistemas más avanzados con mayor optimización y pruebas. Las estrategias y métodos analizados no garantizan ningún resultado comercial y el uso de este contenido es bajo su propio riesgo. Asegúrese siempre de realizar pruebas exhaustivas y considere las posibles condiciones del mercado antes de aplicar cualquier solución de comercio automatizado.
En general, este artículo sirve como guía para automatizar la estrategia PIRANHA y personalizarla para adaptarla a su estilo comercial. Esperamos que proporcione información valiosa y fomente una mayor exploración en la creación de sistemas comerciales sofisticados en MQL5. ¡Feliz codificación y trading exitoso!
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16034





- 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
muchas gracias - artículo muy interesante y condiciones detalladas. Lo tomaré como base para escribir y probar mis robots en caracteres personalizados.
Claro, muy bienvenido.