
Introducción a MQL5 (Parte 8): Guía del trading algorítmico para principiantes (II)
Introducción
Después de haber estudiado los fundamentos de MQL5, ahora está preparado para asumir una de las tareas más importantes asociadas con el trading algorítmico: crear un Asesor Experto funcional. Como indiqué en el artículo anterior, utilizaremos un enfoque basado en proyectos para esta serie. Este método ayuda a comprender ideas abstractas y reconocer cómo se utilizan en situaciones prácticas. Cuando termine de leer esta guía, tendrá una comprensión sólida de cómo automatizar decisiones comerciales basadas en patrones de velas y condiciones predeterminadas.
Muchos principiantes publican frecuentemente preguntas en el foro, y aunque los increíbles miembros de la comunidad responden con excelentes consejos, a algunos usuarios autodidactas les resulta difícil incorporar estas respuestas en sus programas. Si bien se proporciona la respuesta a una pregunta específica, con frecuencia no resulta práctico brindar una explicación detallada de todo el código. Si bien los fragmentos de código y los consejos pueden ser útiles, como novato autodidacta, es posible que aún le resulte difícil unir todo. Con un enfoque basado en proyectos, este artículo busca responder estas preguntas frecuentes y garantizar que las soluciones puedan ser aplicadas por cualquier EA.
En este artículo, nos centraremos en el desarrollo de un EA que utilice el análisis de velas del día anterior para determinar su dirección comercial. El EA se concentrará en vender durante el día si la vela diaria más reciente es bajista y en comprar si es alcista. El EA también verificará sus señales comerciales utilizando el precio de cierre de la primera vela de 1 hora del día. No habrá más de una posición abierta en un momento dado y se aplicará el máximo diario de dos operaciones. Funcionará bajo estrictos límites comerciales. Además, su funcionamiento estará limitado al horario comercial señalado de lunes a miércoles.
A lo largo de este proyecto, abordaremos varias preguntas comunes que los principiantes suelen tener, especialmente aquellas que se plantean con frecuencia en los foros de MQL5. Algunas de estas preguntas incluyen:
- ¿Cómo comprar y vender en MQL5?
- ¿Cómo obtener los precios de apertura y cierre de las velas?
- ¿Cómo evitar operar en cada tick?
- ¿Cómo puedo limitar que un EA solo ingrese una operación a la vez?
- ¿Cómo establecer el período de negociación para un EA?
- ¿Cómo especificar los días de la semana en que un EA puede operar?
- ¿Cómo establecer límites de ganancias y pérdidas para las operaciones?
Al adoptar este enfoque basado en proyectos, mi objetivo es proporcionar respuestas claras y prácticas a estas preguntas, permitiéndole implementar las soluciones directamente en su Asesor Experto. Este método práctico no sólo ayuda a comprender conceptos teóricos sino también a ver su aplicación en escenarios del mundo real. ¡Entremos en el mundo de MQL5 y comencemos a construir tus herramientas comerciales!
1. Configuración del proyecto
1.1. Pseudocódigo
Es crucial utilizar pseudocódigo para describir la lógica de nuestro Asesor Experto antes de comenzar a escribir código. La importancia del pseudocódigo se abordó en el artículo anterior, enfatizando cómo facilita la planificación y organización clara de sus ideas, lo que acelera el proceso de codificación real. Para aprender MQL5, tenga en cuenta que el aprendizaje basado en proyectos es preferible a estudiar todo a la vez. Mejorarás cuanto más proyectos realices. Este es el pseudocódigo básico de nuestro EA:
1. Inicializar el EA:
- Establezca el número mágico para la identificación comercial.
- Definir horas de inicio y finalización de las operaciones.
- Inicializar variables para almacenar precios.
2. En cada tick
- Asegúrese de que la hora esté dentro del horario comercial verificando la hora actual.
- Obtenga los precios de apertura y cierre del día anterior.
- Realizar una operación de venta si el cierre del día anterior fue inferior a la apertura (bajista).
- Realice una operación de compra si el cierre del día anterior fue mayor que la apertura (alcista).
- Asegúrese de que solo haya una operación abierta a la vez.
- Solamente comercio de lunes a jueves.
- Garantizar un máximo de un puesto vacante
- Límites diarios de dos transacciones por día.
- Límite de beneficio diario.
- Cerrar las operaciones al final del período de negociación.
1.2. Importación de las bibliotecas necesarias
Las bibliotecas son conjuntos de clases y funciones preescritas en MQL5 que facilitan la codificación. Le permiten concentrarse en las características distintivas de su Asesor Experto (EA) en lugar de tener que reescribir funcionalidades comunes. En uno de nuestros artículos anteriores, analizamos la idea de incluir archivos. No te preocupes si no entiendes completamente los archivos de inclusión; aún puedes seguir este artículo. No es necesario saberlo todo a la vez para utilizar MQL5; aprender a través del aprendizaje basado en proyectos le permitirá avanzar gradualmente. Puedes utilizar los fragmentos de código y las explicaciones en tus programas con eficacia si simplemente sigues las instrucciones.
Analogía
Considera el trabajo que haces como codificador como una enorme estantería encantada. En esta estantería hay numerosos libros. Cada libro contiene pautas y narrativas únicas que le ayudarán a alcanzar diversos objetivos.Similares a estos libros raros en su estantería encantada están las bibliotecas MQL5. Las historias (o códigos) preescritos en estos libros se pueden usar para construir su robot comercial. Puedes simplemente tomar el libro apropiado y usar las historias que contiene para guiarte, ahorrándote así el problema de tener que empezar desde cero cada vez que escribas.
Por lo tanto, no es necesario que escriba usted mismo cada detalle al desarrollar su Asesor Experto. Estos libros en particular contienen instrucciones que puedes utilizar para simplificar tu codificación. Esto le permitirá concentrarse en los aspectos especiales e interesantes de su robot comercial en lugar de empantanarse en los detalles mundanos.
1.2.1. Incluir la biblioteca comercial
Debes incorporar la biblioteca de Comercio a tu EA para administrar las operaciones comerciales. Esta biblioteca ofrece una colección de clases y funciones para una gestión comercial eficaz. El código que sigue se utiliza para incluir la biblioteca:
Analogía
Imagínate tener una estantería enorme y repleta de volúmenes. Cada libro funciona como una especie de caja de herramientas que ofrece ayuda en diversas áreas. Para crear algo único, como un robot comercial asombroso, se saca el libro correspondiente del estante. Es como si se hubiera sacado un libro especial del estante para incluirlo en la Biblioteca Comercial. En este libro encontrarás todas las herramientas necesarias para gestionar las operaciones comerciales. ¡Este libro ya tiene escritas todas las instrucciones, por lo que no tendrás que hacerlo tú mismo!
Incluye este libro especial en tu proyecto con la siguiente línea de código:
#include <Trade/Trade.mqh> // Include the trade library for trading functions
Su robot de operaciones aprenderá a partir de esta frase a "ir a buscar el libro llamado Trade.mqh de la estantería". Este libro contiene todo tipo de herramientas útiles que agilizan el proceso de apertura, modificación y cierre de operaciones. Asegura que todo funcione sin problemas y te ahorra muchísimo tiempo.
Ejemplo:
#include <Trade/Trade.mqh> // Include the trade library for trading functions // Create an instance of the CTrade class for trading operations CTrade trade; //magic number int MagicNumber = 103432; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set the magic number for the EA's trades trade.SetExpertMagicNumber(MagicNumber); // Return initialization success return(INIT_SUCCEEDED); }
1.2.2.1. Crear una instancia de la clase CTrade
La clase CTrade, parte de la biblioteca Trade, encapsula métodos para ejecutar operaciones comerciales. La creación de una instancia de esta clase le permitirá utilizar sus funciones para administrar sus operaciones.
Analogía
Imagina nuevamente tu estantería. Ya has elegido el libro especial (Trade.mqh) que tiene todas las instrucciones para operar. Ahora, piense en la clase CTrade como un personaje especial en este libro, como un superhéroe que sabe todo sobre trading. Para utilizar a este superhéroe, debe incluirlo en su historia. Para ello, crea una instancia de la clase CTrade. Es como invitar al superhéroe a ayudarte con tu proyecto.
Así es como invitas al superhéroe con una simple línea de código:
CTrade trade;
En este código, CTrade es el superhéroe y el comercio es su invitación. Al crear esta instancia llamada comercio, estás diciendo: "Superhéroe CTrade, ¡únete a mi proyecto y ayúdame a administrar los intercambios!"
Ahora, cada vez que necesites realizar o gestionar un intercambio, puedes pedirle a tu superhéroe que lo haga por ti. Esto hace que su trabajo sea mucho más fácil y garantiza que todo se haga correctamente.
1.2.2.2. Configurando el número mágico
Un número mágico es un identificador único asignado a cada operación realizada por su EA. Este identificador le ayuda a realizar un seguimiento y gestionar las transacciones, lo que garantiza que su EA pueda distinguir sus propias transacciones de las realizadas manualmente o por otros EA. Piense en cada libro de su estantería como una transacción. A veces, es posible que tengas varios libros (intercambios) que parezcan similares, pero cada uno tenga una etiqueta especial con un número único. Esta pegatina te ayuda a encontrar y administrar rápidamente tus libros sin confundirte.
Su asesor experto (EA) conoce esta etiqueta única como un número mágico. Es una forma rastreable y manejable de identificar cada operación que realiza su EA, lo que garantiza que pueda diferenciar sus operaciones de aquellas realizadas manualmente o por otros EA.
Ejemplo:
int MagicNumber = 103432;En el contexto de la clase CTrade, establece el número mágico de la siguiente manera:
trade.SetExpertMagicNumber(MagicNumber);
Esta línea garantiza que cada transacción realizada por la instancia comercial tendrá el número mágico especificado, lo que facilita su identificación y gestión.
Para utilizar código previamente escrito que agilice tareas complejas, debe importar las bibliotecas necesarias en MQL5. Las bibliotecas son similares a kits de herramientas que contienen elementos que ahorran tiempo y esfuerzo. El uso de estas bibliotecas le permitirá concentrarse en las características distintivas de su Asesor Experto (EA) en lugar de tener que escribir todo el código desde cero.
2. Recuperación y análisis de datos de velas japonesas
Es esencial recuperar y analizar los precios de apertura y cierre de las velas. Esta información nos ayuda a comprender las tendencias del mercado y decidir si comprar o vender. Necesitamos acceder a los precios de apertura y cierre de las velas para obtener una imagen clara de los movimientos del mercado. Usando funciones como CopyOpen y CopyClose, podemos obtener estos datos de manera eficiente. Con esto, podemos identificar si una vela es alcista o bajista comparando los precios de apertura y cierre.
2.1. Funciones CopyClose y CopyOpen en MQL5
Para obtener los precios de apertura y cierre de velas para un símbolo y período de tiempo determinados, utilice las funciones CopyClose y CopyOpen de MQL5. Estas funciones tienen múltiples formas de copiar datos según sus necesidades.
2.1.1. Llamada por la primera posición y número de elementos requeridos
Analogía
Considere la estantería como su fuente de datos de precios, donde cada libro sirve como una vela que representa los precios de apertura y cierre. Contarías los libros desde la izquierda y seleccionarías el punto de inicio cuando quisieras comenzar a copiar libros desde un lugar específico del estante. Puedes empezar, por ejemplo, con el tercer libro si es ahí donde quieres empezar.
A continuación, selecciona la cantidad de libros que deseas llevar si deseas copiar un número específico de libros comenzando en esa posición. Por ejemplo, terminarás con un total de cinco libros en la mano si decides tomar cinco libros, comenzando con el tercero. Similar a elegir un libro inicial en el estante y determinar cuántos libros tomar a partir de ese punto, este método utiliza la función CopyClose para permitirle especificar una posición inicial en los datos de precios y la cantidad de elementos (velas) que desea copiar.
Sintaxis:
CopyClose(symbol_name, timeframe, start_pos, count, close_array); CopyOpen(symbol_name, timeframe, start_pos, count, open_array);
Ejemplo:
double close_prices[]; double open_prices[]; CopyClose(_Symbol, PERIOD_D1, 2, 5, close_prices); // Copy the close prices of 5 daily candlesticks starting from the 3rd candlestick CopyOpen(_Symbol, PERIOD_D1, 2, 5, open_prices); // Copy the open prices of 5 daily candlesticks starting from the 3rd candlestick
En esta analogía, los datos de precios de un símbolo y un período de tiempo determinados están representados por la estantería, que está simbolizada por el nombre del símbolo y el período de tiempo. La decisión de tomar cinco libros de la posición inicial (conteo) es equivalente a seleccionar el tercer libro del estante. La posición inicial (start_pos) es similar a esa decisión. Como si tuvieras los libros seleccionados en tus manos, almacenas los datos copiados en la matriz de destino (close_array).
2.1.2. Llamada por fecha de inicio y número de elementos requeridos
Analogía
Considere su estantería como sus datos de precios, donde un solo libro corresponde a un día de lecturas de velas. Para comenzar a copiar precios de cierre de una fecha determinada, ubique el libro en el estante que corresponde a esa fecha. La fecha "1 de junio", por ejemplo, es donde debes comenzar si deseas comenzar en ese punto.
A continuación, selecciona la cantidad de libros que deseas llevar si deseas duplicar un número específico de precios de cierre a partir de esa fecha. Si tomaras, por ejemplo, los precios de cierre de cinco libros que comienzan con "1 de junio", obtendrías los precios de cierre de esos cinco libros. Este método, que es similar a elegir un libro de inicio en el estante y determinar cuántos libros tomar a partir de ese punto, utiliza la función CopyClose para permitirle especificar una fecha de inicio en los datos de precios y la cantidad de elementos (velas) que desea copiar.
Del mismo modo, utilizarías la función CopyOpen de la misma manera para obtener los precios de apertura. Seleccionas el libro que corresponde a una fecha determinada y eliges la cantidad de libros que deseas llevar para obtener los precios abiertos.
Sintaxis:
CopyClose(symbol_name, timeframe, timeframe, count, close_array[]); CopyOpen(symbol_name, timeframe, timeframe, count, open_array[]);
Ejemplo:
close_prices[]; double open_prices[]; datetime start_date = D'2023.06.01 00:00'; // Starting from June 1st, 2023 // Copy the close prices of 5 daily candlesticks starting from June 1st, 2023 CopyClose(_Symbol, PERIOD_D1, start_date, 5, close_prices); // Copy the open prices of 5 daily candlesticks starting from June 1st, 2023 CopyOpen(_Symbol, PERIOD_D1, start_date, 5, open_prices);
En esta analogía, los datos de precios de un símbolo y un período de tiempo determinados están representados por la estantería, que está simbolizada por el nombre del símbolo y el período de tiempo. Seleccionar el libro en el estante que corresponde al "1 de junio" es similar a la fecha de inicio (start_time), y seleccionar cinco libros a partir de esa fecha de inicio es análogo a la cantidad de elementos (count). Como si tuvieras los libros seleccionados en tus manos, almacenas los datos copiados en las matrices de destino (close_array y open_array).
Siguiendo esta analogía, podrá ver cómo las funciones CopyClose y CopyOpen facilitan la recuperación organizada e intuitiva de datos específicos de su biblioteca de datos de precios en función de una fecha de inicio.
2.1.3. Llamada por las fechas de inicio y fin de un intervalo de tiempo requerido
Analogía
Tenga en cuenta que cada libro en su estantería es una vela que muestra los precios de apertura y cierre para un período de tiempo específico. Si desea recuperar precios de cierre para un período de tiempo determinado, buscará libros que cubran el período completo.
A modo de ejemplo, supongamos que desea duplicar los precios de cierre del 1 al 5 de junio. Ubicarías el libro que corresponde a tu punto de inicio el 1 de junio y a tu punto final el 5 de junio. Los precios de cierre para ese período de tiempo se pueden obtener eligiendo cada libro publicado entre estas dos fechas.
De manera similar a seleccionar un libro inicial y uno final para cubrir un rango determinado en el estante, este método le permite especificar una fecha de inicio y una de finalización. Del mismo modo, puedes utilizar la función CopyOpen de la misma manera para obtener los precios de apertura.
Sintaxis:CopyClose( symbol_name, timeframe, start_time, stop_time, close_array[]); CopyOpen(symbol_name, timeframe, start_time, stop_time, open_array[]);Ejemplo:
double close_prices[]; double open_prices[]; datetime start_date = D'2023.06.01 00:00'; // Starting from June 1st, 2023 datetime end_date = D'2023.06.05 00:00'; // Ending on June 5th, 2023 // Copy the close prices from June 1st to June 5th CopyClose(_Symbol, PERIOD_D1, start_date, end_date, close_prices); // Copy the open prices from June 1st to June 5th CopyOpen(_Symbol, PERIOD_D1, start_date, end_date, open_prices);
En esta analogía, los datos de precios de un símbolo y un período de tiempo determinados están representados por la estantería, que está simbolizada por el nombre del símbolo y el período de tiempo. La fecha de inicio (start_time) es similar a elegir el libro correspondiente al "1 de junio" en el estante, y la fecha de finalización (stop_time) es como encontrar el libro correspondiente al "5 de junio". Como si tuvieras los libros seleccionados en tus manos, almacenas los datos copiados en las matrices de destino (close_array y open_array).
Al comprender esta analogía, puede ver cómo las funciones CopyClose y CopyOpen lo ayudan a recuperar datos específicos de su biblioteca de datos de precios en función de una fecha de inicio y finalización, lo que hace que el proceso de recuperación de datos sea intuitivo y organizado.
Convertir cadena a tiempo
Puede resultar difícil utilizar el método de llamada por las fechas de inicio y finalización de un intervalo de tiempo requerido cuando se trabaja con Asesores Expertos que operan dentro de una ventana de tiempo específica durante el día. El problema principal es que las fechas cambian, pero el tiempo no. Automatizamos nuestros Asesores Expertos, por lo que no es posible ingresar manualmente la fecha cada día.
Analogía
Imaginemos una biblioteca en la que los estantes están ordenados según la fecha y la hora del día. Debes saber el día y la hora si deseas leer libros (o recuperar datos) a una hora determinada del día. Puede resultar laborioso actualizar la fecha cada día para poder recuperar los libros adecuados de los estantes. Considere las cadenas de tiempo como etiquetas que coloca en las horas particulares que está interesado en leer para agilizar este proceso (recuperación de datos).
Ejemplo:
// Declaring time strings string start_time_str = "00:00"; // Start time string end_time_str = "20:00"; // End time //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // Converting time strings to datetime values datetime start_time = StringToTime(start_time_str); datetime end_time = StringToTime(end_time_str); Print("start_time: ", start_time,"\nend_time: ",end_time); }
Explicación:
// Declaring time strings string start_time_str = "00:00"; // Start time string end_time_str = "20:00"; // End time
- Considere las cadenas de tiempo como etiquetas que coloca en las horas particulares que está interesado en leer para agilizar este proceso (recuperación de datos).
// Converting time strings to datetime values datetime start_time = StringToTime(start_time_str); datetime end_time = StringToTime(end_time_str);
- Cuando utilizas la función StringToTime, es como si tuvieras un marcador mágico que, en un día determinado, sabe exactamente a qué estante apuntar. Esto es cierto independientemente de la fecha. De esta manera no tendrás que molestarte en cambiar la fecha cada día. Al garantizar que start_time y end_time hagan referencia consistentemente a la hora actual en el estante, se hace sencillo recuperar los libros (datos) apropiados sin necesidad de actualizaciones manuales.
Salida:
Implementación en la EA
Ejemplo:
#include <Trade/Trade.mqh> // Create an instance of the CTrade class for trading operations CTrade trade; // Unique identifier for the EA's trades int MagicNumber = 103432; // Arrays to store the previous day's open and close prices double daily_close[]; double daily_open[]; // Arrays to store the first H1 bar's open and close prices of the day double first_h1_price_close[]; // Arrays to store H1 bars' open and close prices double H1_price_close[]; double H1_price_open[]; // Strings to define the trading start and end times and the first trade time string start = "00:00"; string end = "20:00"; string firsttrade = "02:00"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { trade.SetExpertMagicNumber(MagicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Convert time strings to datetime format datetime start_time = StringToTime(start); // Convert start time string to datetime datetime end_time = StringToTime(end); // Convert end time string to datetime datetime current_time = TimeCurrent(); // Get the current time datetime first_tradetime = StringToTime(firsttrade); // Convert first trade time string to datetime // Copy daily close and open prices CopyClose(_Symbol, PERIOD_D1, 1, 1, daily_close); // Copy the close price of the previous day CopyOpen(_Symbol, PERIOD_D1, 1, 1, daily_open); // Copy the open price of the previous day // Set the arrays to be copied from right to left (latest to oldest) ArraySetAsSeries(daily_close, true); // Set daily_close array as series ArraySetAsSeries(daily_open, true); // Set daily_open array as series // Copy close and open prices for the first H1 bar of the day CopyClose(_Symbol, PERIOD_H1, start_time, 1, first_h1_price_close); // Copy the close price of the first H1 bar // Copy close prices for the latest 5 H1 bars CopyClose(_Symbol, PERIOD_H1, 0, 5, H1_price_close); // Copy the close prices of the latest 5 H1 bars CopyOpen(_Symbol, PERIOD_H1, 0, 5, H1_price_open); // Copy the open prices of the latest 5 H1 bars // Set the arrays to be copied from right to left (latest to oldest) ArraySetAsSeries(H1_price_close, true); // Set H1_price_close array as series ArraySetAsSeries(H1_price_open, true); // Set H1_price_open array as series // If the last daily bar is bearish if(daily_close[0] < daily_open[0]) { // Check specific conditions for a sell trade if(H1_price_close[2] >= first_h1_price_close[0] && H1_price_close[1] < first_h1_price_close[0] && current_time >= first_tradetime) { Comment("Its a sell"); } } // If the last daily bar is bullish if(daily_close[0] > daily_open[0]) { // Check specific conditions for a buy trade if(H1_price_close[2] <= first_h1_price_close[0] && H1_price_close[1] > first_h1_price_close[0] && current_time >= first_tradetime) { Comment("Its a buy"); } } }
Explicación:
Para comenzar, importamos la biblioteca comercial requerida con #include<Trade/Trade.mqh> , lo que nos permite utilizar funciones relacionadas con el comercio. A continuación, creamos una instancia de la clase CTrade, CTrade trade;, que facilitará nuestras operaciones comerciales. También definimos un identificador único para las operaciones de nuestro EA con int MagicNumber = 103432;.
Luego declaramos matrices para almacenar los precios de apertura y cierre del día anterior, los precios de apertura y cierre de la primera barra H1 del día y los precios de apertura y cierre de las barras H1. Estas matrices contendrán los datos de precios recuperados, que analizaremos para tomar decisiones comerciales. Las horas de inicio y finalización de las operaciones y la hora de la primera operación se definen como cadenas, que luego convertiremos al formato de fecha y hora. En la función OnInit, establecemos el número mágico del experto para identificar nuestras operaciones. En la función OnTick, convertimos las cadenas de tiempo al formato de fecha y hora y recuperamos la hora actual. Luego usamos las funciones CopyClose y CopyOpen para copiar los datos de velas diarias y H1 en las matrices respectivas. La función ArraySetAsSeries establece las matrices que se copiarán de derecha a izquierda, garantizando que los datos más recientes estén en el índice 0.
Finalmente, analizamos los datos de velas recuperados para determinar el sentimiento del mercado. Si la última barra diaria es bajista, verificamos condiciones específicas para decidir una operación de venta. De manera similar, si la última barra diaria es alcista, verificamos las condiciones para una operación de compra. La función Comentario muestra la decisión de negociación en el gráfico. Este código de ejemplo configura la estructura para recuperar y analizar los precios de apertura y cierre de velas diarias y de 1 hora, y luego utiliza este análisis para determinar la dirección de la negociación.
3. Implementación de la ejecución comercial
3.1 Cómo comprar y vender en MQL5
Esta sección cubrirá los pasos básicos necesarios para ejecutar órdenes de compra y venta en MQL5, incluida la determinación de los niveles de stop-loss y take-profit. Se utilizará la clase CTrade de la biblioteca Trade.mqh, que ofrece métodos básicos para realizar transacciones.
Realizar órdenes de compra y venta:
Utilizamos los métodos Buy y Sell de la clase CTrade para colocar órdenes de compra y venta. Podemos ejecutar operaciones con poco código gracias a estas técnicas. Considere la clase CTrade como un estante centrado en funciones comerciales especiales en una biblioteca. Sólo necesitamos tomar el libro requerido (método) del estante y aplicarlo cuando queramos ejecutar una operación.
Configuración de Stop Loss y Take Profit:
Los niveles de TP y SL son fundamentales para la gestión de riesgos. Mientras que TP especifica la ganancia deseada, SL especifica la pérdida máxima que está dispuesto a aceptar. Para ambos se especifican los puntos de precio en relación con el precio de mercado actual. Considere SL y TP como marcadores en un libro que indican dónde se detendrá (SL) en caso de que algo salga mal y dónde llevará su recompensa (TP) en caso de que todo salga bien.
Ejemplo:#include <Trade/Trade.mqh> // Include the trade library for trading functions // Create an instance of the CTrade class for trading operations CTrade trade; // Unique identifier for the EA's trades int MagicNumber = 103432; // Arrays to store the previous day's open and close prices double daily_close[]; double daily_open[]; // Arrays to store the first H1 bar's open and close prices of the day double first_h1_price_close[]; // Arrays to store H1 bars' open and close prices double H1_price_close[]; double H1_price_open[]; // Strings to define the trading start and end times and the first trade time string start = "00:00"; string end = "20:00"; string firsttrade = "02:00"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { trade.SetExpertMagicNumber(MagicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Convert time strings to datetime format datetime start_time = StringToTime(start); // Convert start time string to datetime datetime end_time = StringToTime(end); // Convert end time string to datetime datetime current_time = TimeCurrent(); // Get the current time datetime first_tradetime = StringToTime(firsttrade); // Convert first trade time string to datetime // Copy daily close and open prices CopyClose(_Symbol, PERIOD_D1, 1, 1, daily_close); // Copy the close price of the previous day CopyOpen(_Symbol, PERIOD_D1, 1, 1, daily_open); // Copy the open price of the previous day // Set the arrays to be copied from right to left (latest to oldest) ArraySetAsSeries(daily_close, true); // Set daily_close array as series ArraySetAsSeries(daily_open, true); // Set daily_open array as series // Copy close and open prices for the first H1 bar of the day CopyClose(_Symbol, PERIOD_H1, start_time, 1, first_h1_price_close); // Copy the close price of the first H1 bar // Copy close prices for the latest 5 H1 bars CopyClose(_Symbol, PERIOD_H1, 0, 5, H1_price_close); // Copy the close prices of the latest 5 H1 bars CopyOpen(_Symbol, PERIOD_H1, 0, 5, H1_price_open); // Copy the open prices of the latest 5 H1 bars // Set the arrays to be copied from right to left (latest to oldest) ArraySetAsSeries(H1_price_close, true); // Set H1_price_close array as series ArraySetAsSeries(H1_price_open, true); // Set H1_price_open array as series // Get the symbol point size double symbol_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Get the current Bid price double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Calculate the stop loss and take profit prices for sell double tp_sell = Bid - 400 * symbol_point; double sl_sell = Bid + 100 * symbol_point; // Get the current Ask price double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Calculate the stop loss and take profit prices for buy double tp_buy = Ask + 400 * symbol_point; double sl_buy = Ask - 100 * symbol_point; // If the last daily bar is bearish if(daily_close[0] < daily_open[0]) { // Check specific conditions for a sell trade if(H1_price_close[2] >= first_h1_price_close[0] && H1_price_close[1] < first_h1_price_close[0] && current_time >= first_tradetime) { // Execute the sell trade trade.Sell(1.0, _Symbol, Bid, sl_sell, tp_sell); // Replace with your lot size Comment("It's a sell"); } } // If the last daily bar is bullish if(daily_close[0] > daily_open[0]) { // Check specific conditions for a buy trade if(H1_price_close[2] <= first_h1_price_close[0] && H1_price_close[1] > first_h1_price_close[0] && current_time >= first_tradetime) { // Execute the buy trade trade.Buy(1.0, _Symbol, Ask, sl_buy, tp_buy); // Replace with your lot size Comment("It's a buy"); } } }
Explicación:
- Usando #include, incluimos la biblioteca comercial.
- Construimos una instancia comercial de la clase CTrade.
- En función de los precios de oferta y demanda actuales, así como del tamaño de punto del símbolo, determinamos los niveles de SL y TP.
- Utilizamos los métodos de Compra y Venta para realizar órdenes con los niveles SL y TP especificados.
Parámetros de trade.Buy y trade.Sell:
- lotsize: El volumen del comercio.
- _Symbol: El símbolo en el que se ejecuta la operación (por ejemplo, EURUSD).
- Ask/Bid: Precio actual de compra para operaciones de compra o precio de venta para operaciones de venta.
- sl: El nivel de precio de stop-loss.
- tp: El nivel de precio de toma de beneficios.
En este artículo, analizamos cómo obtener ciertos datos de velas, como los precios de apertura y cierre, y cómo colocar órdenes de compra y venta. Esta progresión es crucial para usted como principiante porque explica los fundamentos de la incorporación de operaciones a su programa. Sin embargo, las órdenes podrían continuar enviándose con cada tick gracias al controlador de eventos OnTick.
Es fundamental restringir el Asesor Experto (EA) a una sola operación a la vez para controlar el riesgo y evitar operaciones excesivas. Para hacer esto, se debe implementar una lógica para determinar si existen posiciones abiertas antes de realizar una nueva operación. Esta estrategia ayuda a prevenir el exceso de operaciones al garantizar que las operaciones solo se ejecuten cuando sea necesario.
Cómo evitar operaciones en cada tick:
El manejo de eventos por parte de OnTick podría generar nuevas posiciones para cada tick. Nos aseguramos de que las transacciones se realicen solo cuando sean necesarias, implementando un control de nuevas barras y posiciones abiertas. Considere cada nueva barra como un libro que se ha añadido a nuestra colección. Nos aseguramos de que no se esté leyendo ningún otro libro (intercambio) en este momento y solo elegimos "leer" (intercambio) cuando se agrega un nuevo libro (barra).
Ejemplo:
// Flag to indicate a new bar has formed bool newBar; // Variable to store the time of the last bar datetime lastBarTime; // Array to store bar data (OHLC) MqlRates bar[]; //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check for a new bar CopyRates(_Symbol, PERIOD_H1, 0, 3, bar); // Copy the latest 3 H1 bars if(bar[0].time > lastBarTime) // Check if the latest bar time is greater than the last recorded bar time { newBar = true; // Set the newBar flag to true lastBarTime = bar[0].time; // Update the last bar time } else { newBar = false; // Set the newBar flag to false } // If a new bar has formed if(newBar == true) { // If the last daily bar is bearish if(daily_close[0] < daily_open[0]) { // Check specific conditions for a sell trade if(H1_price_close[2] >= first_h1_price_close[0] && H1_price_close[1] < first_h1_price_close[0] && current_time >= first_tradetime) { // Execute the sell trade trade.Sell(1.0, _Symbol, Bid, sl_sell, tp_sell); // Replace with your lot size Comment("It's a sell"); } } // If the last daily bar is bullish if(daily_close[0] > daily_open[0]) { // Check specific conditions for a buy trade if(H1_price_close[2] <= first_h1_price_close[0] && H1_price_close[1] > first_h1_price_close[0] && current_time >= first_tradetime) { // Execute the buy trade trade.Buy(1.0, _Symbol, Ask, sl_buy, tp_buy); // Replace with your lot size Comment("It's a buy"); } } } }
Explicación:
bool newBar;
- Para indicar si se ha formado una nueva barra (vela), esta línea declara una variable booleana llamada newBar.
datetime lastBarTime;
- Para almacenar la hora de la barra procesada más reciente, esta línea declara una variable llamada lastBarTime de tipo datetime.
MqlRates bar[];
- Esta línea declara una matriz bar de tipo MqlRates. Esta matriz se utilizará para almacenar datos de barras, como precios de apertura, máximo, mínimo y cierre (OHLC).
CopyRates(_Symbol, PERIOD_H1, 0, 3, bar); // Copy the latest 3 H1 bars
- Las tres barras horarias (H1) más recientes para el símbolo actual (_Symbol) se copian en la matriz de barras utilizando la función CopyRates en esta línea.
if(bar[0].time > lastBarTime)
- Esta línea determina si el tiempo de la barra más reciente (bar[0].time) excede el tiempo de la barra anterior. Suponiendo que sea exacto, ha surgido un nuevo bar.
newBar = true;
- El indicador newBar se establece como verdadero si se ha formado una nueva barra.
lastBarTime = bar[0].time;
- La hora de la barra más reciente se actualiza en la variable lastBarTime.
else {
newBar = false;
}
- Si el tiempo de la última barra no es mayor que lastBarTime, el indicador newBar se establece en falso.
if(newBar == true)
{
// do this
}
- Si se ha formado una nueva barra, haga esto.
Este código garantiza que el código dentro de la declaración if se ejecute solo cuando se forme una nueva barra, no en cada marca. Esto se hace determinando si el tiempo de la barra más reciente es más largo que el tiempo de la última barra que se registró. En base a esto, podemos determinar cuándo se forma una nueva barra y establecer el indicador newBar apropiadamente. Esto evita que el código dentro del bloque 'if(newBar == true)' se ejecute repetidamente en cada tick, lo que mejora el rendimiento del asesor experto y elimina acciones innecesarias.
3.2 Limitar el EA a una posición abierta a la vez
Como ahora la ejecución comercial solo se inicia tras la formación de una nueva barra, podemos mejorar nuestro EA aún más restringiendo la cantidad de posiciones abiertas. De esta manera, el EA solo iniciará una nueva operación en caso de que no haya otras operaciones pendientes.
Esta es la lógica:
- Comprobación de posiciones abiertas: Comprobamos si hay posiciones abiertas antes de realizar una nueva operación. El EA colocará una operación si no hay posiciones abiertas.
- Detención de operaciones en cada tick: Nos aseguramos de que las operaciones sólo se realizan cuando son absolutamente necesarias añadiendo una comprobación de nuevas barras y posiciones abiertas.
Considere cada nueva barra como un libro que se ha añadido a nuestra biblioteca. Solo decidimos "leer" (intercambiar) cuando se agrega un nuevo libro (bar), después de asegurarnos de que no se esté leyendo ningún otro libro (intercambio) en ese momento.
Ejemplo:
// Initialize the total number of positions being held int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { // Get the ticket number for the position ulong ticket = PositionGetTicket(i); // Check if the position's magic number matches if(PositionGetInteger(POSITION_MAGIC) == MagicNumber) { // Increment the total positions count totalPositions++; } }
Explicación:
int totalPositions = 0;
- En esta línea, se declara la variable entera totalPositions y se establece en cero. Se contabilizará el número de posiciones abiertas que el Asesor Experto (EA) tiene actualmente utilizando esta variable.
for(int i = 0; i < PositionsTotal(); i++)
- Cada posición abierta se itera en este bucle for. La función PositionsTotal() devuelve el número total de posiciones abiertas en la terminal comercial.
- i: Un contador de bucle iterativo que comienza en 0 y aumenta en 1 cada vez. Mientras i sea menor que PositionsTotal(), el bucle continuará.
ulong ticket = PositionGetTicket(i);
- En esta línea se recupera el número de ticket para la posición en el índice i. La función PositionGetTicket(i) devuelve el número de ticket para la posición en el índice dado.
- ticket: Una variable de tipo ulong que almacena el número de ticket de la posición actual que se está examinando.
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
- Esta sentencia if determina si el número mágico asignado al EA (MagicNumber) y el número mágico de la posición actual coinciden
- PositionGetInteger(POSITION_MAGIC): Función que recupera el valor entero de la propiedad especificada (en este caso, el número mágico) para la posición actual.
- Número mágico: una constante predeterminada que sirve como identificador comercial único de este EA. Garantiza que sólo se cuenten las posiciones que el EA ha abierto.
totalPositions++;
- Esta línea aumenta el contador totalPositions en 1 si el número mágico de la posición actual coincide con MagicNumber. En esencia, se trata de un recuento del número de posiciones que este EA en particular ha abierto.
Este bloque de código se utiliza para contar el número de posiciones ocupadas actualmente por el EA. Esto lo hace de la siguiente manera:
- Inicializando un contador (totalPositions) a cero.
- Recorrer todas las posiciones abiertas mediante un bucle for.
- Para cada posición, recuperando su número de ticket.
- Comprobando si el número mágico de la posición coincide con el número mágico del EA.
- Si hay una coincidencia, se incrementa el contador totalPositions.
3.3 Limitar el EA a un máximo de dos operaciones por día
Nos aseguraremos de que el EA solo abra un máximo de dos operaciones por día para evitar el exceso de operaciones ahora que el EA ejecuta operaciones en función de condiciones específicas en cada nueva barra.
Detalles de implementación
Esta sección del código filtra el historial de transacciones del día actual utilizando la función HistorySelect. A continuación, utilizaremos el número mágico distintivo del EA para recorrer el historial comercial elegido y contar la cantidad de transacciones que ha realizado el algoritmo. Sólo contamos las operaciones de entrada verificando la naturaleza de cada operación. El EA no abrirá nuevas operaciones durante el resto del día si hay dos operaciones de entrada en total.
Considérelo similar a supervisar una estantería, donde cada libro es un intercambio. Sólo hay espacio para dos libros nuevos por día. No pones más libros en el estante hasta el día siguiente, después de poner esos dos allí. Esto garantiza mantener una colección ordenada y manejable, evitando el desorden (sobrecomercio) y manteniendo el orden (gestión de riesgos).
Ejemplo:
// Select the trading history within the specified time range bool success = HistorySelect(start_time, end_time); // Select the trading history // Initialize the total number of trades for the day int totalDeal = 0; if(success) { for(int i = 0; i < HistoryDealsTotal(); i++) { // Get the ticket number for the deal ulong ticket = HistoryDealGetTicket(i); // Check if the deal's magic number matches if(HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber) { // Check if the deal was an entry if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN) { // Increment the total deal count totalDeal++; } } } }
Explicación:
- HistorySelect: filtra el historial de operaciones del día.
- totalDeal: contador para llevar la cuenta del volumen de comercio.
- Loop through history: Para contar las operaciones, itere por el historial de operaciones.
- Verify the magic number: Verificar que nuestro EA es el propietario de la operación.
- Las operaciones de entrada se contabilizan incrementando el contador.
Este código garantiza que el EA solo opere hasta dos veces al día, manteniendo un enfoque comercial disciplinado y reduciendo el riesgo de sobreoperar.
3.4 Limitación de ganancias o pérdidas del día
En esta sección, nos aseguraremos de que el Asesor Experto no exceda las ganancias o pérdidas totales diarias.
// Initialize the total profit double totalProfit = 0; long dealsMagic = 0; double profit = 0; if(success) { for(int i = 0; i < HistoryDealsTotal(); i++) { // Get the ticket number for the deal ulong ticket = HistoryDealGetTicket(i); // Check if the deal was an entry if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN) { // Get the magic number of the deal dealsMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC); } // Check if the deal was an exit if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) { // Get the profit of the deal profit = HistoryDealGetDouble(ticket, DEAL_PROFIT); // Check if the magic number matches if(MagicNumber == dealsMagic) { // Add the profit to the total profit totalProfit += profit; } } } }
Explicación:
- HistorySelect: filtra el historial de operaciones del día.
- totalProfit: Contador para controlar las ganancias o pérdidas totales del día.
- Recorre el historial comercial en un bucle para totalizar las ganancias y pérdidas.
- Verifica el comercio de entrada: asegura el número mágico de cada transacción.
- Verifique la operación de salida calculando la ganancia de cada operación que se cierra.
- Añadir al beneficio total: Acumula el beneficio o la pérdida de las operaciones con el número mágico coincidente.
Al detener operaciones adicionales después de alcanzar un umbral de ganancias o pérdidas predeterminado para el día, esta estrategia reduce el riesgo. Garantiza un comercio controlado y ayuda a evitar operaciones excesivas, que aumentan el riesgo.
3.5 Cerrar todas las posiciones abiertas en un momento determinado
Esta sección cubrirá el uso del marco MQL5 para implementar dicha característica. Como resultado, hay menos posibilidades de que se produzcan fluctuaciones inesperadas en el mercado porque no quedan operaciones abiertas por la noche o durante las horas de menor actividad. Considérese el final del horario de apertura de la biblioteca. Nos aseguramos de que todas las posiciones estén cerradas a la hora final designada, al igual que un bibliotecario se asegura de que todos los libros (intercambios) se devuelvan y se contabilicen al final del día.
// Close trades at the specified end time for(int i = 0; i < PositionsTotal(); i++) { // Get the ticket number for the position ulong ticket = PositionGetTicket(i); // Check if the position's magic number matches and if it's the end time if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && current_time == end_time) { // Close the position trade.PositionClose(ticket); } }
Explicación:
Esta parte recupera el número de ticket específico para cada posición abierta iterándolas todas. A continuación, verifica que el número mágico de la posición corresponde con el número mágico del EA y que la hora actual está dentro de la hora final designada. La función PositionClose de la clase CTrade se utiliza para cerrar la posición si se cumplen estos requisitos.
3.6 Cómo especificar los días de la semana en los que un EA puede operar
Se debe obtener el día actual de la semana y compararlo con nuestras reglas comerciales para garantizar que nuestro Asesor Experto (EA) solo opere en los días designados de la semana.
Ejemplo://getting the day of week and month MqlDateTime day; //Declare an MqlDateTime structure to hold the current time and date TimeCurrent(day); // Get the current time and fill the MqlDateTime structure int week_day = day.day_of_week; //Extract the day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday) //getting the current month MqlDateTime month; //Declare a structure to hold current month information TimeCurrent(month); //Get the current date and time int year_month = month.mon; //Extract the month component (1 for January, 2 for February, ..., 12 for December) if(week_day == 5) { Comment("No trades on fridays", "\nday of week: ",week_day); } else if(week_day == 4) { Comment("No trades on Thursdays", "\nday of week: ",week_day); } else { Comment(week_day); }
Explicación:
Debemos determinar el día actual de la semana y compararlo con pautas predeterminadas para regular los días en los que un Asesor Experto (EA) puede operar. Para almacenar la fecha y hora actuales, primero declaramos una estructura MqlDateTime. Primero llenamos esta estructura con el mes actual y el día de la semana usando la función TimeCurrent().
A continuación, comprobamos el día de la semana mediante declaraciones condicionales. El código muestra un mensaje que indica que no se permiten transacciones los viernes si el día actual es viernes (mostrado por el valor 5). De manera similar, el jueves (valor 4) indica que no se permiten transacciones los jueves. El código indica que se permite operar todos los demás días simplemente mostrando el día actual de la semana.
Analogía
Considere cada día de la semana como un tipo diferente de libro encontrado en una biblioteca.
- Viernes (5): El código prohíbe comerciar los viernes, al igual que no permite prestar novelas de misterio los viernes.
- Jueves (4): El código deja de comercializarse los jueves, del mismo modo que las novelas de ciencia ficción no pueden sacarse los jueves.
- Otros días: El código permite el comercio para todos los demás géneros (días), al igual que para los géneros de libros.
Conclusión
En este artículo hemos utilizado una metodología de aprendizaje basada en proyectos para que sea fácil para cualquier persona comprender los fundamentos del trading algorítmico con MQL5, incluso para los principiantes. Al seguir este tutorial, adquirirá conocimientos sobre cómo realizar tareas fundamentales como comprar y vender en MQL5, obtener los precios de apertura y cierre de las velas y poner en práctica estrategias para evitar operar en cada tick. También ha aprendido a controlar aspectos importantes del trading automatizado, como restringir un EA a una sola operación a la vez, definir días y períodos de negociación y establecer límites de ganancias y pérdidas.
No dudes en hacer preguntas sobre los temas que cubre este artículo a medida que avanzas en tu viaje. Estoy aquí para ayudar, ya sea entendiendo implementaciones de código particulares o aclarando cómo estos conceptos se relacionan con varias estrategias comerciales. Póngase en contacto con nosotros y juntos podremos analizar formas de mejorar su comprensión y utilización del trading algorítmico MQL5. Recuerde que dominar cualquier lenguaje de programación, incluido MQL5, es un camino hacia el trading algorítmico. Naturalmente no es posible comprenderlo todo a la vez. Aumentarás gradualmente tus conocimientos y habilidades adoptando un aprendizaje basado en proyectos, en el que aplicarás conceptos a proyectos del mundo real similares a los que hemos estudiado. Cada proyecto sirve como un trampolín que gradualmente construye tu competencia y confianza.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15299





- 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