
Implementación de un algoritmo de trading de negociación rápida utilizando SAR Parabólico (Stop and Reverse, SAR) y Media Móvil Simple (Simple Moving Average, SMA) en MQL5
Introducción
Este artículo lo guiará a través de los procesos vitales de prueba y optimización de nuestro algoritmo comercial. Utilizaremos el Probador de Estrategias de MQL5 para comparar nuestro EA con datos históricos. En términos sencillos, comprobaremos el rendimiento de nuestro algoritmo en el pasado en comparación con las condiciones reales del mercado. A medida que analicemos el rendimiento de nuestro EA, también explicaremos cómo interpretar varias métricas de rendimiento (por ejemplo, el factor de beneficio, la reducción, la tasa de ganancias, etc.) y lo que nos dicen sobre la fiabilidad y la rentabilidad de nuestro algoritmo. Al final de este artículo, tendremos una idea mucho más clara de cómo aplicar una estrategia de negociación rápida y de lo que se necesita para garantizar su éxito. Los siguientes temas serán efectivos a lo largo del artículo:
- Introducción a la estrategia de negociación rápida
- Comprender los indicadores: SAR Parabólico y SMA
- Implementación en MQL5
- Pruebas y optimización
- Conclusión
Una estrategia de trading rápido se centra en sacar provecho de los movimientos rápidos y frecuentes del mercado ejecutando múltiples operaciones en un período de tiempo corto, a menudo manteniendo posiciones durante menos de una hora. Este enfoque contrasta con las estrategias tradicionales que se centran en las tendencias a largo plazo y que, en cambio, buscan explotar pequeños cambios de precios en valores que se mueven rápidamente. El éxito de esta estrategia depende de la capacidad de procesar y responder a los datos del mercado casi instantáneamente, lo que hace que los sistemas automatizados, como los asesores expertos, sean cruciales para ejecutar operaciones de manera eficiente.
La clave de la eficacia de esta estrategia es el uso de indicadores técnicos como el SAR Parabólico y la Media Móvil Simple, que ayudan a identificar los cambios de tendencia y a suavizar los datos de precios. Estos indicadores, integrados en un algoritmo de negociación de alta frecuencia, permiten a la estrategia adaptarse rápidamente a los cambios del mercado, manteniendo la precisión en la generación de señales y la ejecución de las operaciones. Cuando se aplica correctamente, la negociación rápida puede producir beneficios rápidos, aunque requiere una gestión cuidadosa de los costes y los riesgos.
Comprender los indicadores: SAR Parabólico y SMA
Una estrategia de negociación rápida eficaz requiere comprender los indicadores técnicos importantes que dirigen las decisiones de negociación. Dos guías útiles en este sentido son el SAR Parabólico (Stop and Reverse, SAR) y la Media Móvil Simple (SMA). La SMA es uno de los indicadores de seguimiento de tendencias más antiguos y más utilizados. El SAR Parabólico es relativamente nuevo en comparación, pero ciertamente no es una herramienta menos conocida; ambos indicadores son muy útiles para determinar las condiciones del mercado y señalar posibles oportunidades de trading.<
Diseñado para seguir tendencias, el indicador SAR Parabólico tiene como objetivo encontrar e identificar posibles retrocesos en la dirección de los precios. Funciona trazando una serie de puntos por encima o por debajo del precio, en relación con el punto en el que se encuentra el precio sobre el SAR. Si incorporamos el SAR Parabólico a nuestra estrategia de trading, podemos interpretarlo en relación con la serie de puntos para decidir comprar, vender o tomar una posición corta en el mercado. Si el precio está por encima de los puntos SAR, estamos en una tendencia alcista; si el precio está por debajo de los puntos SAR, estamos en una tendencia bajista. Deben utilizarse los ajustes predeterminados de los indicadores. Esto es como se ilustra a continuación:
Configuración del indicador SAR Parabólico:
Otra parte esencial de nuestra estrategia de trading es la Media Móvil Simple (Simple Moving Average, SMA). El SMA toma los datos de precios de un periodo determinado y los «suaviza». Nos da una visión mucho más fácil de cuál es la tendencia general. También podemos utilizar la SMA para obtener una lectura aún más sencilla de la tendencia. Si la SMA está inclinada hacia arriba, entonces podríamos decir, en términos muy simples, que el mercado está en una tendencia alcista. Si la SMA está inclinada hacia abajo, podríamos decir que el mercado está en una tendencia bajista. La SMA es un filtro de tendencia. Nos indica si debemos buscar operaciones largas (tendencia alcista), cortas (tendencia bajista) o ninguna operación (cuando el precio ronda la línea plana de la SMA). Se utilizará una SMA de 60 períodos. Esto es como se ilustra a continuación:
Configuración del indicador SMA:
Cuando se utilizan conjuntamente, el SAR Parabólico y la Media Móvil Simple ofrecen una imagen completa de las condiciones del mercado. El SAR se puede leer para obtener señales inmediatas sobre lo que podría estar sucediendo con la tendencia actual. Puede, y de hecho lo hace con bastante frecuencia, señalar cambios de tendencia antes de que ocurran. La SMA analiza más datos durante un período más largo y, por lo tanto, confirma la dirección de la tendencia con más certeza. Combinados los tenemos como se muestra a continuación:
Con la descripción general de la estrategia proporcionada, diseñemos el Asesor Experto de la estrategia en MQL5.
Implementación en MQL5
Después de aprender todas las teorías sobre la estrategia de trading rapida, vamos a automatizar la teoría y crear un Asesor Experto (Expert Advisor, EA) en MetaQuotes Language 5 (MQL5) para MetaTrader 5 (MT5).
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. También puede hacer clic en el icono Entorno de Desarrollo Integrado (Integrated Development Environment, IDE) de la barra de herramientas. Esto abrirá el entorno MetaQuotes Language Editor (MetaEditor), que permite escribir robots comerciales, indicadores técnicos, scripts y bibliotecas 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 de nombre, proporcione 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 "Expertos\" por defecto. Esto significa que nuestro EA se creará en la carpeta Expertos 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".
//+------------------------------------------------------------------+ //| #1. RAPID FIRE.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." // Set the copyright property for the Expert Advisor #property link "https://www.mql5.com" // Set the link property for the Expert Advisor #property version "1.00" // Set the version of the Expert Advisor
Al cargar el programa se realiza una información como la que se muestra a continuación.
Primero, 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.
#include <Trade/Trade.mqh> // Include the MQL5 standard library for trading operations CTrade obj_Trade; // Create an instance of the CTrade class to handle trade operations
El preprocesador sustituirá la línea #include <Trade/Trade.mqh> por el contenido del archivo Trade.mqh. Los corchetes angulares indican que el archivo "Trade.mqh" se tomará del directorio estándar (normalmente se encuentra en 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.
int handleSMA = INVALID_HANDLE, handleSAR = INVALID_HANDLE; // Initialize handles for SMA and SAR indicators
Aquí, primero declaramos e inicializamos dos variables integer, «handleSMA» y «handleSAR». Estos servirán como identificadores para dos de los indicadores técnicos que planeamos utilizar en nuestro Asesor Experto. En MQL5, un identificador es un identificador único asignado a un indicador cuando lo creamos utilizando funciones específicas de MQL5. Utilizaremos los identificadores para hacer referencia a los indicadores no pintados en cualquier parte del programa. También hemos asignado ambos identificadores a una constante INVALID_HANDLE, que asegura que el programa no hará referencia a un handle inválido si nos da pereza y nos olvidamos de comprobar la lógica de flujo del programa una vez compilado. Ambos indicadores harán su trabajo sin problemas si los manejamos correctamente y evitamos referenciar un manejador inválido o hacer algo estúpido sin comprobar primero los manejadores.
A continuación, necesitaremos crear arrays donde almacenaremos los valores o datos que recuperemos de los indicadores.
double sma_data[], sar_data[]; // Arrays to store SMA and SAR indicator data
Aquí definimos dos arrays dinámicos, «sma_data» y «sar_data», a los que asignaremos los puntos de datos generados por los indicadores SMA (Media Móvil Simple) y SAR (SAR Parabólico). Asignamos los valores más recientes calculados por cada indicador a estas matrices, dejándolas libres para referenciar y analizar los datos al generar señales comerciales. Las señales comerciales se pueden generar a partir de un cruce de líneas de señal. Para generar una señal de trading con la tendencia actual o una reversión de la tendencia actual, podemos utilizar los valores pasados almacenados en estas matrices. Al hacer uso de estos valores pasados, deberíamos poder interpretar con mayor precisión nuestros indicadores y la acción del precio del activo que se está negociando.
A continuación, necesitamos el manejador de eventos OnInit. El manejador 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é correctamente configurado antes de que el EA comience a procesar los 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 los manejadores de los indicadores para que se les asignen valores de datos.
handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE); // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe
Aquí, utilizamos la función iMA para crear un asa para el indicador de Media Móvil Simple (SMA). El manejador nos dará acceso a los valores de la SMA para cualquier símbolo de negociación y marco temporal que especifiquemos. Aquí, lo configuramos para el símbolo de negociación actual (_Symbol) en un gráfico de 1 minuto (PERIOD_M1), durante un período de 60 barras. El parámetro «0» indica que no hay desplazamiento en el cálculo de la SMA, mientras que la demanda para calcular el modo se especifica con la constante MODE_SMA; por lo tanto, estaremos calculando una media móvil simple. Por último, especificamos el tipo de precio PRICE_CLOSE, lo que significa que el indicador se basará en los precios de cierre de cada barra. Almacenamos el identificador resultante en la variable "handleSMA". A continuación, creamos el manejador para el indicador SAR Parabólico..
handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2); // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe
Del mismo modo, creamos un manejador para el indicador SAR Parabólico (SAR) utilizando la función iSAR. La función iSAR devuelve los valores del SAR Parabólico para un símbolo de negociación y un marco temporal específicos. En nuestro caso, lo utilizamos para el símbolo de negociación actual (_Symbol) en el gráfico de 1 minuto (PERIOD_M1). Los parámetros 0.02 y 0.2 definen el paso y el valor máximo del SAR, lo que significa que controlan lo sensible que será el indicador a los movimientos del precio. El manejador resultante nos permitirá acceder a los datos del SAR que serán fundamentales para nuestra primera estrategia de negociación, que se basa en las tendencias del mercado y los retrocesos de los precios.
Por defecto, el indicador comienza en 10 y avanza con un intervalo de 1. Así que si imprimimos los valores de los indicadores, deberíamos tener 10 y 11 ya que tenemos dos manejadores. Para visualizar sus valores, registrémoslos.
Print("SMA Handle = ",handleSMA); Print("SAR Handle = ",handleSAR);
Los valores que obtenemos son los siguientes:
Ahora registramos los valores correctamente. A continuación, asegurémonos de que los indicadores no estén vacíos.
// Check if the handles for either the SMA or SAR are invalid (indicating failure) if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){ Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW"); // Print error message in case of failure return (INIT_FAILED); // Return failure code, stopping the EA from running }
En esta sección, configuramos un mecanismo para detectar errores críticos que ocurren durante la creación del indicador SMA y SAR. Si estos controladores no se crean correctamente, el Asesor Experto no podrá funcionar correctamente. Así, comprobamos si «handleSMA» o «handleSAR» ha quedado igual a INVALID_HANDLE, lo que indicaría una inicialización fallida de cualquiera de estos dos indicadores. Si encontramos que alguno de estos manejadores no es válido, imprimimos un mensaje de error ("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW.") y devolver el valor INIT_FAILED de la función de inicialización. Estas acciones en conjunto conforman una rutina de gestión de errores elegante y garantizan que el Asesor Experto pueda evitar decisiones de trading arriesgadas. A continuación, tenemos que establecer las matrices de almacenamiento como series temporales. Esto se consigue mediante el siguiente fragmento de código:
// Configure the SMA and SAR data arrays to work as series, with the newest data at index 0 ArraySetAsSeries(sma_data,true); ArraySetAsSeries(sar_data,true);
Configuramos nuestras matrices de almacenamiento para que funcionen como series temporales de datos, con la información más reciente situada en el índice 0. Esto lo conseguimos utilizando la función ArraySetAsSeries y las dos matrices («sma_data» y «sar_data») como primer argumento, con true como segundo argumento para asegurar que las matrices se tratan como series. Se trata de un paso necesario para acceder a los últimos valores del indicador en los momentos de decisión ordinarios de la lógica de negociación, cuando los datos representados por el indicador se habrían escupido a la rutina de toma de decisiones de negociación. La siguiente parte de la lógica hace referencia a una serie de valores actuales y pasados de los dos indicadores. Finalmente, si llegamos a este punto, significa que todo se ha inicializado correctamente y por lo tanto devolvemos una instancia de inicialización exitosa.
return(INIT_SUCCEEDED); // Return success code to indicate successful initialization
Hasta este punto, todo en la sección de inicialización funcionaba correctamente. El código fuente completo responsable de la inicialización del programa es el siguiente:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ // OnInit is called when the EA is initialized on the chart handleSMA = iMA(_Symbol,PERIOD_M1,60,0,MODE_SMA,PRICE_CLOSE); // Create an SMA (Simple Moving Average) indicator handle for the M1 timeframe handleSAR = iSAR(_Symbol,PERIOD_M1,0.02,0.2); // Create a SAR (Parabolic SAR) indicator handle for the M1 timeframe Print("SMA Handle = ",handleSMA); Print("SAR Handle = ",handleSAR); // Check if the handles for either the SMA or SAR are invalid (indicating failure) if (handleSMA == INVALID_HANDLE || handleSAR == INVALID_HANDLE){ Print("ERROR: FAILED TO CREATE SMA/SAR HANDLE. REVERTING NOW"); // Print error message in case of failure return (INIT_FAILED); // Return failure code, stopping the EA from running } // Configure the SMA and SAR data arrays to work as series, with the newest data at index 0 ArraySetAsSeries(sma_data,true); ArraySetAsSeries(sar_data,true); return(INIT_SUCCEEDED); // Return success code to indicate successful initialization }
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. Tenemos que utilizar este manejador de eventos para asegurar un correcto mantenimiento y gestión de los recursos. Cuando el EA finaliza, debemos liberar los manejadores de los indicadores que creamos en la fase de inicialización. Si no lo hiciéramos, podríamos estar dejando atrás posiciones de memoria que utilizábamos, lo que sería ineficiente; desde luego, no queríamos arriesgarnos a dejar atrás ningún recurso que no necesitáramos. Esta es la razón por la que OnDeinit es importante y por la que los pasos de limpieza son críticos en cualquier entorno de programación.
// Release the indicator handles to free up resources IndicatorRelease(handleSMA); IndicatorRelease(handleSAR);
Limpiamos todos los recursos que reservamos para su uso. Para realizar esta limpieza, utilizamos la función IndicatorRelease, que llamamos por separado para cada manejador que asignamos e inicializamos, igual que los guardamos cuando empezamos a utilizarlos. Así nos aseguramos de liberar los recursos en el orden inverso al de su asignación. Aquí, nos fijamos específicamente en los indicadores SMA y SAR. La limpieza es crucial para mantener el rendimiento de la plataforma, especialmente si está utilizando múltiples Asesores Expertos o ejecutando la plataforma durante períodos prolongados. Así, el código fuente completo de la liberación de recursos es el siguiente:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ // OnDeinit is called when the EA is removed from the chart or terminated // Release the indicator handles to free up resources IndicatorRelease(handleSMA); IndicatorRelease(handleSAR); }
A continuación, debemos buscar oportunidades de negociación siempre que se produzcan actualizaciones de los 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 manejadora de eventos, OnTick, ejecuta y procesa información reciente sobre precios 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), ya que es donde ejecutamos nuestra lógica de negociación, cuyas condiciones de negociación son, con suerte, 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, garantizando 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 estar al día de las condiciones actuales del mercado, necesitamos obtener los valores de las cotizaciones actuales.
double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // Get and normalize the Ask price double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // Get and normalize the Bid price
Aquí se obtienen los precios de compra y venta más actuales del símbolo negociado. Para obtener estos precios, utilizamos la función SymbolInfoDouble. Para el precio Ask, especificamos SYMBOL_ASK, y para el precio Bid, 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 last 3 values of the SMA indicator into the sma_data array if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){ Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING"); // Print error if insufficient SMA data return; // Exit the function if not enough data is available }
Utilizamos la función CopyBuffer para recuperar los tres valores más recientes del indicador media móvil simple. La función CopyBuffer toma como entrada el manejador del indicador SMA, el índice del buffer de datos SMA, la posición inicial (en este caso, el valor más reciente), el número de valores a recuperar (tres) y el array para almacenar los datos (sma_data). Después de haber copiado los valores de SMA en la matriz, verificamos si se copiaron correctamente. Si hay un error en este punto, imprimimos un mensaje de error en el registro y salimos de la función para evitar continuar con nuestra lógica comercial basada en datos potencialmente defectuosos o incompletos.
Del mismo modo, utilizamos la función CopyBuffer para recuperar los datos del indicador SAR. Se emplea el siguiente fragmento de código.
// Retrieve the last 3 values of the SAR indicator into the sar_data array if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){ Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING"); // Print error if insufficient SAR data return; // Exit the function if not enough data is available }
Todo lo que necesitas saber ahora es el índice inicial de la barra desde la que almacenar los datos y el índice del buffer. Visualicémoslos como se muestra a continuación:
Imprimamos sus datos en el diario para ver si obtenemos correctamente sus datos.
ArrayPrint(sma_data,_Digits," , "); ArrayPrint(sar_data,_Digits," , ");
Utilizamos la función ArrayPrint para mostrar el contenido de las matrices «sma_data» y «sar_data». ArrayPrint es una función básica que imprime el contenido de un array en el terminal. Sirve para preparar visualizaciones sencillas de los datos con los que se trabaja. Para utilizar la función, debes pasarle tres piezas de información:
- La matriz que desea imprimir ("sma_data" o "sar_data").
- La precisión que desea para los valores mostrados (_Digits), determina cuántos decimales se mostrarán.
- El delimitador debe utilizarse entre los valores impresos (", ") en la salida.
Al imprimir el contenido de las matrices SMA y SAR, podemos realizar un seguimiento de los números reales que nuestro algoritmo está utilizando, lo cual es útil para fines de depuración. También podemos comprobar estos datos en un backtest para asegurarnos de que efectivamente obtenemos los datos necesarios.
Desde la imagen, podemos ver que los datos se recuperan y almacenan correctamente. Los datos en color rojo representan los datos de media móvil mientras que los datos en color azul representan los datos SAR. Desde la cruz, hacemos referencia a la segunda barra y al índice 1. Ahora está claro que obtenemos 3 valores de datos recuperados, tal como se solicitó. Podemos proceder a obtener los valores de barra necesarios.
// Get the low prices for the current and previous bars on the M1 timeframe double low0 = iLow(_Symbol,PERIOD_M1,0); // Low of the current bar double low1 = iLow(_Symbol,PERIOD_M1,1); // Low of the previous bar
Aquí, extraemos los precios bajos de las barras actuales y anteriores en el marco temporal de 1 minuto utilizando la función iLow. El primer parámetro, _Symbol, se ajusta automáticamente al instrumento de negociación para el que está operando el Asesor Experto (por ejemplo, EURUSD). El segundo parámetro, PERIOD_M1, indica al sistema que estamos trabajando en el gráfico de 1 minuto; esto es vital porque la estrategia de disparo rápido se basa en realizar operaciones a corto plazo basadas en movimientos rápidos del precio. El tercer parámetro, 0, en «iLow(_Symbol, PERIOD_M1, 0)», indica que queremos el precio bajo de la barra actual que está en proceso de formación. Del mismo modo, en «iLow(_Symbol, PERIOD_M1, 1)», el 1 indica que queremos el precio mínimo de la barra anterior completada. Almacenamos los valores recuperados en las variables de tipo de datos «double» «low0» y «low1» respectivamente.
De manera similar, para recuperar los precios altos de las barras actuales y anteriores, se adopta una lógica similar.
// Get the high prices for the current and previous bars on the M1 timeframe double high0 = iHigh(_Symbol,PERIOD_M1,0); // High of the current bar double high1 = iHigh(_Symbol,PERIOD_M1,1); // High of the previous bar
Aquí, lo único que cambia es la función utilizada, iHigh, que recupera los datos máximos para el índice de barra y el marco temporal especificados. Para confirmar que los datos que obtenemos son correctos, vamos a imprimirlos de nuevo utilizando la función Print.
Print("High bar 0 = ",high0,", High bar 1 = ",high1); Print("Low bar 0 = ",low0,", Low bar 1 = ",low1);
La confirmación de los datos es la siguiente:
Eso fue un éxito. Ahora podemos pasar a elaborar la lógica comercial. Sin embargo, no necesitamos ejecutar la lógica comercial en cada tick. Por lo tanto, diseñemos un mecanismo para garantizar que solo comerciemos una vez por barra.
// Define a static variable to track the last time a signal was generated static datetime signalTime = 0; datetime currTime0 = iTime(_Symbol,PERIOD_M1,0); // Get the time of the current bar
En esta sección, creamos una variable, «signalTime», para llevar la cuenta de cuándo se generó la última señal de negociación. Hacemos esta variable estática para asegurarnos de que mantiene su valor entre llamadas a la función OnTick. Si «signalTime» no tuviera duración de almacenamiento estático, se asignaría cada vez que se llamara a OnTick, y su valor desaparecería después de que OnTick terminara de ejecutarse. Queremos que el tiempo de la señal se establezca sólo una vez para cada barra, y que mantenga su valor hasta que comience la siguiente barra. Por lo tanto, lo ponemos a 0 al principio para que tenga un valor sólo después de que se genere la primera señal. Para evitar que genere una segunda (o múltiples) señal durante la misma barra, compararemos el tiempo de la barra actual con el valor almacenado en la variable. A continuación, podemos definir la lógica de negociación.
// Check for BUY signal conditions: // - Current SAR is below the current low (bullish) // - Previous SAR was above the previous high (bullish reversal) // - SMA is below the current Ask price (indicating upward momentum) // - No other positions are currently open (PositionsTotal() == 0) // - The signal hasn't already been generated on the current bar if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0 && sma_data[0] < Ask && PositionsTotal() == 0){ Print("BUY SIGNAL @ ",TimeCurrent()); // Print buy signal with timestamp signalTime = currTime0; // Update the signal time to the current bar time }
Aquí, verificamos las condiciones para emitir una señal de compra basándonos en el comportamiento simultáneo de los indicadores SAR Parabólico y SMA, junto con un par de restricciones más. Para obtener una señal alcista del SAR Parabólico, el valor actual del SAR («sar_data[0]») debe estar por debajo del precio mínimo actual («low0»). Al mismo tiempo, el valor anterior del SAR («sar_data[1]») debe haber estado por encima del precio máximo anterior («high1»). Para obtener una señal de SMA alcista, la SMA debe estar por debajo del precio Ask actual («sma_data[0] < Ask»).
Además, comprobamos que no hay más posiciones abiertas (PositionsTotal == 0). No querríamos abrir múltiples operaciones al mismo tiempo. Luego nos aseguramos de que la señal no se haya generado para la barra actual comparando "signalTime" con "currTime0". Si todas estas comprobaciones pasan, imprimimos un mensaje anunciando la generación de una señal de compra. También actualizamos la variable de tiempo de la señal a la hora actual. Ese pequeño y útil truco es lo que nos permite limitar la generación de señal a solo una vez por barra. Al final, lo que estamos haciendo es asegurarnos de que el EA solo reaccione a ciertas condiciones del mercado y no realice ningún trabajo innecesario. Los resultados que obtenemos son los siguientes:
Una vez que recibamos la señal, procedamos a abrir posiciones de compra.
obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point); // Execute a buy order with a lot size of 0.01, stop loss and take profit
Aquí ejecutamos una orden de compra utilizando el método «Buy» de la clase CTrade (representada por el objeto «obj_Trade» que hemos creado). He aquí un desglose de los parámetros:
- 0.01: Es el tamaño del lote de la operación. Aquí, estamos especificando un tamaño de posición pequeño de 0.01 lotes.
- _Symbol: Representa el símbolo comercial (par de divisas, acción, etc.) al que está vinculado el Asesor Experto. El símbolo se detecta automáticamente utilizando la variable incorporada _Symbol.
- Ask: Es el precio al que se ejecutará la orden de compra, que es el precio Ask actual del mercado. El precio Ask se utiliza normalmente para comprar operaciones.
- Ask - 150 * _Point: Este es el nivel de stop loss. Fijamos el stop loss 150 puntos por debajo del precio Ask para limitar las pérdidas potenciales en caso de que el mercado se mueva en contra de la operación.
- Ask + 100 * _Point: Este es el nivel de toma de beneficios. Fijamos la toma de beneficios 100 puntos por encima del precio Ask, lo que significa que la operación se cerrará automáticamente cuando el mercado alcance este nivel de beneficios.
Al colocar esta orden, le estamos indicando al Asesor Experto que abra una operación de compra al precio Ask actual, con niveles predefinidos de stop loss y take profit para gestionar el riesgo y la recompensa. El fragmento de código final para confirmar señales de compra y abrir órdenes de compra es el siguiente:
// Check for BUY signal conditions: // - Current SAR is below the current low (bullish) // - Previous SAR was above the previous high (bullish reversal) // - SMA is below the current Ask price (indicating upward momentum) // - No other positions are currently open (PositionsTotal() == 0) // - The signal hasn't already been generated on the current bar if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0 && sma_data[0] < Ask && PositionsTotal() == 0){ Print("BUY SIGNAL @ ",TimeCurrent()); // Print buy signal with timestamp signalTime = currTime0; // Update the signal time to the current bar time obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point); // Execute a buy order with a lot size of 0.01, stop loss and take profit }
Para confirmar una señal de venta y abrir una orden de venta, se aplica una lógica similar.
// Check for SELL signal conditions: // - Current SAR is above the current high (bearish) // - Previous SAR was below the previous low (bearish reversal) // - SMA is above the current Bid price (indicating downward momentum) // - No other positions are currently open (PositionsTotal() == 0) // - The signal hasn't already been generated on the current bar else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0 && sma_data[0] > Bid && PositionsTotal() == 0){ Print("SELL SIGNAL @ ",TimeCurrent()); // Print sell signal with timestamp signalTime = currTime0; // Update the signal time to the current bar time obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point); // Execute a sell order with a lot size of 0.01, stop loss and take profit }
Aquí, implementamos una lógica de operación de venta que se ejecutará cuando existan condiciones de mercado específicas. Primero verificamos si el indicador SAR Parabólico (SAR) sugiere una tendencia bajista. Para ello, comparamos los datos más recientes del SAR con los precios de la barra. El SAR debe estar por encima del máximo de la barra de la vela actual («sar_data[0] > high0»), y el valor anterior del SAR estaba por debajo del precio mínimo de la barra anterior («sar_data[1] < low1»), lo que indica una posible reversión bajista. La indicación de tendencia del SAR puede ser el impulso «seguro para operar». Luego comprobamos la SMA. Debería estar por encima del precio Bid («sma_data[0] > Bid»), lo que indica una posible tendencia bajista «segura para operar».
Cuando se cumplen todas las condiciones anteriores, señalamos una venta y la registramos ("Print("SELL SIGNAL @ ", TimeCurrent())"). La variable «signalTime» registra la hora de la barra actual; esto es importante porque debemos asegurarnos de no emitir más de una señal de venta por barra. Para ejecutar la operación, utilizamos la función del objeto «obj_Trade.Sell». Entramos sistemáticamente en el mercado en los momentos en que pueden producirse posibles retrocesos bajistas. Lo hacemos teniendo en cuenta el riesgo que corremos. Y gestionamos ese riesgo mediante órdenes stop loss y take profit. La configuración de la confirmación de venta es la siguiente:
Hasta este punto, ahora está claro que hemos elaborado correctamente el algoritmo del Asesor Experto. Así, el código fuente completo de OnTick responsable de confirmar operaciones y abrir posiciones es el siguiente:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ // OnTick is called whenever there is a new market tick (price update) double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // Get and normalize the Ask price double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // Get and normalize the Bid price // Retrieve the last 3 values of the SMA indicator into the sma_data array if (CopyBuffer(handleSMA,0,0,3,sma_data) < 3){ Print("ERROR: NOT ENOUGH DATA FROM SMA FOR FURTHER ANALYSIS. REVERTING"); // Print error if insufficient SMA data return; // Exit the function if not enough data is available } // Retrieve the last 3 values of the SAR indicator into the sar_data array if (CopyBuffer(handleSAR,0,0,3,sar_data) < 3){ Print("ERROR: NOT ENOUGH DATA FROM SAR FOR FURTHER ANALYSIS. REVERTING"); // Print error if insufficient SAR data return; // Exit the function if not enough data is available } //ArrayPrint(sma_data,_Digits," , "); //ArrayPrint(sar_data,_Digits," , "); // Get the low prices for the current and previous bars on the M1 timeframe double low0 = iLow(_Symbol,PERIOD_M1,0); // Low of the current bar double low1 = iLow(_Symbol,PERIOD_M1,1); // Low of the previous bar // Get the high prices for the current and previous bars on the M1 timeframe double high0 = iHigh(_Symbol,PERIOD_M1,0); // High of the current bar double high1 = iHigh(_Symbol,PERIOD_M1,1); // High of the previous bar //Print("High bar 0 = ",high0,", High bar 1 = ",high1); //Print("Low bar 0 = ",low0,", Low bar 1 = ",low1); // Define a static variable to track the last time a signal was generated static datetime signalTime = 0; datetime currTime0 = iTime(_Symbol,PERIOD_M1,0); // Get the time of the current bar // Check for BUY signal conditions: // - Current SAR is below the current low (bullish) // - Previous SAR was above the previous high (bullish reversal) // - SMA is below the current Ask price (indicating upward momentum) // - No other positions are currently open (PositionsTotal() == 0) // - The signal hasn't already been generated on the current bar if (sar_data[0] < low0 && sar_data[1] > high1 && signalTime != currTime0 && sma_data[0] < Ask && PositionsTotal() == 0){ Print("BUY SIGNAL @ ",TimeCurrent()); // Print buy signal with timestamp signalTime = currTime0; // Update the signal time to the current bar time obj_Trade.Buy(0.01,_Symbol,Ask,Ask-150*_Point,Ask+100*_Point); // Execute a buy order with a lot size of 0.01, stop loss and take profit } // Check for SELL signal conditions: // - Current SAR is above the current high (bearish) // - Previous SAR was below the previous low (bearish reversal) // - SMA is above the current Bid price (indicating downward momentum) // - No other positions are currently open (PositionsTotal() == 0) // - The signal hasn't already been generated on the current bar else if (sar_data[0] > high0 && sar_data[1] < low1 && signalTime != currTime0 && sma_data[0] > Bid && PositionsTotal() == 0){ Print("SELL SIGNAL @ ",TimeCurrent()); // Print sell signal with timestamp signalTime = currTime0; // Update the signal time to the current bar time obj_Trade.Sell(0.01,_Symbol,Bid,Bid+150*_Point,Bid-100*_Point); // Execute a sell order with a lot size of 0.01, stop loss and take profit } } //+------------------------------------------------------------------+
¡Un brindis por nosotros por haber creado el programa! A continuación, tenemos que hacer las pruebas del Asesor Experto y optimizarlo para obtener las máximas ganancias. Esto se hace en la siguiente sección.
Pruebas y optimización
Esta sección se concentra en la prueba y optimización de nuestra estrategia de trading rápido utilizando el Probador de estrategias MetaTrader 5. Las pruebas son esenciales porque necesitamos asegurarnos de que nuestro Asesor Experto (EA) esté funcionando como debería y que tenga un buen rendimiento en una variedad de condiciones de mercado. Para comenzar a probar nuestro EA, debemos cargarlo en el Probador de estrategias, elegir el símbolo comercial y el período de tiempo deseados (hemos especificado el período de tiempo M1 en nuestro código) y establecer el período de prueba. Al realizar pruebas retrospectivas de nuestro EA, podemos obtener una simulación histórica de cómo se habría desempeñado nuestro EA con la estrategia y los parámetros especificados. Por supuesto, tratamos de identificar cualquier problema potencial que pudiera causar que el EA no funcione correctamente en un escenario de trading en vivo. Para abrir el Probador de estrategias, haga clic en ver y seleccione Probador de estrategias.
Una vez abierto, navegue a la pestaña de descripción general, seleccione "Visualizar", cambie a la pestaña de configuración, cargue el programa y configure la configuración deseada. Para nuestro caso, tendremos el periodo de pruebas sólo durante este mes para poder trabajar con datos pequeños.
Tras las pruebas, tenemos los siguientes resultados:
Necesitamos analizar de cerca los resultados de la fase de prueba, concentrándonos en métricas de rendimiento vitales como el beneficio neto total, la caída máxima y el porcentaje de operaciones ganadoras. Estos números nos dirán qué tan bien (y qué tan mal) funciona nuestra estrategia y nos darán una idea de su solidez general. También queremos examinar los informes comerciales que produce el Probador de Estrategias. Estos informes nos permitirán ver los tipos de operaciones que nuestro EA está realizando (o no realizando) y nos darán la oportunidad de comprender su "razonamiento" cuando encuentra diversas condiciones de mercado. Si no estamos satisfechos con los resultados, tendremos que reelaborar la estrategia para buscar conjuntos de parámetros de mejor rendimiento para los indicadores SMA y SAR. O tendremos que profundizar más en la lógica general de la estrategia para descubrir por qué no funciona mejor de lo que funciona.
Para encontrar la mejor estrategia comercial, debemos adaptarla al mercado. Para ello, utilizamos un método llamado "optimización estratégica". La optimización estratégica no es algo que hacemos una sola vez y luego olvidamos. Tenemos que ver cómo se comporta el sistema bajo diferentes configuraciones y luego elegir la configuración que nos dé los resultados más consistentes en todas las pruebas que realizamos. Al probar una estrategia de esta manera, efectivamente encontramos los parámetros óptimos y los usamos como claves para desbloquear la estrategia comercial que estamos examinando. Después de encontrar estas claves, tenemos que ejecutar la estrategia nuevamente y probarla durante un período de avance. De lo contrario, no somos mejores que alguien que simplemente adivina la dirección futura del mercado. Para optimizar el sistema, necesitaremos tener algunas entradas desde donde puedan realizarse las iteraciones. Se implementará la siguiente lógica.
input int sl_points = 150; // Stoploss points input int tp_points = 100; // Takeprofit points
Tras la optimización, obtenemos los siguientes resultados:
El gráfico de resultados optimizados se parece al que aparece a continuación:
Finalmente, el informe de prueba es el que se muestra a continuación:
Conclusión
En este artículo, analizamos el desarrollo de un asesor experto (EA) de trading rápido en MQL5, que ejecuta operaciones cortas y rápidas. La estrategia se basa en dos indicadores técnicos principales: el SAR Parabólico, que señala cambios de tendencia, y la Media Móvil Simple (SMA), que mide el impulso del mercado. Estos indicadores forman el núcleo de la lógica comercial del EA.
Cubrimos los detalles de implementación, incluida la configuración de los indicadores, la configuración de los controladores de datos y la gestión de las operaciones para garantizar que el EA interactúe de manera efectiva con el mercado. Por último, hicimos hincapié en la importancia de probar y optimizar el EA utilizando el Probador de Estrategias de MetaTrader 5 para asegurarnos de que cumple nuestros objetivos comerciales. A pesar de ser un sistema automatizado, el proceso de desarrollo es muy similar al de las estrategias de negociación manuales.
Descargo de responsabilidad: La información ilustrada en este artículo sólo tiene fines educativos. Está destinado únicamente a mostrar ideas sobre cómo crear un Asesor Experto (EA) de "disparo rápido" basado en el enfoque de acción del precio, y por tanto debe utilizarse como base para desarrollar un asesor experto mejorado, con una mayor optimización y una extracción de datos más completa tenida en cuenta. La información presentada no garantiza ningún resultado comercial.
Esperamos que el artículo le haya resultado útil, divertido y fácil de entender, de manera que pueda utilizar el conocimiento presentado en el desarrollo de sus futuros asesores expertos. Técnicamente, esto facilita su forma de analizar el mercado basándose en el enfoque de acción del precio y, en particular, en la estrategia Rapid-Fire (disparo rápido). Disfrútalo.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15698





- 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
Debería comprobar los posibles motivos en el registro y confirmar que se están generando señales.
¡Sería interesante añadir la posibilidad de tener ajustes de gestión de riesgo, por tamaño de lote fijo o un porcentaje del saldo, gracias!
¡Muy interesante, encontré esta estrategia después de probarla manualmente!
Sería interesante añadir la posibilidad de tener ajustes de gestión de riesgo, por tamaño de lote fijo o un porcentaje del saldo, ¡gracias!
Nos alegra saberlo. Consideraremos la idea. Gracias.
¡¡¡Gran artículo!!! Después de leerlo y aplicarlo se me ocurren un montón de ideas para el desarrollo de EA basados en esta estrategia.
Muchas gracias y bienvenido.