English Русский 中文 Deutsch 日本語 Português
preview
Visualización de transacciones en un gráfico (Parte 2): Visualización gráfica de datos

Visualización de transacciones en un gráfico (Parte 2): Visualización gráfica de datos

MetaTrader 5Sistemas comerciales |
207 2
Aleksandr Seredin
Aleksandr Seredin

Introducción

En este artículo, completaremos el script para visualizar operaciones en el gráfico que comenzamos a implementar en «Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis». Escribiremos el código para seleccionar los datos de una única operación seleccionada por el usuario, así como para dibujar los objetos de datos necesarios en el gráfico, que luego guardaremos en un archivo como pantalla de impresión de los gráficos correspondientes. El script nos permitirá ahorrar una cantidad significativa de tiempo en el trabajo técnico relacionado con la formación de gráficos de operaciones, así como en guardarlos en pantallas de impresión para su análisis retrospectivo. Quienes no deseen dedicar tiempo a montar los proyectos pueden descargar una versión ya preparada del script en el Market.


Seleccionar los datos de una operación

A diferencia de la selección de datos sobre operaciones durante un periodo determinado, la selección de datos sobre una única operación simplificará considerablemente la aplicación del caso de selección histórica de pedidos. La principal diferencia aquí es el hecho de que en lugar de la función HistorySelect() predefinida del terminal, vamos a utilizar el método HistorySelectByPosition() para solicitar datos históricos. Los parámetros del método deben recibir POSITION_IDENTIFIER que podemos encontrar en el terminal MetaTrader 5 (Ver -> Caja de Herramientas -> Historial -> Columna "Ticket"). El valor debe pasarse al script a través de la variable global de entrada inp_d_ticket.

En todos los demás aspectos, la lógica del caso Select_one_deal repite completamente la implementación de la lógica del caso anterior, y se presenta en su totalidad en el código siguiente con las mismas inserciones de información para los usuarios.

      //--- if one deal is needed
      case Select_one_deal:

         res = MessageBox("You have selected analysis of one deal. Continue?","",MB_OKCANCEL); // informed in the message

         if(res == IDCANCEL)                                            // if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__);  // informed in the journal
            return;                                                     // interrupted
           }

         MessageBox("Please press 'Ok' and wait for the next message until script will be done."); // informed in the message

         //--- select by one position
         if(HistorySelectByPosition(inp_d_ticket))                      // select position by id
           {
            int total = HistoryDealsTotal();                            // total deals

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); // notify
               MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message
               return;
              }

            for(int i=0; i<total; i++)                                  // iterate through the number of deals
              {
               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the deal number
                 {
                  //--- get deals properties
                  position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID);     // took the main id
                  entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);// entry or exit?

                  if(entry == DEAL_ENTRY_IN)                                        // if this is an entry
                    {
                     open = HistoryDealGetDouble(ticket,DEAL_PRICE);                // take open price
                     time_open  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); // take open time
                     symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);   	    // take symbol
                     stop_loss = HistoryDealGetDouble(ticket,DEAL_SL);  	    // take Stop Loss
                     take_profit = HistoryDealGetDouble(ticket,DEAL_TP);	    // take Take Profit
                     //---
                     magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC);   	    // take Magic
                     comment=HistoryDealGetString(ticket,DEAL_COMMENT);       	    // take comment
                     externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID); 	    // take external id
                     volume = HistoryDealGetDouble(ticket,DEAL_VOLUME);             // take volume
                     commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);     // take commission value
                    }

                  if(entry == DEAL_ENTRY_OUT)                           	    // if this is an exit
                    {
                     close = HistoryDealGetDouble(ticket,DEAL_PRICE);               // take close price
                     time_close  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);// take close time
                     //---
                     reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // take reason
                     swap = HistoryDealGetDouble(ticket,DEAL_SWAP);     // take swap
                     profit = HistoryDealGetDouble(ticket,DEAL_PROFIT); // take profit
                     fee = HistoryDealGetDouble(ticket,DEAL_FEE);       // take fee
                    }


                  //--- enter data into the main storage
                  //--- check if there is such id
                  if(Find(PositionID,position_id)==-1)                         // if there is no such deal,
                    {
                     //--- change the dimensions of the arrays
                     ArrayResize(arr_time_open,ArraySize(arr_time_open)+1);    // open time
                     ArrayResize(arr_time_close,ArraySize(arr_time_close)+1);  // close time
                     ArrayResize(arr_symbol,ArraySize(arr_symbol)+1);          // symbols
                     ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1);    // stop levels
                     ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);// profits
                     ArrayResize(arr_open,ArraySize(arr_open)+1);              // entries
                     ArrayResize(arr_close,ArraySize(arr_close)+1);            // exits
                     ArrayResize(PositionID,ArraySize(PositionID)+1);          // position id
                     //---
                     ArrayResize(arr_magic,ArraySize(arr_magic)+1);            // Magic
                     ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1);  // external id
                     ArrayResize(arr_comment,ArraySize(arr_comment)+1);        // comment
                     ArrayResize(arr_volume,ArraySize(arr_volume)+1);          // volume
                     ArrayResize(arr_commission,ArraySize(arr_commission)+1);  // commission
                     ArrayResize(arr_reason,ArraySize(arr_reason)+1);          // reason
                     ArrayResize(arr_swap,ArraySize(arr_swap)+1);              // swap
                     ArrayResize(arr_profit,ArraySize(arr_profit)+1);          // profit
                     ArrayResize(arr_fee,ArraySize(arr_fee)+1);                // fee


                     PositionID[ArraySize(arr_time_open)-1]=position_id;       // id



                     if(entry == DEAL_ENTRY_IN)                         	       // if this is an entry
                       {
                        arr_time_open[    ArraySize(arr_time_open)-1]   = time_open;   // deal time
                        arr_symbol[       ArraySize(arr_symbol)-1]      = symbol;      // instrument symbol
                        arr_stop_loss[    ArraySize(arr_stop_loss)-1]   = stop_loss;   // deal stop loss
                        arr_take_profit[  ArraySize(arr_take_profit)-1] = take_profit; // deal take profit
                        arr_open[         ArraySize(arr_open)-1]        = open;        // open price
                        //---
                        arr_magic[        ArraySize(arr_magic)-1]       = magic;       // Magic
                        arr_comment[      ArraySize(arr_comment)-1]     = comment;     // comment
                        arr_extermalID[   ArraySize(arr_extermalID)-1]  = externalID;  // external id
                        arr_volume[       ArraySize(arr_volume)-1]      = volume;      // volume
                        arr_commission[   ArraySize(arr_commission)-1]  = commission;  // commission
                       }

                     if(entry == DEAL_ENTRY_OUT)                        	       // if this is an exit
                       {
                        arr_time_close[   ArraySize(arr_time_close)-1]  = time_close;  // close time
                        arr_close[        ArraySize(arr_close)-1]       = close;       // close prices
                        //---
                        arr_reason[       ArraySize(arr_reason)-1]      = reason;      // reason
                        arr_swap[         ArraySize(arr_swap)-1]        = swap;        // swap
                        arr_profit[       ArraySize(arr_profit)-1]      = profit;      // profit
                        arr_fee[          ArraySize(arr_fee)-1]         = fee;         // fee
                       }
                    }
                  else
                    {
                     int index = Find(PositionID,position_id);          // if there was a record already,

                     if(entry == DEAL_ENTRY_IN)                         // if this was an entry
                       {
                        arr_time_open[index]   = time_open;             // deal time
                        arr_symbol[index]      = symbol;                // symbol
                        arr_stop_loss[index]   = stop_loss;             // deal stop loss
                        arr_take_profit[index] = take_profit;           // deal take profit
                        arr_open[index]        = open;                  // open price
                        //---
                        arr_magic[index]       = magic;                 // Magic
                        arr_comment[index]     = comment;               // comment
                        arr_extermalID[index]  = externalID;            // external id
                        arr_volume[index]      = volume;                // volume
                        arr_commission[index]  = commission;            // commission
                       }

                     if(entry == DEAL_ENTRY_OUT)                        // if this is an exit
                       {
                        arr_time_close[index]  = time_close;            // deal close time
                        arr_close[index]       = close;                 // deal close price
                        //---
                        arr_reason[index]      = reason;                // reason
                        arr_swap[index]        = swap;                  // swap
                        arr_profit[index]      = profit;                // profit
                        arr_fee[index]         = fee;                   // fee
                       }
                    }
                 }
              }
           }
         else
           {
            printf("%s - %d -> Error of selecting history deals: %d",__FUNCTION__,__LINE__,GetLastError());	// informed in the journal
            printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); 					// informed in the journal
            MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message
            return;
           }
         break;

Ahora que se han descrito ambas opciones, y que todos los almacenamientos se han llenado con los datos necesarios durante la ejecución del programa, podemos empezar a visualizar estos datos en los gráficos del terminal.


Visualización de los gráficos necesarios

Para guardar operaciones en el gráfico, primero tenemos que abrir una nueva ventana con el símbolo necesario a nivel de programa, realizar los ajustes de diseño necesarios, incluido el desplazamiento individual de la sangría a la derecha para que toda la operación sea claramente visible, y llamar a una función predefinida que guardará una pantalla de impresión en la carpeta necesaria.

En primer lugar, vamos a declarar las variables locales que necesitamos para abrir la ventana del gráfico deseado. La variable 'bars' almacenará el valor de desplazamiento para el gráfico de la derecha, las variables 'chart_width' y 'chart_height' almacenarán los tamaños correspondientes para guardar, y el manejador del nuevo gráfico, cuando se abra, se almacenará en la variable 'handle' para acceder al gráfico en el futuro. 

//--- data collected, moving on to printing
   int bars = -1;                                                       // number of bars in a shift
   int chart_width = -1;                                                // chart width
   int chart_height =-1;                                                // chart height
   long handle =-1;                                                     // chart handle

Antes de iniciar una solicitud para abrir nuevas ventanas de símbolos, debemos solicitar la validez de estos símbolos al historial. Esta comprobación es absolutamente necesaria para evitar el error de abrir un «símbolo inexistente» en la cuenta. Creo que es necesario explicar aquí de dónde puede venir un «símbolo inexistente» si se ha guardado en el historial de operaciones, lo que significa que alguna vez existió.

En primer lugar, esto puede estar relacionado con los tipos de cuenta del corredor. Hoy en día, la mayoría de los corredores ofrecen a los operadores varias opciones de cuenta para que su uso sea lo más rentable y cómodo posible en cuanto a las estrategias de negociación utilizadas. Algunas cuentas cobran una comisión por abrir operaciones pero tienen un diferencial muy bajo, mientras que otros tipos de cuentas tienen un diferencial alto pero no cobran comisión por operación. Por lo tanto, los operadores que operan a medio plazo pueden no pagar una comisión por operación, mientras que el tamaño del diferencial en las operaciones a medio plazo no es tan importante. Por el contrario, los operadores que negocian pequeños impulsos intradía prefieren pagar una comisión por abrir una operación que asumir una pérdida sólo porque el diferencial se haya ampliado «de repente». Normalmente, los corredores agrupan estas condiciones en tipos de cuenta, como Estándar, Oro, Platino, ESN, e introducen un nombre de símbolo para cada cuenta. Por ejemplo, en el caso del par EURUSD en una cuenta estándar, el símbolo en otro tipo de cuenta podría parecerse a EURUSDb, EURUSDz o EURUSD_i dependiendo del broker.

Además, los nombres de los símbolos pueden cambiar dependiendo de la fecha de vencimiento de ciertos instrumentos no relacionados con el comercio de pares de divisas en Forex, pero no vamos a considerar este punto en detalle aquí, ya que el artículo sigue dedicado específicamente a los pares de divisas.

Otra condición para la necesidad de comprobar la validez del símbolo es la ausencia puramente técnica de una suscripción a los instrumentos necesarios en la ventana 'Observación del Mercado' del terminal. Incluso si el nombre de un símbolo existe en la cuenta autorizada, pero no está seleccionado en el menú contextual del terminal (Ver -> Observación del Mercado), no podremos abrir su gráfico con el consiguiente error de la función de llamada.

Empezaremos a implementar la comprobación organizando un bucle para iterar sobre cada herramienta de nuestro almacén, como se muestra a continuación.

   for(int i=0; i<ArraySize(arr_symbol); i++)                           // iterate through all deal symbols

Para comprobar la validez de un símbolo guardado en nuestro contenedor, utilizaremos la función predefinida del terminal SymbolSelect(). El primer parámetro que le pasaremos es el nombre del símbolo en formato string. Se trata de un símbolo cuya validez queremos comprobar. El valor lógico de «true» (verdadero) viene en segundo lugar. Si el segundo parámetro es «true», si el instrumento es válido pero no está seleccionado en la «Observación del Mercado», se seleccionará automáticamente. La lógica de comprobación completa es la siguiente.

//--- check for symbol availability

   for(int i=0; i<ArraySize(arr_symbol); i++)                           // iterate through all deal symbols
     {
      if(!SymbolSelect(arr_symbol[i],true))                             // check if the symbol is in the book and add if not
        {
         printf("%s - %d -> Failed to add a symbol %s to the marketbook. Error: %d",
			__FUNCTION__,__LINE__,arr_symbol[i],GetLastError()); // informed in the journal
         MessageBox("Failed to add a symbol to the marketbook: "+arr_symbol[i]+
			". Por favor, seleccione 'Mostrar todo' en Observación del Mercado e inténtelo de nuevo. Script is done."); // informed in the message
         return;                                                        // if failed, abort
        }
     }

En consecuencia, si no se supera la comprobación de validez del símbolo, finalizamos la ejecución del programa con las notificaciones oportunas para el usuario. Una vez superadas todas las comprobaciones de validez, podemos proceder a abrir los gráficos de símbolos necesarios directamente en el terminal.

En primer lugar, vamos a proporcionar la variable auxiliar deal_close_date del tipo de datos MqlDateTime, que además nos ayudará a ordenar convenientemente todos los gráficos guardados en las carpetas de periodos de tiempo correspondientes. Para la reducción explícita del tipo de datos datetime a MqlDateTime en nuestro almacenamiento, utilizaremos la función terminal predefinida TimeToStruct() como se muestra a continuación.

      MqlDateTime deal_close_date;                                      // deal closure date in the structure
      TimeToStruct(arr_time_close[i],deal_close_date);                  // pass date to the structure

Los gráficos se dibujarán en función de los datos definidos por el usuario en las variables main_graph, addition_graph, addition_graph_2 y addition_graph_3. Si la variable contiene el valor de enumeración PERIOD_CURRENT, no dibujamos ningún gráfico. Si se introduce un valor específico en la variable (por ejemplo PERIOD_D1), tomamos este gráfico para dibujarlo. Realizaremos esta comprobación para todas las variables introducidas de la siguiente forma (la variable principal se muestra a continuación como ejemplo):

      //--- check the main one
      if(main_graph != PERIOD_CURRENT)                                  // if the main one selected

Para dibujar cada gráfico, abra un nuevo gráfico con el símbolo deseado. El gráfico de símbolos se abrirá utilizando la función predefinida de terminal ChartOpen(), mientras se pasa el símbolo y el marco temporal requeridos desde el almacenamiento, como se muestra a continuación.

         //--- open the required chart
         handle = ChartOpen(arr_symbol[i],main_graph);                  // open the necessary symbol chart

Una vez abierto el gráfico, le aplicamos todos los ajustes de usuario estándar que he mencionado anteriormente. Para ello, utilizaremos la función predefinida terminal ChartApplyTemplate(), que nos ayudará mucho con esto y nos ahorrará escribir el código nosotros mismos. Los parámetros de la función ChartApplyTemplate() obtienen el manejador del gráfico obtenido al llamar a la función ChartOpen(), así como el nombre de la plantilla especificada por el usuario para el marco temporal de la operación en el formato dailyHistorytemp. A continuación se muestra el código para llamar a la función de aplicación de plantillas.

         ChartApplyTemplate(handle,main_template);                      // apply template

Hagamos aquí una pequeña digresión para aquellos que no hayan utilizado plantillas en el terminal MetaTrader 5 hasta ahora. Si utilizamos una plantilla «fea», la pantalla de impresión guardada de la operación puede resultar «irritante» o incluso «inútil». Siga estos pasos para crear su propia plantilla dailyHistorytemp:

  • Abra el gráfico de cualquier símbolo mediante Archivo -> Nuevo gráfico.
  • Una vez abierto el gráfico, pulse F8 para abrir la ventana Propiedades, por ejemplo «PropertiesGBPAUD,Daily».
  • La ventana Propiedades contiene varias pestañas: General, Mostrar y Colores. En cada uno de ellos, realice los ajustes que le resulten más familiares, por ejemplo, para un gráfico diario, y haga clic en Aceptar. Encuentre los detalles aquí: Ajustes de gráficos (ayuda oficial del terminal).
  • Tras hacer clic en Aceptar, se cierra la ventana Propiedades y el gráfico adopta la forma que usted necesita.
  • Ahora, en el menú contextual, seleccione Gráficos - Plantillas - Guardar plantilla. Aparece la ventana de guardado de plantillas. Introduzca dailyHistorytemp.tpl en «Nombre de archivo» y haga clic en Guardar.
  • Después de eso, en la carpeta terminal ..MQL5\Profiles\Templates aparecerá el archivo dailyHistorytemp.tpl, que podrá utilizar en el script. Lo principal a tener en cuenta es que el nombre de la plantilla se introduce en el script sin la extensión .tpl.

Ahora volvamos a nuestro código. Una vez aplicada la plantilla deseada, tenemos que hacer un pequeño retraso en la ejecución del código para dar tiempo a que el gráfico se cargue con la calidad deseada. De lo contrario, es posible que el gráfico no se muestre correctamente debido al tiempo necesario para cargar los datos de precios históricos requeridos en el terminal. Por ejemplo, si no ha abierto un gráfico durante bastante tiempo, el terminal necesita tiempo para mostrarlo correctamente. Anunciaremos el tiempo de retardo a través de la función predefinida de terminal Sleep(), como se muestra a continuación.

         Sleep(2000);                                                   // wait for the chart to load

Como retardo, utilizaremos el valor de 2000 milisegundos o 2 segundos, tomado puramente de la práctica, para que se garantice que el gráfico tenga tiempo de cargar, y la ejecución del script no llegue a largos minutos con un gran número de operaciones. Para personalizar este valor usted mismo, puede introducirlo de forma independiente en la configuración del script para acelerar o ralentizar el proceso, en función del rendimiento de su equipo o de su conexión a Internet. Como demuestra la práctica, dos segundos serán suficientes en la mayoría de los casos.

Ahora tenemos que desactivar el desplazamiento de los gráficos a los valores de barra más recientes, ya que estamos analizando la historia y no necesitamos nuevos ticks para desplazar nuestro gráfico a la derecha todo el tiempo. Esto se puede hacer estableciendo la propiedad CHART_AUTOSCROLL a 'false' del gráfico necesario a través de la función predefinida ChartSetInteger(), como se muestra a continuación.

         ChartSetInteger(handle,CHART_AUTOSCROLL,false);                // disable auto scroll

Ahora que el desplazamiento automático está desactivado, primero tenemos que contar el número de barras del gráfico del marco temporal correspondiente a la izquierda para desplazar el gráfico hacia el historial del periodo de cierre de la operación en cuestión. Podemos obtener el valor a través de la función predefinida de terminal iBarShift(), pasando como parámetros el símbolo, el timeframe del gráfico y la hora de cierre de la operación, ya que queremos ver en la pantalla de impresión la operación completa de principio a fin. En el parámetro 'exact', pasamos 'false' por si el historial es realmente profundo. Sin embargo, no es tan crítico para nuestra aplicación en este caso. A continuación se muestra la llamada completa al método con los parámetros.

         bars = iBarShift(arr_symbol[i],main_graph,arr_time_close[i],false); // get the shift for the deal time

Una vez que conocemos el desplazamiento del gráfico que necesitamos, podemos mostrar exactamente el periodo que capturará la operación que necesitamos en el historial. Podemos desplazar el gráfico en la dirección deseada la distancia que necesitemos utilizando la variable terminal predefinida ChartNavigate() pasándole los siguientes parámetros, como se muestra a continuación.

         ChartNavigate(handle,CHART_CURRENT_POS,-bars+bars_from_right_main); // shifted the chart with a custom margin

Para desplazar el gráfico, pasamos el manejador del gráfico, el valor de CHART_CURRENT_POS posición actual de la enumeración ENUM_CHART_POSITION, así como el desplazamiento a la operación que obtuvimos anteriormente en la variable bars con el desplazamiento introducido por el usuario para evaluar el potencial de movimiento del precio tras salir de la posición.

Después de las transformaciones del gráfico descritas, llame al método ChartRedraw() por si acaso y comience a dibujar datos adicionales en el gráfico para analizar las operaciones históricas.

Para dibujar los elementos del panel de información personalizado y las líneas que indican la apertura y el cierre de la posición, así como los niveles Stop Loss y Take Profit, utilizaremos las funciones personalizadas paintDeal() y paintPanel() correspondientes. Los definiremos nosotros mismos basándonos en patrones de comportamiento estándar para trabajar con gráficos de terminal, donde paintDeal() dibujará líneas de precios de apertura y cierre de la operación, así como Take Profit y Stop Loss , mientras que el método paintPanel() contendrá una tabla con toda la información de la operación en la esquina de la pantalla.

La definición detallada de los métodos figura en la sección siguiente. Aquí simplemente indicaremos que los métodos serán llamados en este segmento de código. Esto también se hace desde el punto de vista de que no necesariamente tiene que utilizar la implementación dada en el presente artículo para dibujar estos dos grupos de elementos. Puede redefinirlos usted mismo, manteniendo la firma deseada. La aplicación de estos métodos en el presente artículo es un ejemplo de la relación óptima entre belleza y contenido informativo de los gráficos en el momento de escribirlo en el código. El objetivo principal aquí es mantener la posición de las llamadas a métodos en el código principal.

         //--- draw the deal
         paintDeal(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i],arr_close[i],arr_time_open[i],arr_time_close[i]);

         //--- draw the information panel
         paintPanel(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i],
                    arr_close[i],arr_time_open[i],arr_time_close[i],arr_magic[i],arr_comment[i],
                    arr_extermalID[i],arr_volume[i],arr_commission[i],arr_reason[i],arr_swap[i],
                    arr_profit[i],arr_fee[i],arr_symbol[i],(int)SymbolInfoInteger(arr_symbol[i],SYMBOL_DIGITS));

Después de que los métodos hayan dibujado las líneas de reparto y el panel de información en el gráfico, podemos proceder a la implementación de guardar una pantalla de impresión de todo lo ocurrido en el gráfico actual. Para ello, primero determinamos las dimensiones futuras de la pantalla de impresión en anchura y altura simplemente solicitando estos datos al gráfico abierto mediante la función ChartSetInteger() redefinida del terminal, como se muestra a continuación.

         //--- get data by screen size
         chart_width = (int) ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS);   // look at the chart width
         chart_height = (int) ChartGetInteger(handle,CHART_HEIGHT_IN_PIXELS); // look at the chart height

Pasamos los valores de enumeración ENUM_CHART_PROPERTY_INTEGER para el ancho CHART_WIDTH_IN_PIXELS y el alto CHART_HEIGHT_IN_PIXELS respectivamente como los parámetros correspondientes para mostrar el gráfico.

Una vez recibidos los datos de tamaño, tendremos que crear una ruta para guardar el gráfico de la pantalla de impresión del acuerdo en la carpeta estándar del terminal. Para evitar que el EA coloque todos los archivos en una carpeta, sino que los ordene para comodidad del usuario, automatizamos este proceso a través del nombre del archivo en la siguiente cadena.

         string name_main_screen = brok_name+"/"+
                                   IntegerToString(account_num)+"/"+
                                   IntegerToString(deal_close_date.year)+"-"+IntegerToString(deal_close_date.mon)+
				   "-"+IntegerToString(deal_close_date.day)+"/"+
                                   IntegerToString(PositionID[i])+"/"+
                                   EnumToString(main_graph)+IntegerToString(PositionID[i])+".png"; // assign the name

Gráficamente, la estructura de clasificación de archivos en carpetas de un directorio estándar será la que se muestra en la Figura 1.

Figura 1. Estructura de direcciones de carpetas de pantallas de impresión guardadas por operaciones

Figura 1. Estructura de direcciones de carpetas de pantallas de impresión guardadas por operaciones


Como podemos ver, los ficheros de gráficos se ordenarán por nombre de broker, número de cuenta, año, mes y día de ejecución, de forma que el usuario pueda encontrar fácilmente la operación deseada sin necesidad de buscar el nombre del fichero en una lista general. Los distintos plazos se ubicarán en la carpeta del número de posición correspondiente del terminal.

Guardaremos directamente la información llamando a la función predefinida de terminal ChartScreenShot(), pasándole como parámetros el manejador del gráfico requerido, los tamaños de pantalla de impresión que obtuvimos anteriormente, que se corresponden con los tamaños de los gráficos, y también el nombre del fichero que contiene toda la estructura de direcciones de carpetas, tal y como se muestra en la Figura 1 y en el código siguiente.

         ChartScreenShot(handle,name_main_screen,chart_width,chart_height,ALIGN_LEFT);             // make a screenshot

Si las carpetas especificadas en la jerarquía no existen en la carpeta estándar del terminal, éste las creará automáticamente sin intervención del usuario.

Después de guardar el archivo, podemos cerrar el gráfico para no saturar la vista del terminal, especialmente si la descarga contiene un gran número de operaciones históricas en la cuenta. Cerraremos el gráfico utilizando la función terminal predefinida ChartClose() pasándole el handle del gráfico requerido, para no cerrar nada innecesario. La llamada a la función se muestra a continuación.

         ChartClose(handle);                                            // closed the chart

Repetiremos esta operación de forma similar para todos los plazos especificados por el usuario en las entradas. Ahora, para completar nuestro script, necesitamos definir el comportamiento de los métodos paintDeal() y paintPanel() fuera del código principal del programa.


Dibujar objetos de datos en gráficos

Para colocar cómodamente la información en el gráfico de la pantalla de impresión, sólo tenemos que redefinir dos métodos, que determinarán cómo se dibujarán exactamente los datos requeridos por el usuario.

Comencemos con una descripción del método paintDeal(). Su objetivo es dibujar gráficos para una posición asociados a la ubicación de los precios de apertura y cierre, Stop Loss y Take Profit. Para ello, declare la descripción del método con la siguiente firma fuera del cuerpo del código principal:

void paintDeal(long handlE,
               ulong tickeT,
               double stop_losS,
               double take_profiT,
               double opeN,
               double closE,
               datetime timE,
               datetime time_closE)

Los siguientes valores se especifican en los parámetros del método: handlE - handle del gráfico que vamos a dibujar, tickeT - deal ticket, stop_losS - precio de Stop Loss si está presente, take_profiT - Take Profit si está presente, open price - opeN y close price - closE, deal open time - timE y deal close time - time_closE.

Empecemos a dibujar con el nombre del objeto, que corresponderá a un nombre único que no debe repetirse. Por lo tanto, en el nombre vamos a implementar una característica que este objeto corresponde a una parada en la forma de «name_sl_». Para que el nombre sea único, añadiremos también el número de billete de la operación, como se muestra a continuación.

   string name_sl = "name_sl_"+IntegerToString(tickeT);                    // assign the name

Ahora podemos crear el objeto gráfico propiamente dicho utilizando la función ObjectCreate() predefinida del terminal, que dibuja el nivel Stop Loss por posición histórica en el gráfico. Los parámetros pasados son el manejador del gráfico y el nombre único de la variable name_sl. Especifique el valor OBJ_ARROW_LEFT_PRICE como un tipo de objeto, lo que significa la etiqueta de precio izquierda de la enumeración ENUM_OBJECT, así como el valor de precio real y el momento en que se colocó la etiqueta en el gráfico, como se muestra a continuación.

   ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS);     // create the left label object

Ahora que el objeto ha sido creado, vamos a establecer los valores de sus campos OBJPROP_COLOR y OBJPROP_TIMEFRAMES. Establezca OBJPROP_COLOR en clrRed, ya que Stop Loss suele ser de color rojo, mientras que OBJPROP_TIMEFRAMES se establece en OBJ_ALL_PERIODS para mostrar en todos los plazos. Aunque la segunda condición no es crítica en esta aplicación. En general, el bloque de dibujo Stop Loss tendrá el siguiente aspecto.

//--- draw stop loss
   string name_sl = "name_sl_"+IntegerToString(tickeT);                    // assign the name
   ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS);     // create the left label object
   ObjectSetInteger(handlE,name_sl,OBJPROP_COLOR,clrRed);                  // add color
   ObjectSetInteger(handlE,name_sl,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);    // set visibility
   ChartRedraw(handlE);                                                    // redraw

Después de dibujar cada bloque, llama al método ChartRedraw().

Dibujar el bloque Take Profit será similar a dibujar Stop Loss con las siguientes excepciones. En primer lugar, añada «name_tp_» más el ticket de la operación al nombre único del objeto, y establezca el color de la paleta verde, que corresponde a la designación tradicional del beneficio recibido, mediante el color clrLawnGreen. Por lo demás, la lógica es similar a la del bloque Stop Loss y se presenta completa aquí.

//--- draw take profit
   string name_tp = "name_tp_"+IntegerToString(tickeT);                    // assign the name
   ObjectCreate(handlE,name_tp,OBJ_ARROW_LEFT_PRICE,0,timE,take_profiT);   // create the left label object
   ObjectSetInteger(handlE,name_tp,OBJPROP_COLOR,clrLawnGreen);            // add color
   ObjectSetInteger(handlE,name_tp,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);    // set visibility
   ChartRedraw(handlE);                                                    // redraw

Pasemos a la implementación de dibujar también el precio de entrada a través de la etiqueta de precio de la izquierda. La diferencia con los bloques anteriores está, en primer lugar, de nuevo en el nombre único del objeto. Añadiremos «name_open_» al principio. Otra diferencia es el color de la línea clrWhiteSmoke, para que no destaque demasiado en el gráfico, pero por lo demás todo es igual.

//--- draw entry price
   string name_open = "name_open_"+IntegerToString(tickeT);                // assign the name
   ObjectCreate(handlE,name_open,OBJ_ARROW_LEFT_PRICE,0,timE,opeN);        // create the left label object
   ObjectSetInteger(handlE,name_open,OBJPROP_COLOR,clrWhiteSmoke);         // add color
   ObjectSetInteger(handlE,name_open,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);  // set visibility
   ChartRedraw(handlE);                                                    // redraw

La línea que une las etiquetas de precio de apertura y cierre de la operación se muestra con el mismo color. El tipo de línea será diferente. Al crear un objeto en el parámetro ObjectCreate() del método, pasaremos el valor OBJ_TREND de la enumeración ENUM_OBJECT como tercer parámetro para crear una línea de tendencia. Para posicionar correctamente la línea de tendencia en el gráfico, necesitaremos especificar parámetros adicionales para la posición de dos puntos, donde cada punto tendrá dos atributos: precio y tiempo. Para ello, pasaremos los precios de apertura y cierre opeN y closE a los parámetros posteriores junto con las horas de cierre y apertura en las variables timE y time_closE, como se muestra a continuación.

//--- deal line
   string name_deal = "name_deal_"+IntegerToString(tickeT);                // assign the name
   ObjectCreate(handlE,name_deal,OBJ_TREND,0,timE,opeN,time_closE,closE);  // create the left label object
   ObjectSetInteger(handlE,name_deal,OBJPROP_COLOR,clrWhiteSmoke);         // add color
   ObjectSetInteger(handlE,name_deal,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);  // set visibility
   ChartRedraw(handlE);                                                    // redraw

Para visualizar completamente la operación en el gráfico, queda por dibujar la etiqueta de precio de cierre de la operación. Para conseguirlo, utilizaremos la etiqueta de precio correcto, de modo que la información se muestre en la pantalla de impresión de una forma visualmente más agradable. Para dibujar la etiqueta derecha, el método ObjectCreate() debe recibir el valor OBJ_ARROW_RIGHT_PRICE como tercer parámetro, que significa la etiqueta de precio derecha de la enumeración ENUM_OBJECT. Para el resto del dibujo sólo necesitamos el precio y la hora, que vamos a pasar a través de las correspondientes variables time_closE, closE como se muestra a continuación.

//--- draw exit price
   string name_close = "name_close"+IntegerToString(tickeT);                // assign the name
   ObjectCreate(handlE,name_close,OBJ_ARROW_RIGHT_PRICE,0,time_closE,closE);// create the left label object
   ObjectSetInteger(handlE,name_close,OBJPROP_COLOR,clrWhiteSmoke);         // add color
   ObjectSetInteger(handlE,name_close,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);  // set visibility
   ChartRedraw(handlE);                                                     // redraw

Esto completa la descripción de nuestro método personalizado paintDeal() para dibujar las líneas de entrada y salida de la posición. Ahora podemos proceder a describir el método para dibujar el panel de información completa del acuerdo en el método paintPanel().

Describir el método para dibujar el panel nos obligará a tener una estructura más compleja de métodos responsables de dibujar etiquetas de texto, como OBJ_LABEL etiquetas de texto de tipo, ENUM_OBJECT enumeración y OBJ_RECTANGLE_LABEL objeto para crear y diseñar interfaces gráficas de usuario. Vamos a declarar los métodos personalizados correspondientes llamados LabelCreate() para crear etiquetas de texto y RectLabelCreate() para crear una etiqueta rectangular. Comenzaremos la descripción con los métodos auxiliares, para pasar después a la descripción del método principal paintPanel(), en el que utilizaremos métodos auxiliares.

En general, la estructura de nuestros métodos de script se parecerá a la de la Figura 2.

Figura 2. Estructura de los métodos personalizados para dibujar gráficos

Figura 2. Estructura de los métodos personalizados para dibujar gráficos


Declare el método LabelCreate() con la siguiente firma como parámetros:

bool LabelCreate(const long              chart_ID=0,               // chart ID
                 const string            name="Label",             // label name
                 const int               sub_window=0,             // subwindow number
                 const long              x=0,                      // X coordinate
                 const long              y=0,                      // Y coordinate
                 const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER, // chart corner for anchoring
                 const string            text="Label",             // text
                 const string            font="Arial",             // font
                 const int               font_size=10,             // font size
                 const color             clr=clrRed,               // color
                 const double            angle=0.0,                // text angle
                 const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type
                 const bool              back=false,               // in the background
                 const bool              selection=false,          // select to move
                 const bool              hidden=true,              // hidden in the list of objects
                 const long              z_order=0)                // priority for clicking with a mouse

El parámetro chart_ID recibe el manejador del gráfico en el que tenemos que dibujar el objeto: 'name' es un nombre de objeto único, mientras que el valor 0 del parámetro sub_window significa que queremos dibujar el objeto en la ventana principal del gráfico. Las coordenadas de la esquina superior izquierda del objeto se pasarán a través de los parámetros X e Y respectivamente. Podemos cambiar la vinculación de la esquina del objeto al gráfico desde la esquina izquierda estándar pasando el valor correspondiente al parámetro 'corner', pero dejaremos ahí el valor por defecto de ANCHOR_LEFT_UPPER. Pase el valor de la cadena de la información a mostrar en el parámetro 'text'. El tipo de visualización, como el tipo de fuente y su tamaño, color y ángulo, se pasarán en los parámetros 'font', 'font_size', 'clr' y 'angle' correspondientes. También haremos que nuestro objeto quede oculto en la lista de objetos para el usuario y no seleccionable con el ratón, utilizando los parámetros 'selection' y 'hidden'. El parámetro z_order será el responsable del orden de prioridad de los clics del ratón.

Comencemos la descripción del método restableciendo la variable error para que sea posible controlar correctamente el resultado de la creación de un objeto en el futuro a través de la función terminal predefinida ResetLastError(). La creación del resultado de la creación del objeto de tipo OBJ_LABEL se maneja a través del operador lógico if mientras se llama a la función ObjectCreate() en él, como se muestra a continuación. Si el objeto no se crea, informe al usuario de ello en el registro del EA e interrumpa la ejecución del método mediante la sentencia return como de costumbre.

//--- reset the error value
   ResetLastError();
//--- create a text label
   if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0))
     {
      Print(__FUNCTION__,
            ": failed to create the text label! Error code = ",GetLastError());
      return(false);
     }

Si el objeto se ha creado correctamente, inicialice los campos de propiedad del objeto mediante las funciones terminales predefinidas ObjectSetInteger(), ObjectSetString() y ObjectSetDouble() para darle la apariencia requerida. Utilice la función ObjectSetInteger() para establecer los valores de las coordenadas correspondientes, el ángulo de anclaje del objeto, el tamaño de fuente, el método de anclaje del objeto, el color, el modo de visualización, así como las propiedades relacionadas con la visibilidad del objeto para el usuario. Establezca los valores de ángulo de la fuente mediante la función ObjectSetDouble(), mientras que la función ObjectSetString() se utiliza para definir el contenido del texto pasado y el tipo de fuente para su visualización. A continuación se presenta la aplicación completa del cuerpo del método.

//--- reset the error value
   ResetLastError();
//--- create a text label
   if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0))
     {
      Print(__FUNCTION__,
            ": failed to create the text label! Error code = ",GetLastError());
      return(false);
     }
//--- set label coordinates
   ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
//--- set the chart's corner, relative to which point coordinates are defined
   ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
//--- set the text
   ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
//--- set the text font
   ObjectSetString(chart_ID,name,OBJPROP_FONT,font);
//--- set font size
   ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size);
//--- set the text angle
   ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle);
//--- set anchor type
   ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor);
//--- set the color
   ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- enable (true) or disable (false) the mode of moving the label by mouse
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
//--- set the priority for receiving the event of a mouse click on the chart
   ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- successful execution
   return(true);

Declara el método RectLabelCreate() con la siguiente firma como parámetros de creación del objeto:

bool RectLabelCreate(const long             chart_ID=0,               // chart ID
                     const string           name="RectLabel",         // label name
                     const int              sub_window=0,             // subwindow number
                     const int              x=19,                     // X coordinate
                     const int              y=19,                     // Y coordinate
                     const int              width=150,                // width
                     const int              height=20,                // height
                     const color            back_clr=C'236,233,216',  // background color
                     const ENUM_BORDER_TYPE border=BORDER_SUNKEN,     // border type
                     const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring
                     const color            clr=clrRed,               // flat border color (Flat)
                     const ENUM_LINE_STYLE  style=STYLE_SOLID,        // flat border style
                     const int              line_width=1,             // flat border width
                     const bool             back=true,                // 'true' in the background
                     const bool             selection=false,          // select to move
                     const bool             hidden=true,              // hidden in the list of objects
                     const long             z_order=0)                // priority for clicking with a mouse

Los parámetros del método RectLabelCreate() son muy similares a los parámetros del método LabelCreate() declarado anteriormente, excepto por los ajustes adicionales para el borde de la etiqueta rectangular, que servirá de fondo para mostrar los datos del objeto del método anterior. Parámetros adicionales para configurar el borde del objeto: <border> - tipo de borde definido por la enumeración ENUM_BORDER_TYPE con el valor por defecto de BORDER_SUNKEN, <style> - estilo de borde definido por la enumeración ENUM_LINE_STYLE utilizando el valor por defecto de STYLE_SOLID y line_width - ancho de línea del borde como valor entero.

La definición del cuerpo del método tendrá un aspecto similar al anterior y, del mismo modo, constará de dos secciones globales: la creación del objeto y la definición de sus propiedades a través de los correspondientes métodos terminales predefinidos, tal y como se muestra a continuación.

//--- reset the error value
   ResetLastError();                                                    // reset error
//--- create a rectangle label
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))   // create object
     {
      //--- set label coordinates
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);              // assign x coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);              // assign y coordinate
      //--- set label size
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);              // width
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);             // height
      //--- set the background color
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);         // background color
      //--- set border type
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);       // border type
      //--- set the chart corner, relative to which point coordinates are defined
      ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);            // anchor corner
      //--- set flat border color (in Flat mode)
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);                // frame
      //--- set flat border line style
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);              // style
      //--- set flat border width
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);         // width
      //--- display in the foreground (false) or background (true)
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);                // default is background
      //--- enable (true) or disable (false) the mode of moving the label by mouse
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);     // is it possible to select
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);       //
      //--- hide (true) or display (false) graphical object name in the object list
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);            // is it visible in the list
      //--- set the priority for receiving the event of a mouse click on the chart
      ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);           // no events
      //--- successful execution
     }
   return(true);

Ahora que todos los métodos auxiliares han sido descritos, vamos a definir el cuerpo del método principal que dibujará todo el panel - paintPanel(). Las entradas contendrán los campos necesarios para mostrar la información completa de la operación al usuario, como se muestra a continuación.

void paintPanel(long handlE,                 
                ulong tickeT,                
                double stop_losS,            
                double take_profiT,          
                double opeN,                 
                double closE,                
                datetime timE,               
                datetime time_closE,         
                int magiC,                   
                string commenT,              
                string externalIDD,          
                double volumE,               
                double commissioN,           
                ENUM_DEAL_REASON reasoN,     
                double swaP,                 
                double profiT,               
                double feE,                  
                string symboL,               
                int digitS                   
               )

Como en los métodos anteriores, el primer parámetro será el encargado de definir el manejador del gráfico, sobre el que se crearán todos los objetos asociados al panel de información. Todos los demás parámetros repetirán los campos del objeto de operación histórico.

Comenzaremos la implementación del método para dibujar el panel de datos del acuerdo definiendo las variables para almacenar el tamaño del panel, así como las coordenadas para anclar las columnas con el nombre de la información mostrada y los valores obtenidos previamente, tal y como se muestra a continuación.

int height=20, max_height =0, max_width = 0;	// column height and max values for indent
int x_column[2] = {10, 130};			// columns X coordinates
int y_column[17];				// Y coordinates

La variable 'height' almacenará un valor estático de 20 como altura de cada columna para asegurar que cada fila se dibuja uniformemente, y los valores max_height y max_width almacenarán los valores máximos de cada columna para asegurar un dibujo uniforme. Las coordenadas requeridas a lo largo de los ejes X e Y se almacenarán en las matrices x_column[] e y_column[], respectivamente.

Ahora tenemos que declarar dos matrices que almacenarán los valores de fila para mostrar la columna de cabecera y la columna de valor. Declararemos la columna de cabecera a través de la matriz de datos de tipo string, como se muestra en el siguiente código.

   string column_1[17] =
     {
      "Symbol",
      "Position ID",
      "External ID",
      "Magic",
      "Comment",
      "Reason",
      "Open",
      "Close",
      "Time open",
      "Time close",
      "Stop loss",
      "Take profit",
      "Volume",
      "Commission",
      "Swap",
      "Profit",
      "Fee"
     };

Todos los valores del array se declaran e inicializan estáticamente, ya que el panel no cambiará y los datos se mostrarán siempre en la misma secuencia. Esto debería ser conveniente para acostumbrarse a ver información sobre diferentes ofertas. Es posible implementar una funcionalidad que permita excluir del panel los datos que no contengan valores o sean iguales a cero, pero esto sería un inconveniente para la vista a la hora de buscar información rápidamente. Sigue siendo más familiar buscar información en un patrón de visualización conocido que mirar cada vez los valores de las columnas.

En la misma secuencia de datos, declare un segundo array, que ya contendrá los valores de las columnas declaradas en el array anterior. Describe la declaración del array de la siguiente manera:

   string column_2[17] =
     {
      symboL,
      IntegerToString(tickeT),
      externalIDD,
      IntegerToString(magiC),
      commenT,
      EnumToString(reasoN),
      DoubleToString(opeN,digitS),
      DoubleToString(closE,digitS),
      TimeToString(timE),
      TimeToString(time_closE),
      DoubleToString(stop_losS,digitS),
      DoubleToString(take_profiT,digitS),
      DoubleToString(volumE,2),
      DoubleToString(commissioN,2),
      DoubleToString(swaP,2),
      DoubleToString(profiT,2),
      DoubleToString(feE,2)
     };

La matriz se declarará localmente, a nivel de método, y los campos se inicializarán al mismo tiempo, directamente a partir de los parámetros del método, utilizando las funciones terminales predefinidas correspondientes.

Ahora que hemos declarado los contenedores con los datos requeridos, necesitamos calcular los valores de anclaje de las coordenadas de cada celda, considerando la búsqueda del valor máximo en cada una de ellas. Podemos implementar esto con el siguiente código:

   int count_rows = 1;
   for(int i=0; i<ArraySize(y_column); i++)
     {
      y_column[i] = height * count_rows;
      max_height = y_column[i];
      count_rows++;

      int width_curr = StringLen(column_2[i]);

      if(width_curr>max_width)
        {
         max_width = width_curr;
        }
     }

   max_width = max_width*10;
   max_width += x_column[1];
   max_width += x_column[0];

Aquí encontramos las coordenadas de anclaje de cada objeto haciendo un bucle sobre el número de filas, multiplicándolas por un valor fijo de altura Y para cada una. También comparamos cada valor con el valor de anchura máxima para obtener la coordenada X.

Una vez que tenemos todos los valores con sus coordenadas, podemos empezar a dibujar la información usando el método personalizado LabelCreate() previamente declarado, que llamaremos cíclicamente, según el número de nuestras filas a mostrar, como se muestra a continuación.

   color back_Color = clrWhiteSmoke;
   color font_Color = clrBlueViolet;

   for(int i=0; i<ArraySize(column_1); i++)
     {
      //--- draw 1
      string name_1 = column_1[i]+"_1_"+IntegerToString(tickeT);            
      LabelCreate(handlE,name_1,0,x_column[0],y_column[i],CORNER_LEFT_UPPER,column_1[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false);
      //--- draw 2
      string name_2 = column_1[i]+"_2_"+IntegerToString(tickeT);            
      LabelCreate(handlE,name_2,0,x_column[1],y_column[i],CORNER_LEFT_UPPER,column_2[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false);
     }

Al final del método, sólo tenemos que dibujar el fondo utilizando el método personalizado RectLabelCreate() previamente declarado y descrito a estos valores y actualizar el gráfico mostrado, como se muestra a continuación.

//--- draw the background
   RectLabelCreate(handlE,"RectLabel",0,1,height,max_width,max_height,back_Color);                   

   ChartRedraw(handlE);                                                 

Esto completa la descripción de todos los métodos. El proyecto está listo para su montaje y uso.

Como resultado, el archivo gráfico tendrá el aspecto que se muestra en la Figura 3 después de utilizar el script.

Figura 3. El resultado de la operación mostrados

Figura 3. El resultado de la operación mostrados

Como vemos, toda la información sobre la operación se presenta de forma generalizada en un único gráfico, lo que hace más cómodo el análisis y la evaluación de las operaciones realizadas por el usuario. El script ordena dichos archivos en las carpetas adecuadas, lo que también permite al usuario encontrar en cualquier momento la información necesaria sobre cualquier operación comercial en el historial de la cuenta.


Conclusión

Con este artículo, hemos terminado de escribir un script para la visualización automatizada de operaciones en un gráfico. Utilizando esta solución, puede mejorar significativamente sus operaciones corrigiendo posibles errores al elegir un punto de entrada, así como aumentar la expectativa matemática de toda su estrategia eligiendo los símbolos correctos y la ubicación esperada del impulso del precio. El uso del script le permitirá ahorrar mucho tiempo en la preparación de archivos de gráficos, que podrá dedicar al análisis y a la búsqueda de nuevas ideas para operar. Lo principal que hay que recordar es que el mercado cambia constantemente y, para garantizar un funcionamiento estable, hay que estar siempre en contacto y vigilar los cambios. Esta herramienta puede ayudarle a conseguirlo. Les deseo éxito en su trabajo. Por favor, deje su opinión en los comentarios.


Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/14961

Archivos adjuntos |
DealsPrintScreen.mq5 (104.52 KB)
Alexander Piechotta
Alexander Piechotta | 1 dic 2024 en 15:17
Una gran idea y una explicación de primera para entenderlo. Gracias.
Aleksandr Seredin
Aleksandr Seredin | 1 dic 2024 en 17:01
Alexander Piechotta #:
Una gran idea y una explicación de primera para entenderlo. Muchas gracias.

Muchas gracias por su comentario. Estaré encantado si esto te ayuda en tu trabajo. Servus! :)

Algoritmo de optimización de reacciones químicas (CRO) (Parte I): Química de procesos en la optimización Algoritmo de optimización de reacciones químicas (CRO) (Parte I): Química de procesos en la optimización
En la primera parte de este artículo, nos sumergiremos en el mundo de las reacciones químicas y descubriremos un nuevo enfoque de la optimización. La optimización de reacciones químicas (Chemical Reaction Optimization, CRO) utiliza principios derivados de las leyes de la termodinámica para lograr resultados eficientes. Desvelaremos los secretos de la descomposición, la síntesis y otros procesos químicos que se convirtieron en la base de este innovador método.
Desarrollo de un sistema de repetición (Parte 73): Una comunicación inusual (II) Desarrollo de un sistema de repetición (Parte 73): Una comunicación inusual (II)
En este artículo, veremos cómo transferir información en tiempo real entre el indicador y el servicio, y comprenderemos por qué pueden surgir problemas al modificar el timeframe y cómo resolverlos correctamente. Como bono, tendrás acceso a la última versión de la aplicación de repetición/simulador. El contenido es exclusivamente didáctico y no debe utilizarse con otros fines.
Desarrollamos un asesor experto multidivisa (Parte 16): Efecto de diferentes historias de cotizaciones en los resultados de las pruebas Desarrollamos un asesor experto multidivisa (Parte 16): Efecto de diferentes historias de cotizaciones en los resultados de las pruebas
El asesor experto que estamos desarrollando debería mostrar buenos resultados al negociar con diferentes brókeres. Pero hasta ahora hemos usado las cotizaciones de la cuenta demo de MetaQuotes para las pruebas. Veamos si nuestro asesor experto está listo para trabajar en una cuenta comercial con cotizaciones diferentes a las utilizadas durante las pruebas y la optimización.
Desarrollo de un sistema de repetición (Parte 72): Una comunicación inesperada (I) Desarrollo de un sistema de repetición (Parte 72): Una comunicación inesperada (I)
Lo que construiremos será complejo de entender. Por esta razón, en este artículo solo presentaré el inicio de la construcción. Léelo con calma, ya que es esencial comprender su contenido para pasar al siguiente paso. El objetivo de este contenido es meramente didáctico, sin aplicación práctica más allá del aprendizaje y estudio de los conceptos presentados.