Abrir y cerrar gráficos

Un programa MQL no sólo puede analizar la lista de gráficos, sino también modificarla: abrir nuevos o cerrar los existentes. Se asignan dos funciones a estos efectos: ChartOpen y ChartClose.

long ChartOpen(const string symbol, ENUM_TIMEFRAMES timeframe)

La función abre un nuevo gráfico con el símbolo y el marco temporal especificados y devuelve el ID del nuevo gráfico. Si se produce un error durante la ejecución, el resultado es 0 y el código de error puede leerse en la variable integrada _LastError.

Si el parámetro symbol es NULL, significa el símbolo del gráfico actual (en el que se está ejecutando el programa MQL). El valor 0 del parámetro timeframe corresponde a PERIOD_CURRENT.

El número máximo posible de gráficos abiertos simultáneamente en el terminal no puede superar CHARTS_MAX (100).

Veremos un ejemplo de uso de la función ChartOpen en la próxima sección, después de estudiar las funciones para trabajar con plantillas tpl.

Tenga en cuenta que el terminal le permite crear no sólo ventanas completas con gráficos, sino también objetos gráficos. Se colocan dentro de los gráficos normales del mismo modo que otros objetos gráficos como líneas de tendencia, canales, etiquetas de precios, etc. Los objetos gráficos permiten mostrar dentro de un gráfico estándar varios fragmentos pequeños de series de precios para símbolos y marcos temporales alternativos.

bool ChartClose(long chartId = 0)

La función cierra el gráfico con el ID especificado (el valor por defecto 0 significa el gráfico actual). La función devuelve un indicador de éxito.

Como ejemplo, vamos a implementar el script ChartCloseIdle.mq5, que cerrará los gráficos duplicados con combinaciones repetidas de símbolo y marco temporal si no contienen programas MQL y objetos gráficos.

En primer lugar, tenemos que hacer una lista que cuente los gráficos de un determinado par símbolo/marco temporal. Esta tarea se implementa mediante la función ChartIdleList, que es muy similar a la que vimos en el script ChartList.mq5. La propia lista se forma en el array del mapa MapArray<string,int> chartCounts.

#include <MQL5Book/Periods.mqh>
#include <MQL5Book/MapArray.mqh>
   
#define PUSH(A,V) (A[ArrayResize(AArraySize(A) + 1) - 1] = V)
   
void OnStart()
{
   MapArray<string,intchartCounts;
   ulong duplicateChartIDs[];
   // collect duplicate empty charts
   if(ChartIdleList(chartCountsduplicateChartIDs))
   {
      ...
   }
   else
   {
      Print("No idle charts.");
   }
}

Mientras tanto, la función ChartIdleList rellena el array duplicateChartIDs con identificadores de gráficos libres que coinciden con las condiciones de cierre.

int ChartIdleList(MapArray<string,int> &mapulong &duplicateChartIDs[])
{
   // list charts until their list ends
   for(long id = ChartFirst(); id != -1id = ChartNext(id))
   {
      // skip objects
      if(ChartGetInteger(idCHART_IS_OBJECT)) continue;
   
      // getting the main properties of the chart
      const int win = (int)ChartGetInteger(idCHART_WINDOWS_TOTAL);
      const string expert = ChartGetString(idCHART_EXPERT_NAME);
      const string script = ChartGetString(idCHART_SCRIPT_NAME);
      const int objectCount = ObjectsTotal(id);
   
      // count the number of indicators
      int indicators = 0;
      for(int i = 0i < win; ++i)      
      {
         indicators += ChartIndicatorsTotal(idi);
      }
      
      const string key = ChartSymbol(id) + "/" + PeriodToString(ChartPeriod(id));
      
      if(map[key] == 0     // the first time we always read a new symbol/TF combination
                           // otherwise, only empty charts are counted:
         || (indicators == 0           // no indicators
            && StringLen(expert) == 0  // no Expert Advisor
            && StringLen(script) == 0  // no script
            && objectCount == 0))      // no objects
      {
         const int i = map.inc(key);
         if(map[i] > 1)                // duplicate
         {
            PUSH(duplicateChartIDsid);
         }
      }
   }
   return map.getSize();
}

Una vez formada la lista para borrar, en OnStart llamamos a la función ChartClose en un bucle sobre la lista.

void OnStart()
{
   ...
   if(ChartIdleList(chartCountsduplicateChartIDs))
   {
      for(int i = 0i < ArraySize(duplicateChartIDs); ++i)
      {
         const ulong id = duplicateChartIDs[i];
         // request to bring the chart to the front
         ChartSetInteger(idCHART_BRING_TO_TOPtrue);
         // update the state of the windows, pumping the queue with the request
         ChartRedraw(id);
         // ask user for confirmation
         const int button = MessageBox(
            "Remove idle chart: "
            + ChartSymbol(id) + "/" + PeriodToString(ChartPeriod(id)) + "?",
            __FILE__MB_YESNOCANCEL);
         if(button == IDCANCELbreak;   
         if(button == IDYES)
         {
            ChartClose(id);
         }
      }
      ...

Para cada gráfico, primero se llama a la función ChartSetInteger(id, CHART_BRING_TO_TOP, true) para mostrar al usuario qué ventana debe cerrarse. Dado que esta función es asíncrona (sólo pone el comando para activar la ventana en la cola de eventos), es necesario llamar adicionalmente a ChartRedraw, que procesa todos los mensajes acumulados. A continuación, se pide al usuario que confirme la acción. El gráfico sólo se cierra al hacer clic en Yes. Seleccionando No se salta el gráfico actual (lo deja abierto), y el bucle continúa. Pulsando Cancel, puede interrumpir el bucle antes de tiempo.