Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (I)
Contenido:
- Introducción
- Solución a los desafíos anteriores al dibujar rectángulos de riesgo y recompensa
- Creación de un Asesor Experto que funcione en base a un indicador
- Pruebas
- Conclusión
Introducción
El software MetaEditor incluye un compilador que gestiona eficazmente los errores detectados durante los intentos de creación de perfiles. Esta herramienta me ayudó a descubrir por qué la versión anterior no mostraba los rectángulos de riesgo-recompensa como estaba previsto. Aunque el programa se compiló correctamente, el problema no estaba en el código en sí. En cambio, el desafío residía en el hecho de que no se mostraba nada dentro del rango de las velas retrospectivas, principalmente debido a tecnicismos específicos. - El valor de la vela retrospectiva se estableció demasiado alto en 5000 barras de manera predeterminada.
- Tener varios buffers en un solo programa aumenta la complejidad de los cálculos, lo que puede ralentizar la visualización de la ventana del gráfico del indicador.
Después de discutir brevemente cómo resolvimos los problemas encontrados, pasaremos al objetivo principal de este artículo: desarrollar un Asesor Experto basado en el Indicador de Restricción de Tendencia refinado. A continuación se muestra una imagen que muestra cómo un script independiente abordó con éxito el problema que originalmente pretendíamos resolver con el indicador principal.

Rectángulos de riesgo y recompensa dibujados automáticamente usando rectángulo.
Solución a los desafíos anteriores al dibujar rectángulos de riesgo y recompensa
Para abordar los desafíos del programa de indicadores:- Redujimos el período de retrospección de 5000 barras a 1000 barras, lo que disminuyó significativamente la cantidad de datos a calcular.
- Nuestro objetivo era reducir la carga de trabajo del programa creando un script independiente como parte del conjunto de herramientas. Este script comprueba específicamente las condiciones manejadas por el Buffer 6 y el Buffer 7 en el indicador. Una vez que se cumplen estas condiciones, el script dibuja los rectángulos de riesgo-recompensa necesarios y coloca líneas con etiquetas de precio para el precio de entrada, Stop Loss y Take Profit. Sin embargo, es importante tener en cuenta que el script realiza una tarea puntual y no se ejecuta continuamente. El usuario debe añadir manualmente el script al gráfico para visualizar los niveles de negociación representados por los objetos dibujados y las marcas de precio.
A continuación se muestra una imagen del lanzamiento del programa script:

Trend Constraint R-R script: Para dibujar el rectángulo de Riesgo y Recompensa cuando se produce un cruce de Medias Móviles.
Al aislar esta característica, nos aseguramos de que nuestro indicador funcione sin problemas, evitando cualquier congelación del ordenador o del terminal de negociación. Incorporar rectángulos de riesgo-recompensa y marcar los niveles de salida permite a los operadores evaluar visualmente la dirección de la operación y los objetivos de antemano, lo que permite la negociación manual sin necesidad de un Asesor Experto. El script con la lógica requerida funcionó perfectamente, sin problemas. Aquí está nuestro script completo.//+------------------------------------------------------------------+ //| Trend Constraint R-R.mq5 | //| Script program | //+------------------------------------------------------------------+ #property strict #property script_show_inputs #property copyright "2024 Clemence Benjamin" #property version "1.00" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover." //--- input parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points input color RiskColor = clrIndianRed; // Default risk color input color RewardColor = clrSpringGreen; // Default reward color input int MaxBars = 500; // Maximum bars to process input int RectangleWidth = 10; // Width of the rectangle in bars input bool FillRectangles = true; // Option to fill rectangles input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; } ArraySetAsSeries(FastMA, true); ArraySetAsSeries(SlowMA, true); CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA); CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA); limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA)); for (i = 1; i < limit - 1; i++) { //--- check for crossover if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1]) { //--- long position entry point (bullish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice + RiskHeightPoints * Point(); rewardLevel = closePrice - RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1]) { //--- short position entry point (bearish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice - RiskHeightPoints * Point(); rewardLevel = closePrice + RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } } } //+------------------------------------------------------------------+ //| Function to delete existing rectangles and lines | //+------------------------------------------------------------------+ void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } } //+------------------------------------------------------------------+ //| Function to draw rectangles | //+------------------------------------------------------------------+ void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); // Set the color with transparency (alpha value) int alphaValue = FillTransparency; // Adjust transparency level (0-255) color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color } } //+------------------------------------------------------------------+ //| Function to draw price lines | //+------------------------------------------------------------------+ void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background // Create text label string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5); ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0); }
Vamos a discutir a fondo el rendimiento del script antes de proceder con el desarrollo del Asesor Experto:
- Nosotros, permitimos la personalización de aspectos clave de la estrategia de negociación mediante la definición de parámetros de entrada. Proporcionamos opciones para ajustar los periodos de las medias rápidas y lentas, establecer las dimensiones y colores de los rectángulos de riesgo y recompensa, determinar el número máximo de barras a procesar y elegir si se rellenan los rectángulos con color. Esta configuración permite que el script se adapte a diferentes estrategias de negociación según las preferencias del usuario y que formó la sección de parámetros de entrada de nuestro programa:
//---- Input Parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; input double RewardHeightPoints = 15000.0; input color RiskColor = clrIndianRed; input color RewardColor = clrSpringGreen; input int MaxBars = 500; input int RectangleWidth = 10; input bool FillRectangles = true; input int FillTransparency = 128;
- En la función (OnStart), nos aseguramos de que el gráfico se mantuviera limpio programando el script para que borrara primero cualquier rectángulo de riesgo-recompensa y líneas de precio existentes. A continuación, hicimos que calculara las medias rápidas y lentas mediante la función (iMA) y almacenamos estos valores en matrices para su posterior procesamiento. A medida que el script recorre las barras del gráfico, establecemos las condiciones para detectar cruces alcistas, en los que la media rápida cruza por encima de la lenta. Cuando se cumplen estas condiciones, el script calcula el precio de entrada, el nivel de riesgo (Stop Loss) y el nivel de recompensa (Take Profit). A continuación, dibuja rectángulos y líneas de precios en el gráfico, marcando eficazmente estos niveles críticos de negociación, como explicaremos más adelante en fragmentos de subcódigo:
//----Onstart Function void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; }
- Para mantener la claridad en el gráfico, hemos desarrollado la función "Delete Existing Objects", que el script utiliza para eliminar cualquier objeto previamente dibujado relacionado con las señales de negociación. Al comprobar los nombres de todos los objetos del gráfico, el script garantizaba que sólo se mostrara la información más reciente y relevante, manteniendo el gráfico centrado y libre de desorden:
//---- DeleteAllExistingObjects Function void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } }
- En la función "Draw Rectangle", nos aseguramos de que el script pudiera representar visualmente los niveles de riesgo y recompensa eliminando primero cualquier rectángulo existente con el mismo nombre para evitar la duplicación. A continuación, hicimos que el script calculara las horas de inicio y fin de los rectángulos en función de los índices de las barras, y establecimos cuidadosamente los colores y los niveles de transparencia. De este modo, los rectángulos destacan en el gráfico sin ocultar otros detalles importantes:
///---Draw rectangle function void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); int alphaValue = FillTransparency; color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); } }
- Por último, implementamos la función "Draw Price Line" para indicar al script que añada líneas horizontales en los niveles de entrada, Stop Loss y Take Profit. El script extendía estas líneas por el gráfico y añadía etiquetas de texto que mostraban los niveles de precios correspondientes. Esto proporcionó una referencia visual, permitiendo a los usuarios identificar y gestionar rápidamente los puntos clave para las operaciones basadas en las señales generadas por las medias móviles:
///---- Draw Price Lines Function void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_BACK, true); }
Ahora como parte del kit de trading, este script puede ser lanzado regularmente para ver los niveles de trading visualizados, del pasado y del tiempo actual. Ahora pasamos a crear nuestro Asesor Experto exclusivo. Me centraré en explicar todo el desarrollo hasta el EA de trabajo final. En este artículo, sólo nos centraremos en hacerlo funcionar junto con el indicador que hicimos anteriormente, Trend Constraint V1.09.
Crear un Asesor Experto que funcione en base a un indicador:
Para crear un asesor experto (EA) en MQL5 utilizando un indicador personalizado tenemos que asegurarnos de que el indicador personalizado, archivo (.ex5) está disponible en la carpeta "Indicators" dentro de la plataforma MetaTrader 5, en este caso es Trend Constraint V1.09. Utilizando MetaEditor podemos escribir nuestro EA, incorporando funciones MQL5 para acceder a los valores del buffer del indicador. Utilizamos la función (iCustom()) para llamar al indicador personalizado dentro del EA, especificando los parámetros necesarios, como el símbolo y el marco temporal.
Para extraer datos de los búferes de los indicadores, utilizamos la función (CopyBuffer()), que recupera los valores de los búferes que se pretenden analizar para las señales de negociación. A continuación, aplicamos nuestra lógica de negociación basándonos en estos valores del búfer, definiendo condiciones para abrir, cerrar o modificar órdenes según nuestra estrategia. Integre funciones de gestión de riesgos, como Stop Loss y Take Profit, para una gestión prudente de las operaciones. Sobre la prueba retrospectiva del EA utilizando el Probador de estrategias de MetaTrader 5 para evaluar su rendimiento y ajustar sus parámetros. Por último, verificaremos la funcionalidad del EA en un entorno de cuenta demo antes de pasar al trading en vivo para garantizar que funcione de manera efectiva en condiciones reales del mercado.
Podemos comenzar lanzando una plantilla de Asesor Experto en MetaEditor y luego desarrollarla o modificarla según el ejemplo que se muestra en esta imagen:

Cómo lanzar una plantilla de Asesor Experto en MetaEditor.
Para guiarlo a través de la construcción de nuestro Asesor Experto (EA), dividiremos el proceso en seis etapas. A medida que avanzamos, recomiendo escribir los fragmentos de código directamente en MetaEditor. Este enfoque práctico le ayudará a comprender e internalizar mejor los pasos, especialmente si es nuevo en el desarrollo de EA.
1. Encabezado y metadatos
En la sección de encabezado, definimos el nombre y el propósito de nuestro Asesor Experto (EA). Al incluir información de derechos de autor, un enlace a nuestro perfil y especificar la versión, garantizamos que nuestro EA sea fácilmente identificable y rastreable. Estos metadatos nos ayudan a nosotros y a otros a comprender el origen y el propósito del EA, especialmente cuando se comparte o modifica:
//You can replace the author details with yours. //+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
2. Parámetros de entrada
Aquí, definimos los parámetros de entrada clave que nos permiten personalizar el comportamiento del EA sin alterar el código en sí. Al establecer parámetros como "Lots, Slippage, Stop Loss, Take Profit, and Magic Number" (Lotes, Slippage, Stop Loss, Take Profit y Número mágico), hacemos que el EA sea flexible y adaptable a diferentes estrategias comerciales. El número mágico es particularmente importante, ya que nos permite identificar de forma única las operaciones realizadas por este EA, lo cual es crucial cuando hay múltiples EAs o operaciones manuales involucradas:
///----------- EA inputs parameters for customizations input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders
3. Función de inicialización (OnInit)
En la función OnInit, preparamos el escenario para el funcionamiento del EA inicializando los componentes necesarios. Comenzamos intentando obtener un identificador para nuestro indicador personalizado, "Trend Constraint V1.09". Este identificador nos permite interactuar con el indicador de manera programática. Si se obtiene correctamente el identificador, procedemos a configurar las matrices de búfer (Buffer6 y Buffer7) en serie, lo que nos permitirá almacenar y manipular los valores del indicador. Sin embargo, si no se puede recuperar el identificador, nos aseguramos de que el EA devuelva un error de inicialización, con un mensaje de error para ayudarnos a diagnosticar el problema:
////-------Initialization Function int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); }
4. Función de desinicialización (OnDeinit)
Cuando nuestro EA se elimina del gráfico o cuando se cierra la plataforma, se ejecuta la función (OnDeinit). Aquí, nos encargamos de liberar el controlador del indicador, asegurándonos de liberar todos los recursos que se asignaron durante la operación del EA. Este paso de limpieza es crucial para mantener la eficiencia y la estabilidad de nuestro entorno comercial, ya que evita el consumo innecesario de recursos:
///------Deinitialization Function(OnDeinit) void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); }
5. Función de ejecución principal (OnTick)
En la función (OnTick) es donde ocurre la verdadera acción de negociación. Cada vez que se recibe un nuevo tick de mercado, se llama a esta función. Comenzamos comprobando si ya existe una posición abierta con el mismo número mágico, asegurándonos de no realizar operaciones duplicadas. A continuación, copiamos los últimos valores de los búferes de los indicadores (Buffer6 y Buffer7) para tomar decisiones de negociación. Si se cumplen nuestras condiciones para una señal de compra o venta, elaboramos y enviamos la solicitud de operación correspondiente. Nos encargamos de especificar todos los parámetros necesarios, como el tipo de orden, el precio, el Stop Loss, el Take Profit y el deslizamiento (Slippage), para ejecutar nuestra estrategia de negociación con eficacia:
///---Main Execution Function(OnTick) void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } }
6. Otras funciones
También incluimos varias otras funciones para manejar diferentes eventos que nuestro EA podría encontrar, y vienen como parte de una plantilla de EA y actualmente no las usamos por simplicidad:
- (OnTrade): Aquí podemos gestionar cualquier acción específica que deba realizarse cuando ocurre un evento comercial. Aunque actualmente está vacía, esta función nos proporciona un espacio para agregar lógica si es necesario.
- (OnTester): Esta función se utiliza para cálculos personalizados durante las pruebas retrospectivas. Al devolver un valor, podemos optimizar nuestra estrategia en función de métricas específicas.
- (OnTesterInit, OnTesterPass, OnTesterDeinit): Estas funciones están involucradas en el proceso de optimización dentro del probador de estrategias. Nos permiten inicializar la configuración, realizar acciones después de cada pasada de optimización y limpiar los recursos después.
- (OnChartEvent): Esta función nos permite manejar varios eventos del gráfico, como clics del ratón o pulsaciones de teclas. Aunque actualmente está vacío, podemos utilizar este espacio para añadir funciones interactivas a nuestro EA:
///----Other Template functions available void OnTrade() { //--- Handle trade events if necessary } double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } void OnTesterInit() { //--- Initialization for the strategy tester } void OnTesterPass() { //--- Code executed after each pass in optimization } void OnTesterDeinit() { //--- Cleanup after tester runs } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here }
Nuestro programa final queda así:
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09" //--- Input parameters for the EA input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders //--- Indicator handle int indicator_handle; double Buffer6[]; double Buffer7[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } } //+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- Handle trade events if necessary } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } //+------------------------------------------------------------------+ //| TesterInit function | //+------------------------------------------------------------------+ void OnTesterInit() { //--- Initialization for the strategy tester } //+------------------------------------------------------------------+ //| TesterPass function | //+------------------------------------------------------------------+ void OnTesterPass() { //--- Code executed after each pass in optimization } //+------------------------------------------------------------------+ //| TesterDeinit function | //+------------------------------------------------------------------+ void OnTesterDeinit() { //--- Cleanup after tester runs } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here } //+------------------------------------------------------------------+
En el siguiente segmento después de una compilación exitosa vamos a probar nuestro programa.
Pruebas:
En MetaEditor, podemos usar el botón "Compilar" o "Ejecutar" para preparar nuestro programa para la prueba. En este caso, la compilación fue exitosa y lanzamos la prueba en el índice Boom 500 utilizando el Probador de estrategias.

Iniciar el Probador de estrategias desde el navegador.
Se abre un panel de Probador de estrategias que le permite ajustar algunas configuraciones antes de hacer clic en el botón "Iniciar" en la esquina inferior derecha. Por ejemplo, el tamaño de lote predeterminado en el EA está establecido en 0,1 lotes, pero para el índice Boom 500, tuve que aumentarlo a un mínimo de 0,2 lotes en este caso.

Panel del Probador de estrategias.
Sorprendentemente, nuestro sistema funcionó bien en el Probador de estrategias, como se muestra en la siguiente imagen:

Visualización del Probador de estrategias: Trend Constraint EA.
Conclusión
La adición de rectángulos de riesgo y recompensa proporciona a los operadores una representación gráfica clara de sus operaciones, lo que facilita el seguimiento y la gestión de las posiciones abiertas. Esta ayuda visual es especialmente útil en mercados de rápido movimiento, donde son necesarias decisiones rápidas. Los rectángulos sirven como un recordatorio constante de los resultados potenciales del comercio, ayudando a los operadores a mantenerse alineados con su plan comercial original.
La exitosa colaboración entre el indicador (Trend Constraint V1.09) y el Asesor Experto resalta la importancia de la sinergia entre herramientas en una estrategia comercial. El indicador identifica tendencias y reversiones potenciales, mientras que el EA ejecuta operaciones y gestiona el riesgo basándose en esta información. Este enfoque integrado conduce a una estrategia comercial más cohesiva y efectiva.
A continuación se adjuntan el indicador, el script y el EA utilizados. Todavía hay margen de mejora y modificación. Espero que esta información le resulte valiosa. Eres bienvenido a compartir tus ideas en la sección de comentarios. ¡Feliz trading!
| Archivo adjunto | Descripción |
|---|---|
| (Trend_Constraint V1.09.mq5) | Código fuente para que el indicador funcione con el EA. |
| (Trend Constraint R-R.mq5) | Código fuente para el script de rectángulo de riesgo-recompensa. |
| (Trend Constraint Expert.mq5) | Código fuente para el Asesor Experto que trabaja estrictamente con (Trend Constraint V1.09) |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15321
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Monitoreo de transacciones usando notificaciones push: ejemplo de un servicio en MetaTrader 5
Algoritmo de optimización del comportamiento social adaptativo (ASBO): — Adaptive Social Behavior Optimization (ASBO): Evolución en dos fases
Integración en MQL5: Python
Reimaginando las estrategias clásicas (Parte IV): SP500 y bonos del Tesoro de EE.UU.
- 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
Hola @argatafx28