English Русский Deutsch 日本語
preview
De novato a experto: Noticias animadas utilizando MQL5 (IX) Gestión de múltiples símbolos en un único gráfico para el trading de noticias

De novato a experto: Noticias animadas utilizando MQL5 (IX) Gestión de múltiples símbolos en un único gráfico para el trading de noticias

MetaTrader 5Ejemplos |
56 2
Clemence Benjamin
Clemence Benjamin

Contenido


Introducción

En períodos de gran volatilidad —como cuando se publican noticias económicas—, los operadores suelen apostar por rupturas del precio, ya que la reacción inmediata del mercado es impredecible. Cuando se produce una noticia importante, el precio suele dispararse bruscamente, a lo que suelen seguir correcciones y posibles continuaciones de la tendencia. En estas circunstancias, es posible que los operadores deseen operar con varios instrumentos a la vez, pero esto resulta difícil de conseguir con la configuración predeterminada de MetaTrader 5. Por diseño, un gráfico solo admite un asesor experto, lo que significa que los operadores deben abrir varios gráficos y asignar un asesor experto distinto a cada símbolo.

En este artículo, presentamos una solución a esta limitación: una funcionalidad de trading multisímbolo integrada en el EA «News Headline». Gracias a esta mejora, los operadores pueden gestionar varios pares desde un único gráfico mediante botones de negociación intuitivos. Analizaremos cómo el potencial de MQL5 —aprovechando tanto la biblioteca estándar como las clases de trading personalizadas— permite crear un EA sofisticado capaz de gestionar múltiples símbolos sin problemas en un solo gráfico.

Un asesor experto por cada par.

Figura 1: Solo se permite un EA por gráfico en la terminal MetaTrader 5.

La imagen superior ilustra la limitación de la configuración de MetaTrader 5, donde solo se puede ejecutar un Asesor Experto (EA) en un único gráfico. Para operar con múltiples símbolos de manera efectiva, necesitamos un EA sofisticado capaz de gestionar tanto el par de divisas actual como otros pares simultáneamente, incluso operando desde un solo gráfico.

Al final del artículo, nuestro objetivo es lograr lo siguiente:

  • Desarrollar un EA más sofisticado.
  • Ampliar una clase existente definida en un archivo de cabecera de MQL5.
  • Aprovechar la biblioteca estándar de MQL5 para crear nuevas clases.
  • Integrar nueva funcionalidad en un EA existente.
  • Aplicar la modularización y la agrupación estructurada de las entradas.


Comprender el concepto

Esta etapa comienza con una breve revisión de nuestro trabajo anterior. Comenzamos con un EA sencillo con titulares de noticias animados que obtenía datos del Calendario Económico y de API de noticias externas como Alpha Vantage. Con el tiempo, integramos modelos de IA alojados localmente, estrategias automatizadas de negociación basadas en noticias y botones de negociación manual para mejorar la fiabilidad del EA.

Si bien estas innovaciones mejoraron el sistema, no constituían una solución completa. El trading algorítmico sigue evolucionando y, con cada avance tecnológico, surgen nuevos desafíos que nos impulsan a actualizar nuestros sistemas. Hoy abordaremos uno de esos desafíos: habilitar el trading con múltiples pares de divisas dentro del mismo Asesor Experto (EA).

¿Por qué es necesario?

Una pregunta válida que uno podría hacerse es: ¿por qué necesitamos esta función?

Durante eventos de alta volatilidad, como la publicación de noticias económicas, los operadores deben reaccionar rápidamente, gestionando a menudo múltiples posiciones y símbolos en cuestión de segundos. Este avance proporciona una ventaja crucial al fusionar el trading algorítmico y manual en un solo lugar, mejorando la eficiencia y el control. Con un solo clic, un operador puede abrir operaciones en varios símbolos y gestionar múltiples posiciones simultáneamente, lo que le permite ganar en velocidad y eficiencia.

Proceso de integración de la funcionalidad

Teniendo esto en cuenta, vamos a describir brevemente cómo se añadirá la nueva función. Para ampliar nuestro EA, nos apoyamos en la inclusión de archivos de cabecera y clases de botones de negociación personalizadas, lo que mantiene el código base principal limpio y modular.

Para operar con múltiples símbolos, necesitamos la capacidad de seleccionar los pares deseados que se ejecutarán junto con el par que aparece en el gráfico actual cuando se pulsen los botones de negociación manual. Para lograr esto, utilizaremos las clases CCheckBox y CLabel de la biblioteca estándar MQL5. Estos componentes nos permitirán mostrar pares seleccionables, gestionar la entrada del usuario y vincular las selecciones directamente a los controladores de eventos de los botones.

Finalmente, nuestra clase CTradingButtons se ampliará para incorporar estas nuevas funciones sin problemas.


Implementación

Abordaremos esto en dos etapas principales. En primer lugar, modificaremos la clase CTradingButtons en el archivo de encabezado TradingButtons para implementar las funciones de negociación de múltiples símbolos descritas en nuestro diseño. La segunda fase se centrará en adaptar el EA «News Headline» para que sea compatible con estas nuevas funcionalidades.

Síguenos atentamente mientras desglosamos el código y explicamos cómo cada parte contribuye a dar vida a la idea. Para mayor claridad, cada sección del código y su explicación estarán numeradas secuencialmente de arriba abajo, haciendo hincapié en la nueva funcionalidad.

Si quieres profundizar en los aspectos fundamentales del código, te animo a que vuelvas a consultar las publicaciones anteriores de esta serie, en las que tratamos las versiones iniciales con detalle.


Modificación de la clase CtradingButtons para el trading con múltiples símbolos

Ya presentamos este encabezado en el artículo anterior, al que puedes acudir para mayor claridad. En esta sección, lo ampliamos con una nueva función.

Resumen general

Esta clase (CTradingButtons) agrupa tres funciones, por lo que actúa como un módulo de negociación multisímbolo compacto que se puede integrar en un EA: (1) una interfaz de usuario (lienzo + botones + casillas de selección creadas dinámicamente para los símbolos), (2) una pequeña capa auxiliar de trading (una instancia de CTrade que envía órdenes), y (3) un motor de resolución de símbolos y multisímbolo (asigna los nombres base solicitados, como EURUSD, a los símbolos del bróker y aplica acciones a todos los símbolos seleccionados). El diseño de alto nivel mantiene la alineación de índices entre las matrices: la lista solicitada (lo que pasa el EA), los símbolos de los brókers resueltos (lo que realmente negocia la plataforma) y las casillas de selección (lo que activa o desactiva el usuario); el índice i representa el mismo par en todas las matrices. Esto hace que la conexión entre la interfaz de usuario y las matrices de selección de EA sea sencilla y predecible. 

// top-of-file: class skeleton + key members (from TradingButtons.mqh)

class CTradingButtons

{

private:

   // UI & buttons
   CButton btnMultiToggle;
   CButton btnBuy, btnSell, btnCloseAll, btnDeleteOrders, btnCloseProfit, btnCloseLoss, btnBuyStop, btnSellStop;
   CCanvas buttonPanel;

   // trading

   CTrade  trade;

   // multipair UI & resolution

   CCheckBox *pairChecks[];      // dynamic checkbox pointers, index-aligned with requested list
   string     availablePairs[];  // resolved broker symbols (index-aligned)
   string     resolvedBases[];   // original requested bases (for logging)
   bool       multiEnabled;
public:

   double LotSize;
   int    StopLoss;
   int    TakeProfit;
   int    StopOrderDistancePips;
   double RiskRewardRatio;
   CTradingButtons() { /* default init inlined in full file */ }
   void Init();
   void Deinit();
   // ... other methods follow

};

Campos y constructor: lo que almacena la clase

La clase almacena los parámetros de diseño (ancho, alto y espaciado de los botones), el tamaño y las coordenadas iniciales de las casillas de selección, la matriz dinámica de casillas de selección, las matrices de símbolos resueltos y la configuración de operaciones (tamaño del lote, stops, relación riesgo/recompensa). Su constructor establece unos valores predeterminados razonables (por ejemplo, LotSize=0,01, StopLoss=50, TakeProfit=100, multiEnabled=true), de modo que el EA pueda comenzar con una configuración operativa y modificar lo que sea necesario más adelante. Mantener estos campos como públicos (para los parámetros de trading) y privados (para los componentes internos de la interfaz de usuario) permite que la interfaz sea sencilla y segura. 

// constructor + key field defaults (actual defaults in your file)

CTradingButtons() :
   buttonWidth(100), buttonHeight(30), buttonSpacing(10),
   checkWidth(120), checkHeight(20), checkSpacing(6), checkStartX(10),
   LotSize(0.01), StopLoss(50), TakeProfit(100),
   StopOrderDistancePips(8), RiskRewardRatio(2.0),
   multiEnabled(true)

{

   // constructor body intentionally minimal — Init() performs heavier setup

}

Inicialización y limpieza

La función `Init()` configura la capa auxiliar de ejecución de órdenes de CTrade (número mágico, desviación) y crea la interfaz de usuario (panel, botones, conmutador múltiple). La función `Deinit()` destruye cuidadosamente todos los objetos dinámicos (casillas de selección, botones, lienzos) y libera las matrices para evitar que queden objetos de gráficos huérfanos o se produzcan fugas de memoria. La rutina de limpieza recorre la matriz pairChecks[] y llama a Destroy() y a delete en los punteros dinámicos; a continuación, libera la matriz, lo cual es fundamental cuando se ejecuta y se cierra el EA repetidamente durante el desarrollo. 

// Init & Deinit excerpt

void Init()

{
   trade.SetExpertMagicNumber(123456);
   trade.SetDeviationInPoints(10);
   CreateButtonPanel();
   CreateButtons();
   CreateMultiToggle();
   UpdateMultiToggleVisual();

}

void Deinit()

{
   // destroy checkboxes
   for(int i = 0; i < ArraySize(pairChecks); i++)

   {
      if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
         pairChecks[i].Destroy();
         delete pairChecks[i];
      }
   }
   ArrayFree(pairChecks);

   // destroy buttons and panel
   btnMultiToggle.Destroy();
   btnBuy.Destroy();
   btnSell.Destroy();
   btnCloseAll.Destroy();
   btnDeleteOrders.Destroy();
   btnCloseProfit.Destroy();
   btnCloseLoss.Destroy();
   btnBuyStop.Destroy();
   btnSellStop.Destroy();
   buttonPanel.Destroy();
   ObjectDelete(0, "ButtonPanel");

}

Resolución de símbolos (importante para multisímbolo)

Para operar con un nombre «fácil de recordar» como EURUSD, el EA debe asignarle la cadena de símbolo exacta del bróker (que podría ser EURUSD, EURUSD.ecn, FX.EURUSD, etc.). ResolveSymbol(base) primero busca una coincidencia exacta (primero prueba una coincidencia exacta). Si eso falla, recorre todos los símbolos de terminal, busca coincidencias que empiecen por y luego las que contengan (dando prioridad a las que empiecen por), y excluye los símbolos desactivados. Este paso de resolución genera las entradas de `availablePairs[i]` que utilizan las rutinas de negociación y las casillas de selección de la interfaz de usuario; es el nexo de unión entre los nombres solicitados por el EA y los símbolos negociables reales del bróker. 

// ResolveSymbol implementation (exact + starts-with + contains search)

string ResolveSymbol(const string base)

{
   if(StringLen(base) == 0) return("");
   // 1) Try exact symbol name first
   string baseName = base;
   if(SymbolInfoInteger(baseName, SYMBOL_SELECT) != 0 || SymbolSelect(baseName, false))
   {
      if(SymbolInfoInteger(baseName, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_DISABLED)

      {
         PrintFormat("ResolveSymbol: exact match found %s", baseName);

         return(baseName);
      }
   }

   // 2) search all terminal symbols

   int total = SymbolsTotal(false);
   string base_u = base; StringToUpper(base_u);
   string firstStarts = ""; string firstContains = "";

   for(int i = 0; i < total; i++)

   {
      string sym = SymbolName(i, false);
      string sym_u = sym; StringToUpper(sym_u);
      if(sym_u == base_u) continue;
      if(SymbolInfoInteger(sym, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED) continue;
      if(StringFind(sym_u, base_u) == 0)
      {
         if(firstStarts == "") firstStarts = sym;
      }
      else if(StringFind(sym_u, base_u) >= 0)

      {
         if(firstContains == "") firstContains = sym;
      }
   }

   if(firstStarts != "") { PrintFormat("ResolveSymbol: resolved %s -> %s (starts-with)", base, firstStarts); return(firstStarts); }
   if(firstContains != "") { PrintFormat("ResolveSymbol: resolved %s -> %s (contains)", base, firstContains); return(firstContains); }
   PrintFormat("ResolveSymbol: no match for %s", base);
   return("");
}

Creación de la interfaz de usuario multisímbolo — CreatePairCheckboxes(...)

CreatePairCheckboxes(inMajorPairs[], inPairSelected[], yPos) es la rutina que: (a) resuelve cada par de divisas solicitado en un símbolo de bróker, (b) comprueba que el símbolo esté presente en Market Watch (SymbolSelect) y sea negociable, y (c) crea dinámicamente un CCheckBox para cada símbolo resuelto, manteniendo la alineación de índices con las matrices del EA. Las entradas sin resolver o desactivadas se conservan como marcadores de posición, de modo que availablePairs[i] se corresponda correctamente con el elemento original de inMajorPairs[i]. El estado inicial de cada casilla de verificación creada se toma de inPairSelected[i], por lo que las matrices de selección de la interfaz de usuario y del EA están sincronizadas desde el principio. 

// CreatePairCheckboxes: resolve requested bases -> create checkboxes aligned by index

void CreatePairCheckboxes(string &inMajorPairs[], bool &inPairSelected[], int yPos)

{
   // cleanup previous

   for(int i = 0; i < ArraySize(pairChecks); i++)

   {
      if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
      {
         pairChecks[i].Destroy();
         delete pairChecks[i];
      }
   }

   ArrayFree(pairChecks);
   ArrayResize(availablePairs, ArraySize(inMajorPairs));
   ArrayResize(resolvedBases, ArraySize(inMajorPairs));
   for(int k = 0; k < ArraySize(availablePairs); k++) { availablePairs[k] = ""; resolvedBases[k] = ""; }
   int count = ArraySize(inMajorPairs);
   if(count == 0) return;

   // Resolve each requested base

   for(int i = 0; i < count; i++)
   {
      string requested = inMajorPairs[i];
      string resolved = ResolveSymbol(requested);
      if(resolved == "")
      {
         PrintFormat("CreatePairCheckboxes: could not resolve %s -> skipping checkbox", requested);
         availablePairs[i] = "";
         resolvedBases[i] = requested;
         continue;
      }
      if(!SymbolSelect(resolved, true))
         PrintFormat("CreatePairCheckboxes: SymbolSelect failed for %s (from %s) Err=%d", resolved, requested, GetLastError());
      if(SymbolInfoInteger(resolved, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
      {
         PrintFormat("CreatePairCheckboxes: resolved symbol %s is disabled (from %s) - skipping", resolved, requested);
         availablePairs[i] = "";
         resolvedBases[i] = requested;
         continue;
      }
      availablePairs[i] = resolved;
      resolvedBases[i] = requested;
   }

   // Create checkbox controls (preserve index alignment)

   ArrayResize(pairChecks, count);
   int xPos = checkStartX;
   int chartW = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int wrapX = chartW - (buttonWidth * 3) - 30;
   for(int i = 0; i < count; i++)
   {
      if(StringLen(availablePairs[i]) == 0) { pairChecks[i] = NULL; continue; }
      pairChecks[i] = new CCheckBox();
      string objName = "Chk_" + availablePairs[i];
      if(!pairChecks[i].Create(ChartID(), objName, 0, xPos, yPos, xPos + checkWidth, yPos + checkHeight))
      {

         PrintFormat("CreatePairCheckboxes: failed to create checkbox %s Err=%d", objName, GetLastError());
         delete pairChecks[i];
         pairChecks[i] = NULL;
         availablePairs[i] = "";
         continue;
      }
      pairChecks[i].Text(" " + availablePairs[i]);
      pairChecks[i].Color(clrBlack);
      bool checked = false;
      if(i < ArraySize(inPairSelected)) checked = inPairSelected[i];
      pairChecks[i].Checked(checked);
      xPos += checkWidth + checkSpacing;
      if(xPos + checkWidth > wrapX) { xPos = checkStartX; yPos += checkHeight + checkSpacing; }
   }

   ChartRedraw();
   PrintFormat("CreatePairCheckboxes: created checkboxes (resolved count=%d)", CountResolvedPairs());
}

Contador/ayuda — CountResolvedPairs()

Los pequeños ayudantes mantienen la legibilidad del código. La función CountResolvedPairs() simplemente cuenta las entradas no vacías del array availablePairs[] y se utiliza para registrar datos o actualizar el texto de la interfaz de usuario. Es una línea de código, pero resulta útil durante la inicialización y la resolución de problemas. 

// Count resolved availablePairs entries

int CountResolvedPairs()
{
   int c = 0;
   for(int i = 0; i < ArraySize(availablePairs); i++)
      if(StringLen(availablePairs[i]) > 0) c++;
   return c;
}

Gestión de eventos — HandleChartEvent(...)

Todos los clics realizados en los objetos del gráfico se canalizan hacia HandleChartEvent. Reconoce tres categorías: (A) clics en casillas de selección (nombres de objetos con el prefijo Chk_ — determina qué símbolo resuelto recibió el clic y sincroniza la matriz inPairSelected[i]), (B) el botón de alternancia múltiple (alterna multiEnabled y actualiza los elementos visuales), y (C) los botones de acción (Comprar/Vender/Cerrar todo/Eliminar pendientes/Colocar stops); cada clic en un botón delega en la operación correspondiente, pasando los pares solicitados por el EA y los indicadores de selección. La función actúa como enrutador entre la interfaz de usuario y el motor, y mantiene sincronizadas las matrices de la interfaz de usuario y del motor. 

// HandleChartEvent: routes object clicks to checkboxes / toggle / actions

void HandleChartEvent(const int id, const string &sparam, string &inMajorPairs[], bool &inPairSelected[])

{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      // Checkbox click handling
      if(StringFind(sparam, "Chk_") == 0)
      {
         for(int i = 0; i < ArraySize(availablePairs); i++)
         {
            if(StringLen(availablePairs[i]) == 0) continue;
            string expected = "Chk_" + availablePairs[i];
            if(expected == sparam)

            {
               if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
               {
                 bool current = pairChecks[i].Checked();
                  if(i < ArraySize(inPairSelected)) inPairSelected[i] = current;
                  else { ArrayResize(inPairSelected, i+1); inPairSelected[i] = current; }
                  PrintFormat("HandleChartEvent: checkbox for %s toggled -> %s", availablePairs[i], current ? "true":"false");
               }
               break;
            }
         }
         return;
      }
      // Multi toggle

      if(sparam == btnMultiToggle.Name())

      {
         multiEnabled = !multiEnabled;
         UpdateMultiToggleVisual();
         PrintFormat("HandleChartEvent: Multi toggle clicked. New multiEnabled=%s", (multiEnabled ? "true":"false"));
         return;
      }

      // Buttons - delegate to command handlers

      if(sparam == btnBuy.Name())        OpenBuyOrder(inMajorPairs, inPairSelected);
      else if(sparam == btnSell.Name()) OpenSellOrder(inMajorPairs, inPairSelected);
      else if(sparam == btnCloseAll.Name()) CloseAllPositions(inMajorPairs, inPairSelected);
      else if(sparam == btnDeleteOrders.Name()) DeleteAllPendingOrders(inMajorPairs, inPairSelected);
      else if(sparam == btnCloseProfit.Name()) CloseProfitablePositions(inMajorPairs, inPairSelected);
      else if(sparam == btnCloseLoss.Name())   CloseLosingPositions(inMajorPairs, inPairSelected);
      else if(sparam == btnBuyStop.Name())     PlaceBuyStop(inMajorPairs, inPairSelected);
      else if(sparam == btnSellStop.Name())    PlaceSellStop(inMajorPairs, inPairSelected);
   }
}

Herramientas de ayuda para la creación de interfaces de usuario

La creación de la interfaz de usuario se divide en pequeñas funciones auxiliares: CreateButtonPanel() genera un mapa de bits del lienzo (fondo del panel y rectángulo decorativo), CreateButtons() crea instancias de cada botón de acción y les aplica un estilo con fuente, tamaño y posiciones uniformes, y CreateMultiToggle() crea el botón de alternancia situado encima de los botones de acción principales. La función `UpdateMultiToggleVisual()` actualiza el texto y el color del conmutador para indicar si el modo multisímbolo está activo. Estos ayudantes mantienen el código visual separado de la lógica de negocio y facilitan los cambios de estilo. 

// Create panel + buttons + multi-toggle visual helpers

void CreateButtonPanel()

{
   int panelWidthLocal = buttonWidth + 20;
   int panelHeightLocal = (buttonHeight + buttonSpacing) * 9 + buttonSpacing + 40;
   int x = 0, y = 40;

   if(!buttonPanel.CreateBitmap(0, 0, "ButtonPanel", x, y, panelWidthLocal, panelHeightLocal, COLOR_FORMAT_ARGB_NORMALIZE))
   {
      Print("Failed to create button panel: Error=", GetLastError());
      return;
   }
   ObjectSetInteger(0, "ButtonPanel", OBJPROP_ZORDER, 10);
   buttonPanel.FillRectangle(0, 0, panelWidthLocal, panelHeightLocal, ColorToARGB(clrDarkGray, 200));
   buttonPanel.Rectangle(0, 0, panelWidthLocal - 1, panelHeightLocal - 1, ColorToARGB(clrRed, 255));
   buttonPanel.Update(true);
   ChartRedraw(0);
}
void CreateMultiToggle()
{
   int x = 10, y = 120;
   string font = "Calibri"; int fontSize = 8;
   color buttonBgColor = clrBlack;

   if(btnMultiToggle.Create(0, "btnMultiToggle", 0, x, y, x + buttonWidth, y + buttonHeight))
   {

      ObjectSetString(0, "btnMultiToggle", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnMultiToggle", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnMultiToggle", OBJPROP_BGCOLOR, buttonBgColor);
      ObjectSetInteger(0, "btnMultiToggle", OBJPROP_ZORDER, 11);
   }
}

void UpdateMultiToggleVisual()
{
   if(multiEnabled)
   {
      btnMultiToggle.Text("MULTI:ON");
      btnMultiToggle.ColorBackground(clrGreen);
      btnMultiToggle.Color(clrWhite);
   }
   else
   {
      btnMultiToggle.Text("MULTI:OFF");
      btnMultiToggle.ColorBackground(clrRed);
      btnMultiToggle.Color(clrWhite);
   }
}

Funciones auxiliares para el trading

Estos ayudantes encapsulan la mecánica de trades de bajo nivel. PipSize(symbol) devuelve el tamaño en pips (en tu código se utiliza SYMBOL_POINT * 10.0), IsSymbolValid(symbol) comprueba si existen precios de compra y venta, y TradeBuySingle()/TradeSellSingle() verifican la negociabilidad y los límites de lotes, calculan el precio, el SL y el TP utilizando el tamaño en pips, establecen el tipo de ejecución y envían la orden a través de trade.Buy()/trade.Sell(). Estas funciones auxiliares centralizan la lógica de envío de órdenes, de modo que los bucles multisímbolo solo tienen que llamarlas por símbolo. 

// Pip size, validation, and single-symbol trade helpers

double PipSize(string symbol)

{
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   if(point <= 0) { Print("Invalid point size for ", symbol, ": Error=", GetLastError()); return 0; }
   return point * 10.0;
}
bool IsSymbolValid(string symbol)
{
   bool inMarketWatch = SymbolInfoDouble(symbol, SYMBOL_BID) > 0 && SymbolInfoDouble(symbol, SYMBOL_ASK) > 0;
   if(!inMarketWatch) Print("Symbol ", symbol, " invalid: Not in Market Watch or no valid bid/ask price.");
   return inMarketWatch;
}
bool TradeBuySingle(const string symbol)
{
   if(!IsSymbolValid(symbol)) return false;
   long tradeMode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
   if(tradeMode != SYMBOL_TRADE_MODE_FULL) { Print("TradeBuySingle: Skipping ", symbol, ": Trading disabled"); return false; }
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   if(LotSize < minLot || LotSize > maxLot) { Print("TradeBuySingle: invalid lot"); return false; }

   double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double sl = StopLoss > 0 ? price - StopLoss * PipSize(symbol) : 0;
   double tp = TakeProfit > 0 ? price + TakeProfit * PipSize(symbol) : 0;
   trade.SetTypeFillingBySymbol(symbol);
   int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   price = NormalizeDouble(price, digits);
   sl    = NormalizeDouble(sl, digits);
   tp    = NormalizeDouble(tp, digits);

   if(trade.Buy(LotSize, symbol, price, sl, tp))
   { Print("Buy order placed on ", symbol, ": Ticket #", trade.ResultOrder()); return true; }
   else { Print("Buy order failed on ", symbol, ": Retcode=", trade.ResultRetcode()); return false; }
}
bool TradeSellSingle(const string symbol)
{
   if(!IsSymbolValid(symbol)) return false;
   long tradeMode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
   if(tradeMode != SYMBOL_TRADE_MODE_FULL) { Print("TradeSellSingle: Skipping ", symbol, ": Trading disabled"); return false; }

   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   if(LotSize < minLot || LotSize > maxLot) { Print("TradeSellSingle: invalid lot"); return false; }

   double price = SymbolInfoDouble(symbol, SYMBOL_BID);
   double sl = StopLoss > 0 ? price + StopLoss * PipSize(symbol) : 0;
   double tp = TakeProfit > 0 ? price - TakeProfit * PipSize(symbol) : 0;
   trade.SetTypeFillingBySymbol(symbol);

   int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);

   price = NormalizeDouble(price, digits);
   sl    = NormalizeDouble(sl, digits);
   tp    = NormalizeDouble(tp, digits);

   if(trade.Sell(LotSize, symbol, price, sl, tp))
   { Print("Sell order placed on ", symbol, ": Ticket #", trade.ResultOrder()); return true; }
   else { Print("Sell order failed on ", symbol, ": Retcode=", trade.ResultRetcode()); return false; }
}

Operaciones principales: cómo se aplican los comandos multisímbolo

Las operaciones principales (por ejemplo, OpenBuyOrder, OpenSellOrder, CloseAllPositions, PlaceBuyStop, PlaceSellStop) aceptan las matrices inMajorPairs[] e inPairSelected[] proporcionadas por el EA. Cuando multiEnabled es true, las rutinas recorren todos los índices y llaman a las funciones auxiliares de negociación utilizando availablePairs[i] para cada índice seleccionado. Si multiEnabled es false, la rutina solo opera con el símbolo del gráfico. Las funciones OpenBuyOrder y OpenSellOrder también comprueban, mediante casillas de selección, si ya se ha negociado con el símbolo del gráfico y, en caso contrario, vuelven a negociar con dicho símbolo; esto garantiza que se cumplan las expectativas del usuario al cambiar entre los modos centrado en el gráfico y multisímbolo. 

// OpenBuyOrder / OpenSellOrder excerpt (multipair iteration + fallback to chart symbol)

void OpenBuyOrder(string &inMajorPairs[], bool &inPairSelected[])

{
   Print("Starting OpenBuyOrder");
   string chartSym = Symbol();
   if(!multiEnabled)

   {
      PrintFormat("OpenBuyOrder: multipair disabled => trading only chart symbol %s", chartSym);
      if(SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED) { Print("chart symbol not tradeable"); return; }
      TradeBuySingle(chartSym);
      return;
   }
   bool chartTraded = false;
   for(int i = 0; i < ArraySize(inMajorPairs); i++)
   {
      if(i < ArraySize(inPairSelected) && inPairSelected[i] && StringLen(availablePairs[i]) > 0)
      {
         string symbol = availablePairs[i];
         Print("Attempting Buy order on ", symbol, " (requested ", resolvedBases[i], ")");
         if(TradeBuySingle(symbol) && symbol == chartSym) chartTraded = true;
      }
      else
      {
         if(i < ArraySize(inMajorPairs)) Print("Skipping ", inMajorPairs[i], ": Not selected or unresolved");
      }
   }
   if(!chartTraded && SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_DISABLED)
   {
      PrintFormat("OpenBuyOrder: attempting BUY on chart symbol %s", chartSym);
      TradeBuySingle(chartSym);
   }
}
void OpenSellOrder(string &inMajorPairs[], bool &inPairSelected[])

{
   // (same structure as OpenBuyOrder but calling TradeSellSingle)
   // Implementation mirrors the buy flow but uses SELL specifics.
}


Integración de la funcionalidad de trading con múltiples símbolos en el EA «News Headline»

Dónde se instala el sistema multisímbolo (includes y entradas)

El EA incluye en la parte superior del archivo el encabezado de interfaz y trading con soporte multisímbolo, y ofrece una opción para activar o desactivar la función multisímbolo al inicio. Este es el único lugar en el que el EA indica: (a) que utilizará el objeto de interfaz de usuario/negociación multisímbolo externo, y (b) que el usuario puede configurar el modo multisímbolo inicial. Esto hace que la funcionalidad sea opcional desde el inicio y quede claramente expuesta al usuario del EA. 

#include <TradingButtons.mqh>             // header that implements the multipair UI & trading logic.
// ... other includes ...
input bool   EnableMultipair = true;      // initial multipair enabled state

Tenemos que incluir el encabezado y añadir un campo de entrada para que los usuarios puedan seleccionar el comportamiento deseado durante la inicialización.

Dónde se almacenan los datos de pares múltiples (majorPairs y los indicadores de selección)

 El EA define una matriz de cadenas majorPairs[] con los nombres de los pares solicitados y una matriz booleana paralela pairSelected[] que registra qué pares están marcados. Estas dos matrices constituyen el acuerdo entre el EA y el encabezado: el índice i en ambas matrices hace referencia al mismo par de divisas. El encabezado crea casillas de selección y utiliza la matriz booleana para saber qué pares están seleccionados. 

// MULTIPAIR arrays (provided to the header)
// default major pairs (you can edit or later replace with resolved broker symbols)
string majorPairs[] = {"EURUSD","GBPUSD","USDJPY","USDCHF","AUDUSD","USDCAD","NZDUSD"};
bool   pairSelected[];

Mantener una lista de pares sencilla y alineada con el índice, junto con indicadores de selección. Es fácil de leer, se puede pasar por referencia sin problemas y facilita la sincronización.

Inicializar los valores predeterminados de selección y pasar los datos de entrada al encabezado (configuración de OnInit)

Durante la función OnInit(), el EA ajusta el tamaño de «pairSelected» para que coincida con «majorPairs» y establece por defecto el valor de todos los elementos en «true». A continuación, el EA configura los parámetros públicos del EA (tamaño del lote, stops, ajustes de riesgo) e inicializa el encabezado llamando a Init() y SetMultiEnabled(EnableMultipair). Esto garantiza que el header comience en el modo seleccionado por el EA y utilice los mismos parámetros de operación. 

// In OnInit()
ArrayResize(pairSelected, ArraySize(majorPairs));
for(int i = 0; i < ArraySize(pairSelected); i++) pairSelected[i] = true;

// Initialize TradingButtons
buttonsEA.LotSize = ButtonLotSize;
buttonsEA.StopLoss = ButtonStopLoss;
buttonsEA.TakeProfit = ButtonTakeProfit;
buttonsEA.StopOrderDistancePips = StopOrderDistancePips;
buttonsEA.RiskRewardRatio = RiskRewardRatio;
buttonsEA.Init();
buttonsEA.SetMultiEnabled(EnableMultipair); // pass initial multipair state

Sincroniza la configuración antes de inicializar el objeto de interfaz de usuario/negociación: define primero los campos públicos y el modo, y luego llama a Init() para que el encabezado tenga los parámetros de tiempo de ejecución correctos.

Creación de casillas de selección (alineación de la interfaz de usuario) — Llamada a CreatePairCheckboxes

Aquí hacemos que el EA calcule la posición vertical de checkboxY (para que las casillas aparezcan debajo de las bandas de noticias) y llamamos a CreatePairCheckboxes(majorPairs, pairSelected, checkboxY). Esto crea las casillas de selección en el encabezado, manteniendo la alineación del índice con majorPairs. El encabezado también establecerá el estado inicial de cada casilla de verificación a partir de pairSelected[] para que la interfaz de usuario y el EA estén sincronizados. Esto permite que el componente de UI renderice los controles mientras sigue trabajando con los arrays del EA por referencia. Esto mantiene al EA como el estado canónico de qué pares existen y cuáles se seleccionan (el encabezado manipula las mismas matrices).

// create pair checkboxes aligned below the canvas lanes

int checkboxY = InpTopOffset + (InpSeparateLanes ? 8 : 28) * lineH + 6; // adjust +6 px margin if needed

buttonsEA.CreatePairCheckboxes(majorPairs, pairSelected, checkboxY);

Enrutamiento de eventos: reenvío de eventos del gráfico al encabezado

 El EA no implementa por sí mismo la lógica de los clics en los botones, sino que reenvía todos los clics en los objetos del gráfico al encabezado llamando a buttonsEA.HandleChartEvent(...) desde OnChartEvent. Este contrato de una sola llamada simplifica la EA, ya que el encabezado se encarga de la conmutación entre pares, los clics en las casillas de selección y las acciones de los botones de operación manual. 

// OnChartEvent: forward to the header with majorPairs and pairSelected arrays
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   // Header will handle multipair toggle and trade behaviour
   buttonsEA.HandleChartEvent(id, sparam, majorPairs, pairSelected);
}

 Al aplicar un enrutamiento de eventos independiente, el EA actúa como un canalizador de eventos, mientras que el encabezado gestiona de forma clara los eventos de la interfaz de usuario y las decisiones de negociación. De este modo, las responsabilidades quedan claramente delimitadas.

Cómo se activan las operaciones manuales multisímbolo (el papel del encabezado)

El encabezado es el motor de ejecución de las operaciones manuales multiplexadas; basta con proporcionar matrices simples y dejar que el encabezado las recorra y resuelva los símbolos. Esto mantiene el EA ordenado. Cuando un usuario hace clic en un botón de «Comprar/Vender» o activa la opción «multisímbolo», la función `HandleChartEvent` del encabezado utiliza las matrices `majorPairs` y `pairSelected` que se le pasan para decidir dónde actuar; por ejemplo, recorrerá los índices y operará solo en aquellos en los que `pairSelected[i] == true`. El EA solo proporciona las matrices y la configuración; el encabezado se encarga de resolver los símbolos y ejecutar operaciones sobre múltiples símbolos. (Consulte el encabezado para obtener información sobre las funciones de ayuda para operaciones por símbolo y la iteración multisímbolo.) 
// (conceptual) header receives arrays and performs per-index iteration:
// Pseudocode excerpt of header behavior (actual code in TradingButtons.mqh)
for(i = 0; i < ArraySize(majorPairs); i++)
{
   if(i < ArraySize(pairSelected) && pairSelected[i])
   {
      // resolve broker symbol for majorPairs[i]
      // call TradeBuySingle(resolvedSymbol) or TradeSellSingle(...)
   }
}

La lógica de órdenes automatizadas sigue dependiendo del gráfico concreto (cómo se integra la automatización con el uso de varios símbolos)

La colocación automática de órdenes stop previas al evento y las órdenes posteriores al impacto en este EA se aplican al símbolo del gráfico (_Symbol) en lugar de a los pares principales. El sistema manual multisímbolo es independiente: las operaciones manuales multisímbolo (botones) y las operaciones automatizadas basadas en eventos constituyen flujos distintos. Esta separación evita que se generen órdenes automáticas accidentales con varios símbolos, a menos que se amplíe explícitamente la automatización para utilizar «majorPairs». 

// Example: automated BuyStop/SellStop placement uses _Symbol (chart symbol)
if(trade.BuyStop(InpOrderVolume, buyPrice, _Symbol, buySL, buyTP))
   ticketBuyStop = trade.ResultOrder();
if(trade.SellStop(InpOrderVolume, sellPrice, _Symbol, sellSL, sellTP))
   ticketSellStop = trade.ResultOrder();

De lo anterior se desprende que la lección clave es mantener los controles manuales de múltiples pares separados de las estrategias automatizadas específicas de cada gráfico, a menos que se desee deliberadamente que la automatización se aplique a varios símbolos. Una separación clara evita sorpresas.

Modelo de sincronización: EA gestiona los datos, mientras que el encabezado se encarga de la interfaz de usuario y la lógica

El EA define y almacena majorPairs[] y pairSelected[] (el estado oficial). El encabezado lee estos datos (crea controles, actúa sobre los elementos marcados) y vuelve a escribir los cambios (al marcar una casilla se establece pairSelected[i]), ya que las matrices se pasan por referencia. Este modelo de sincronización bidireccional es sencillo y fiable: el EA puede consultar pairSelected[] en cualquier momento (por ejemplo, en OnTimer) y el encabezado lo actualiza cuando el usuario interactúa con él. 

// EA owns arrays; header is given references and updates them when checkboxes are toggled
buttonsEA.CreatePairCheckboxes(majorPairs, pairSelected, checkboxY);
...
// header's HandleChartEvent updates pairSelected[] in-place when checkboxes are clicked

Utilizamos el paso por referencia para el estado compartido en tiempo de ejecución. Requiere pocos recursos y mantiene sincronizadas UI y EA sin necesidad de intercambiar mensajes adicionales.

Consideraciones sobre la ubicación y el diseño visual (recuadros debajo de las franjas de noticias)

El EA calcula el valor de «checkboxY» basándose en las franjas de noticias y las opciones de configuración (InpTopOffset, InpSeparateLanes, lineH), de modo que las casillas de selección de pares múltiples aparezcan visualmente debajo de las franjas de noticias e indicadores. La integración de elementos de la interfaz de usuario procedentes de otros módulos es tanto una tarea de diseño como de lógica: el cálculo dinámico de los desplazamientos hace que la interfaz se adapte si cambian la altura o la posición de los carriles. 

// compute vertical position for checkboxes so they sit below the lanes
int checkboxY = InpTopOffset + (InpSeparateLanes ? 8 : 28) * lineH + 6;
buttonsEA.CreatePairCheckboxes(majorPairs, pairSelected, checkboxY);

 Al combinar lienzos y paneles de interfaz de usuario externos, centraliza los cálculos de diseño en el EA para que ambos sistemas compartan unas reglas de espaciado coherentes.


Pruebas

La implementación del EA en la plataforma MetaTrader 5 dio excelentes resultados. Pude seleccionar los pares con los que quería operar, y respondieron al instante a los botones de negociación. Las órdenes se ejecutaban a velocidad algorítmica y, con un solo clic, podía cerrar todas las posiciones en varios pares, una función de gran valor para el trading basado en noticias y otras estrategias de scalping de alta volatilidad.

La imagen siguiente muestra el resultado del proceso de pruebas del gráfico en tiempo real. Es importante señalar que las funciones manuales requieren una interacción en tiempo real con el gráfico, mientras que los componentes automatizados del EA pueden evaluarse a fondo en el Probador de estrategias para garantizar su eficacia.

Operaciones con múltiples símbolos mediante el EA «News Headline»

Operaciones con múltiples símbolos mediante el EA «News Headline»



Conclusión

La Parte IX supuso otro hito importante en la evolución del EA «News Headline»: la integración del trading con múltiples símbolos. Esta novedad resuelve una limitación que existía desde hacía tiempo, ya que permite a los operadores gestionar varios pares desde un único gráfico. Aunque no se trata de un sistema de negociación totalmente automatizado, esta función actúa como una interfaz de negociación manual basada en la ejecución algorítmica, lo que garantiza rapidez y precisión, al tiempo que deja la toma de decisiones en manos del operador en situaciones de alta volatilidad.

El proceso de desarrollo en sí mismo resultó muy enriquecedor, ya que aplicamos principios de modularización y separación de responsabilidades para crear un sistema compacto pero potente. Lo que comenzó como una simple herramienta para mostrar calendarios y noticias se ha convertido ahora en un marco versátil que combina funciones manuales y automatizadas para resolver los retos prácticos a los que se enfrentan los operadores de noticias. Aunque se ha diseñado pensando en los principales pares de divisas, el sistema puede adaptarse a símbolos personalizados con unos pequeños ajustes de compatibilidad.

Uno de los principales retos con los que nos encontramos fue la nomenclatura de símbolos específica de cada bróker. Por ejemplo, las operaciones fallaron inicialmente porque la cuenta utilizaba EURUSD.0 en lugar del EURUSD estándar. Para solucionar esto, hemos mejorado el EA para que se adapte dinámicamente a la nomenclatura de los pares principales utilizada por los brókers.

Además, hemos aprovechado la clase CCheckBox de la biblioteca estándar de MQL5 para facilitar la selección de pares, lo que pone de manifiesto la flexibilidad y la capacidad de ampliación del lenguaje MQL5.

Espero que este debate haya aportado ideas útiles y lecciones prácticas. A continuación se adjunta el código fuente completo; utilízalo junto con este artículo, en el que se describe la implementación. Para mayor comodidad, también he resumido los puntos clave en una tabla a continuación. Siempre agradecemos vuestras opiniones y comentarios.


Lecciones clave

Lección clave Descripción
Matrices con índices alineados Ambos proyectos utilizan las matrices `majorPairs[]` y `pairSelected[]`, alineadas por índice. De este modo, se garantiza que una casilla de selección, un nombre base solicitado y un símbolo de bróker resuelto hagan referencia al mismo par de divisas de forma coherente.
Sincronización por referencia Las matrices se pasan por referencia desde el EA a TradingButtons.mqh, lo que permite que el encabezado actualice directamente los estados de selección al activar o desactivar las casillas de selección, manteniendo así el estado del EA sincronizado al instante.
Reenvío explícito de eventos El EA no gestiona directamente los clics en los botones. En cambio, OnChartEvent reenvía los eventos a buttonsEA.HandleChartEvent(), donde el encabezado interpreta los cambios de configuración entre pares múltiples, los clics en las casillas de selección y las acciones de negociación.
Abstracción de la resolución de símbolos La función `ResolveSymbol()` del encabezado asigna a los símbolos fáciles de recordar (por ejemplo, EURUSD) los símbolos específicos de cada bróker (por ejemplo, EURUSD.ecn). El EA puede ignorar las peculiaridades de los nombres de los brókers.
Flujos manuales frente a flujos automatizados En el EA, las órdenes automáticas previas y posteriores al evento siempre se aplican al símbolo del gráfico, mientras que la funcionalidad multisímbolo se reserva para las acciones manuales mediante botones. Esta separación evita operaciones masivas inesperadas.
Creación y limpieza dinámicas de la interfaz de usuario El encabezado crea y elimina dinámicamente casillas de selección y botones durante las funciones Init() y Deinit(). El EA calcula los desplazamientos de diseño (debajo de las franjas de noticias) para que los componentes encajen a la perfección en su interfaz de usuario.
Inicializar antes de Init() En OnInit(), el EA establece LotSize, StopLoss, TakeProfit y EnableMultipair antes de llamar a buttonsEA.Init(). Esto garantiza que el encabezado se genere con la configuración correcta.
Herramientas de negociación centralizadas La lógica de negociación está integrada en funciones auxiliares reutilizables como TradeBuySingle() y TradeSellSingle(). De este modo se evita la duplicación de código entre los bucles multisímbolo y los controladores de botones.
Comportamiento del interruptor de varios pares El botón btnMultiToggle permite alternar entre los modos de un solo símbolo y de varios pares. En el modo multisímbolo, las acciones se aplican a todos los pares seleccionados; en el modo individual, las acciones se aplican únicamente al símbolo del gráfico.
Recurrir al símbolo del gráfico Si el modo multisímbolo está activado pero no se ha seleccionado el símbolo del gráfico, el encabezado garantiza de todos modos que la operación se realice sobre ese símbolo. Esto ofrece resultados predecibles a los usuarios que se centran en su gráfico.
Coordinación de la maquetación El EA calcula el valor de «checkboxY» para colocar las casillas de selección de varios pares de forma ordenada debajo del área de noticias desplazable. Aquí se explica cómo integrar paneles de interfaz de usuario de terceros con superposiciones de indicadores personalizados sin que se solapen.
Registro de errores y claridad Ambos módulos generan registros detallados (por ejemplo, errores en la resolución de símbolos o códigos de error en la introducción de órdenes). Esta trazabilidad ayuda a los programadores y a los usuarios a diagnosticar rápidamente los problemas de configuración.
Modelo de sincronización bidireccional Los estados de las casillas de selección se inicializan a partir de `pairSelected[]` (del EA a la interfaz de usuario) y, al hacer clic en ellas, se actualiza `pairSelected[]` (de la interfaz de usuario al EA). Este bucle continuo garantiza que ambos módulos compartan el mismo estado de selección.
Uso seguro de la memoria dinámica El encabezado utiliza un nuevo objeto `CCheckBox()` para cada par y los elimina cuidadosamente en `Deinit()`. Esto enseña a los programadores de MQL5 a gestionar los objetos de la interfaz gráfica de usuario de forma segura en los EA de ejecución prolongada.
Extensibilidad gracias a la modularidad Al integrar la negociación con múltiples pares en un encabezado independiente, la misma clase se puede reutilizar en varios EA (como el EA «News Headline») sin necesidad de reescribir la lógica de los múltiples pares, lo que constituye un patrón escalable para la reutilización del código.


Archivos adjuntos

Nombre del archivo Versión Descripción
News_Headline_EA.mq5 1.13 Un asesor experto que integra los eventos del calendario económico y los titulares de las noticias directamente en el gráfico. Gestiona las órdenes de stop previas a los eventos, las operaciones posteriores al impacto y muestra noticias en pantalla. La versión 1.13 amplía su funcionalidad con la compatibilidad con la negociación de múltiples pares a través del módulo TradingButtons, lo que permite la ejecución manual de órdenes de múltiples pares junto con la negociación automatizada basada en eventos de los gráficos.
TradingButtons.mqh 1 Un archivo de encabezado modular que ofrece una interfaz de trading multisímbolo. Crea botones para órdenes de compra, venta, cierre, eliminación y stop, además de casillas de selección para elegir varios pares de divisas. Incluye una lógica de resolución de símbolos, funciones auxiliares para la realización de órdenes y la posibilidad de alternar entre la negociación de un solo símbolo y la de varios pares. Diseñado para su reutilización en diferentes EA, incluido el EA «News Headline».

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

Archivos adjuntos |
TradingButtons.mqh (38.59 KB)
Stanislav Korotky
Stanislav Korotky | 17 sept 2025 en 18:34

¿Cuál es el verdadero sentido de estas cosas? El razonamiento proporcionado es irracional, porque los EAs son capaces de operar con muchos símbolos desde cualquier gráfico por diseño (out of the box), y puedes cambiar fácilmente de gráfico entre símbolos sin afectar a los EAs - no se recargan cuando se cambia el símbolo/marco temporal del gráfico.

PS. El comentario original está publicado en Inglés - por favor, léalo para una correcta comprensión - autotraducción puede producir textos ridículos.

Clemence Benjamin
Clemence Benjamin | 6 oct 2025 en 06:02
Stanislav Korotky #:

¿Cuál es el verdadero sentido de estas cosas? El razonamiento proporcionado es irracional, porque los EAs son capaces de operar con muchos símbolos desde cualquier gráfico por diseño (out of the box), y se puede cambiar fácilmente de gráfico entre símbolos sin afectar a los EAs - no se recargan cuando se cambia el símbolo/marco de tiempo del gráfico.

PS. El comentario original está publicado en Inglés - por favor, léalo para una correcta comprensión - autotraducción puede producir textos ridículos.

Hola Stanislav Korotky,

Gracias por compartir su perspectiva. Entiendo completamente tu punto de vista - de hecho, un EA puede operar con múltiples símbolos desde cualquier gráfico, y cambiar de símbolo manualmente no recarga o interrumpe la ejecución del EA.

Sin embargo, mi idea se dirige específicamente a situaciones en las que se requiere la ejecución simultánea a través de múltiples pares - por ejemplo, durante los eventos de noticias de alto impacto cuando es posible que desee colocar órdenes sincronizadas en GBPUSD y EURUSD en el mismo momento exacto. En estos casos, el cambio manual de símbolos no resulta práctico.

Es por eso que hago hincapié en la gestión programática de múltiples símbolos - asegurando que el EA puede manejar y ejecutar operaciones a través de pares seleccionados de forma automática, incluso si está conectado a un gráfico con un símbolo base diferente.

Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Motor de decisión Multi-IA para MQL5 (Parte 1): Integrar múltiples IA con votación por consenso Motor de decisión Multi-IA para MQL5 (Parte 1): Integrar múltiples IA con votación por consenso
Presentamos una arquitectura en la que el EA habla con un AI Manager que consulta a OpenAI, Claude, Gemini y DeepSeek, parsea sus JSON y los convierte a un estándar AIResponse. Con un prompt común y un sistema de votación ponderada con quórum, se obtiene una señal final COMPRAR/VENDER/MANTENER. Incluye gestión de errores, temporizador y un EA mínimo para su integración práctica.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Formulación de un Asesor Experto Multipar Dinámico (Parte 4): Ajuste de volatilidad y riesgo Formulación de un Asesor Experto Multipar Dinámico (Parte 4): Ajuste de volatilidad y riesgo
Esta fase permite ajustar con precisión tu EA multipar para adaptar el tamaño de las operaciones y el riesgo en tiempo real utilizando indicadores de volatilidad como el ATR, lo que mejora la consistencia, la protección y el rendimiento en diversas condiciones de mercado.