English Русский 中文 Deutsch 日本語 Português
preview
Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (I)

Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (I)

MetaTrader 5Probador |
299 2
Clemence Benjamin
Clemence Benjamin

Contenido:


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.
  1. El valor de la vela retrospectiva se estableció demasiado alto en 5000 barras de manera predeterminada.
  2. 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.

    Relación riesgo-recompensa extraída para el cruce de medias móviles

    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:

    1. Redujimos el período de retrospección de 5000 barras a 1000 barras, lo que disminuyó significativamente la cantidad de datos a calcular.
    2. 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:

    Lanzamiento del script Trend Constraint R-R

    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:

      Lanzar una plantilla de EA en MetaEditor.

      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.

      Lanzamiento del 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.

      Probador de estrategias.

      Panel del Probador de estrategias.

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

      Repetición de MetaTester.

      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)

      Volver al índice

      Traducción del inglés realizada por MetaQuotes Ltd.
      Artículo original: https://www.mql5.com/en/articles/15321

      Helga Gustana Argita
      Helga Gustana Argita | 14 ago 2024 en 19:50
      hola tengo un error ¿cómo solucionarlo?

      2024.08.15 00:47:15.123 2024.08.01 00:00:00 no se puede cargar el indicador personalizado 'Trend Constraint V1.09' [4802]
      2024.08.15 00:47:15.123 2024.08.01 00:00:00 error de creación de indicador en 'Trend_Constraint_Expert.mq5' (1,1)

      Clemence Benjamin
      Clemence Benjamin | 15 ago 2024 en 22:43
      argatafx28 el indicador personalizado 'Trend Constraint V1.09' [4802]
      2024.08.15 00:47:15.123 2024.08.01 00:00:00 error de creación de indicador en 'Trend_Constraint_Expert.mq5' (1,1)

      Hola @argatafx28

      1. Asegúrese de que el indicador Trend Constraint V1.09 está instalado para que el EA funcione.
      2. Trend Constraint V1.09 utiliza ShellExecute para integrar telegrama, ¿podría asegurarse de permitir DLL en dependencias al lanzar el indicador?


      Activar Permitir DLL

      Por cierto, ¿qué sistema operativo está utilizando?
      Monitoreo de transacciones usando notificaciones push: ejemplo de un servicio en MetaTrader 5 Monitoreo de transacciones usando notificaciones push: ejemplo de un servicio en MetaTrader 5
      En este artículo veremos cómo crear un programa de servicio para enviar notificaciones a un smartphone sobre los resultados comerciales. Asimismo, aprenderemos cómo trabajar con listas de objetos de la biblioteca estándar para organizar una muestra de objetos según las propiedades requeridas.
      Algoritmo de optimización del comportamiento social adaptativo (ASBO): — Adaptive Social Behavior Optimization (ASBO): Evolución en dos fases Algoritmo de optimización del comportamiento social adaptativo (ASBO): — Adaptive Social Behavior Optimization (ASBO): Evolución en dos fases
      Este artículo supone una continuación del tema del comportamiento social de los organismos vivos y su impacto en el desarrollo de un nuevo modelo matemático: el ASBO (Adaptive Social Behavior Optimization). Así, nos sumergiremos en la evolución en dos fases, probaremos el algoritmo y sacaremos conclusiones. Al igual que en la naturaleza un grupo de organismos vivos une sus esfuerzos para sobrevivir, el ASBO utiliza los principios de comportamiento colectivo para resolver problemas de optimización complejos.
      Integración en MQL5: Python Integración en MQL5: Python
      Python es un lenguaje de programación conocido y popular con muchas características, especialmente en los campos de las finanzas, la ciencia de datos, la Inteligencia Artificial y el Aprendizaje Automático. Python es una herramienta poderosa que también puede resultar útil en el trading. MQL5 nos permite utilizar este poderoso lenguaje como una integración para lograr nuestros objetivos de manera efectiva. En este artículo, compartiremos cómo podemos usar Python como una integración en MQL5 después de aprender información básica sobre Python.
      Reimaginando las estrategias clásicas (Parte IV): SP500 y bonos del Tesoro de EE.UU. Reimaginando las estrategias clásicas (Parte IV): SP500 y bonos del Tesoro de EE.UU.
      En esta serie de artículos, analizamos estrategias de trading clásicas utilizando algoritmos modernos para determinar si podemos mejorar la estrategia utilizando IA. En el artículo de hoy, retomamos un enfoque clásico para operar con el SP500 utilizando la relación que guarda con los bonos del Tesoro estadounidense.