English Русский 中文 Deutsch 日本語 Português
preview
Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis

Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis

MetaTrader 5Sistemas comerciales | 3 diciembre 2024, 10:39
424 0
Aleksandr Seredin
Aleksandr Seredin

Introducción

En este artículo, desarrollaremos un script desde cero para visualizar transacciones durante el análisis retrospectivo de decisiones comerciales. En pocas palabras, si operamos manualmente y analizamos nuestras entradas pasadas al mercado para mejorar nuestro desempeño comercial, queremos dedicar el menor tiempo posible al análisis manual del historial y al trabajo puramente técnico asociado con él: Abrir gráficos, buscar transacciones en el historial, guardar manualmente capturas de pantalla en la terminal, dibujar de forma independiente Stop Loss y Take Profit para transacciones completadas, así como recopilar información sobre comisiones pagadas y swaps. El script que vamos a desarrollar aquí ayudará a reducir significativamente todo el trabajo mecánico. Al utilizar el script (que ha recopilado de los archivos adjuntos a continuación), podrá reducir significativamente el tiempo dedicado al trabajo técnico para prestar más atención al análisis de decisiones comerciales. Aquellos que no quieran perder tiempo ensamblando los proyectos pueden descargar una versión ya preparada del script en el Market de MQL5. El desarrollo del script se dividirá en dos partes con el código fuente completo adjunto en cada parte.


¿Por qué necesitamos un análisis retrospectivo?

El objetivo principal de cualquier persona que ingresa al mercado es obtener ganancias a largo plazo con un riesgo controlado. Este tipo de negocios se puede comparar con otros en los que también hay que lidiar con riesgos. Sin embargo, como lo demuestra la práctica, la mayoría de las nuevas empresas en la economía real eventualmente quiebran. En los mercados financieros, los fondos pueden perderse mucho más rápido debido al uso del apalancamiento y la regresión total de las pérdidas en todos los fondos disponibles. En los negocios reales, si algo sale mal, puedes reorientar tus instalaciones de producción hacia otros fines (si no están hipotecadas) y empezar de nuevo. En el trading, toda su cuenta soporta todas las pérdidas.

Múltiples estadísticas confirman la tesis de que invertir en los mercados financieros sin control de riesgos y con un uso desmedido del apalancamiento puede hacer de esa inversión la más peligrosa en términos de riesgos de inversión de capital. Según la investigación realizada por la Comisión de Bolsa y Valores (Securities and Exchange Commission, SEC) de EE.UU. Securities and Exchange Commission (Release No. 34-64874, File Number: S7-30-11) en un plazo de 17 años:

“Aproximadamente el 70% de los clientes pierden dinero cada trimestre y, en promedio, el 100% de la inversión de un cliente minorista se pierde en menos de 12 meses”

En la serie anterior de artículos sobre la importancia del equilibrio de riesgos y la gestión de riesgos, ya he señalado que el riesgo descontrolado siempre conduce a la pérdida de dinero e incluso una estrategia inicialmente rentable puede convertirse en poco rentable si no se gestiona el riesgo. En esta serie de artículos, consideraremos el aspecto de que el mercado es una entidad muy flexible que cambia con el tiempo bajo la influencia de diversos factores económicos y políticos. Para una comprensión más clara, podemos simplificar esta tesis diciendo que el mercado puede encontrarse al menos en dos etapas: un movimiento plano y una tendencia. Por lo tanto, es muy importante analizar constantemente sus actividades de negociación para determinar si la estrategia elegida es adecuada a la situación del mercado.

Muchos operadores algorítmicos descubren que determinadas estrategias funcionan bien en un mercado plano, pero empiezan a perder dinero cuando el mercado se mueve. Del mismo modo, las estrategias adaptadas a las tendencias pierden eficacia en un mercado plano. Crear algoritmos capaces de reconocer los cambios de fase del mercado, antes de que las pérdidas empiecen a devorar los beneficios, requiere importantes recursos informáticos y tiempo.

Estos problemas obligan a los operadores a preguntarse constantemente: «¿Lo estoy haciendo todo bien?», «¿La reducción de hoy es normal o tengo que ajustar los algoritmos?», «¿Cómo puedo mejorar mis resultados?». Las respuestas a estas preguntas son importantes para tener éxito a largo plazo en el mercado. Los métodos para encontrarlos varían: algunos utilizan optimizadores de estrategias, otros aplican redes neuronales profundas, otros se basan en modelos matemáticos o en muchos años de experiencia. Todos estos enfoques pueden ser eficaces, ya que el propio mercado es el principal maestro.

El seguimiento del mercado se convierte a menudo en una fuente de estrategias innovadoras e ideas de inversión. En este artículo, crearemos un script que no sólo ayudará a mejorar la eficiencia de las operaciones, sino que también ofrecerá nuevas ideas para algoritmos basados en el análisis de datos. El análisis de operaciones históricas es imprescindible para mí, a pesar de que llevo mucho tiempo operando exclusivamente con EAs.

Pasemos a escribir un script que ayudará a simplificar considerablemente el proceso rutinario de análisis de las operaciones. Al final, el script debería mostrarnos la información en una pantalla de impresión, como se muestra en la Figura 1.

Figura 1. Visualización de datos del script

Figura 1. Visualización de datos del script

Empezaremos a implementar el script introduciendo los datos de entrada del usuario.


Entradas del script

La funcionalidad del script debe incluir la capacidad de cargar automáticamente datos sobre transacciones históricas en una pantalla de impresión de gráficos con la posibilidad de establecer varios marcos temporales para cada operación en un archivo independiente, así como proporcionar al usuario la opción de acceder a los datos de una operación individual o de todas las operaciones durante un periodo especificado por el usuario. También intentaré ofrecer la máxima posibilidad de personalizar los gráficos en las pantallas de impresión resultantes.

Para que el usuario pueda cambiar el script para descargar los datos de una operación o de un periodo de datos históricos, en primer lugar debemos proporcionar un tipo de datos personalizado del tipo enumerado enum de la siguiente forma:

enum input_method
  {
   Select_one_deal,
   Select_period
  };

La aplicación de nuestra enumeración personalizada constará únicamente de dos opciones anunciadas previamente: elegir un trato y elegir un periodo. Ahora podemos pasar a declarar la entrada a nivel global de la clase de memoria input:

input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

Para comodidad del usuario, aquí hemos proporcionado un bloque con nombre utilizando la palabra clave group para separar visualmente cada parámetro importante para el usuario y proporcionar las explicaciones necesarias. Además, comente las variables de clase de memoria input para que los nombres de las variables se sustituyan por comentarios de texto comprensibles para el usuario medio.

En la interfaz gráfica, la introducción de los valores de las variables tendrá el aspecto que se muestra en la Figura 2.

Figura 2. Interfaz de usuario para introducir las condiciones de descarga de datos

Figura 2. Interfaz de usuario para introducir las condiciones de carga de datos

Para que el usuario pueda obtener rápidamente información sobre una determinada operación, debe establecer la enumeración correspondiente en Select_one_deal en la variable inp_method. A continuación, el usuario deberá indicar el número (ticket) de la operación necesaria. Declare el bloque de entrada de la siguiente forma:

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

Pongamos un valor por defecto a la variable para que sirva de ejemplo al usuario, y por si acaso, indicar que se trata de un ticket de pedido por número global. Por regla general, el terminal muestra este número en el historial de operaciones, por lo que el usuario no debería tener dificultades con este asunto.

Pero si el usuario desea seleccionar un periodo para el análisis de modo que se descarguen todas las operaciones, es necesario proporcionar entradas correspondientes al valor del principio y el final del periodo de selección de operaciones en el historial. Esto puede implementarse a través de la siguiente entrada a nivel global:

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

El usuario introducirá la fecha de inicio de la muestra en la variable start_date, y la fecha de finalización del periodo de muestra en la variable finish_date. Inicialicemos también estas variables con valores por defecto para comodidad del usuario.

Ahora que tenemos claro qué operaciones hay que analizar, es hora de implementar un mecanismo que permita al usuario guardar los datos de una misma operación en varios gráficos. Esto será muy conveniente si, por ejemplo, durante la negociación manual, el operador utiliza un gráfico diario para determinar los niveles de negociación y busca un punto de entrada en los gráficos M30. Será mucho más cómodo analizar las operaciones históricas si nuestro script descarga inmediatamente un gráfico (tanto M30 como D1) con todos los datos.

También pondremos en práctica esta idea a través de las entradas y proporcionaremos no dos, sino cuatro gráficos para las operaciones con el fin de ampliar las capacidades del usuario, ya que algunos operadores utilizan más de dos gráficos en sus operaciones, pero muy pocos utilizan más de cuatro. En casos excepcionales, será posible ejecutar el script varias veces. Para ello, declara cuatro variables del tipo estándar enumerado ENUM_TIMEFRAMES, donde la variable main_graph denota el marco temporal de descarga principal, mientras que las restantes son auxiliares. Escribamos el código de la siguiente forma:

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

Los periodos seleccionados por el usuario pueden variar mucho en el tiempo, de hecho, para eso se han previsto en los ajustes anteriores. Por eso necesitamos también un desplazamiento del gráfico a la derecha, para que las imágenes se coloquen en el gráfico de la forma más cómoda para el usuario. Esto significa que si hacemos una pantalla de impresión del gráfico diario, unas pocas barras después de la operación pueden ser suficientes para que veamos dónde acabó yendo el precio después de la operación. Para plazos más pequeños, necesitaremos un ajuste correspondiente con un valor mucho más alto. Por lo tanto, proporcionaremos estos desplazamientos en los gráficos con los valores por defecto como en el código siguiente.

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

Para personalizar el gráfico y ahorrar pantallas de impresión en el terminal MetaTrader 5, podemos aplicar la funcionalidad de uso de plantillas o configurar todas las propiedades del gráfico implementando en el código operaciones con gráficos y asignar cada valor en una entrada de script independiente. Creo que, en el caso de mi solución, será más elegante y correcto no «inflar» la sección con las entradas del script al utilizar la configuración del gráfico, sino utilizar plantillas ya preparadas para trabajar con la presentación del gráfico en el terminal. Como resultado, simplemente escribiremos el nombre de una plantilla preparada previamente en la entrada para cada marco temporal, y el script trabajará con ella. También puede utilizar plantillas creadas previamente para que la visualización le resulte más familiar.

La presentación de los gráficos en el terminal MetaTrader 5 puede personalizarse para adaptarse a casi todos los gustos y preferencias. Pulsando F8 en el gráfico, podemos personalizar los modos de visualización, los objetos, la paleta de colores y mucho más. Cualquier configuración del gráfico puede modificarse rápida y cómodamente creando plantillas para distintas configuraciones de visualización. La opción del menú contextual Gráficos -> Plantillas -> Guardar plantilla/Cargar plantilla nos permite cambiar rápidamente la configuración de visualización del gráfico de precios incluso sin cambiar la ventana del gráfico activa. Como resultado, los ajustes de visualización de múltiples gráficos se ajustan a varias variables en función del número de marcos temporales analizados.

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

En la interfaz de usuario de las entradas del terminal, tendrá el aspecto que se muestra en la Figura 3:

Figura 3. Interfaz de usuario de las entradas de plantilla

Figura 3. Interfaz de usuario de las entradas de plantilla

Ahora que ya hemos decidido todos los ajustes estándar, vamos a añadir los ajustes relacionados específicamente con la visualización de información completa sobre las operaciones, con el fin de proporcionar al usuario toda la información necesaria para analizar sus operaciones comerciales. Se trata principalmente de objetos correspondientes al precio de apertura de la posición, Stop Loss, Take Profit y la línea de conexión. Las variables de tipo color correspondientes tendrán este aspecto:

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

En general, todas las entradas de nuestro script tendrán el aspecto que se describe a continuación:

#property copyright "Visit product page"
#property link      "https://www.mql5.com/ru/market/product/86223"
#property version   "1.00"
#property description "Make an automatic printscreen with a full description of all transactions for the period or 
			specify the ticket of the desired transaction."
#property script_show_inputs

enum input_method
  {
   Select_one_deal,
   Select_period
  };


input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

Hemos definido todas las variables a nivel global y ahora podemos proceder a implementar el código en el punto de entrada del script en OnStart(). Empecemos por definir todas las variables necesarias para almacenar, manejar y mostrar los datos que se enviarán al archivo de pantalla de impresión guardado. Notificaremos al usuario de cada paso al manejar el script.

Empecemos por informar al usuario de que el script se ha iniciado, restablecer la variable de error para que podamos comprobar correctamente el código de retorno de error si algo va mal, y proporcionar variables para todas las propiedades de posición, así como proporcionar el almacenamiento adecuado para recopilar información sobre todas las transacciones.

   Print("Script starts its work.");                                    // notified

   ResetLastError();                                                    // reset error

   string brok_name = TerminalInfoString(TERMINAL_COMPANY);             // get broker name
   long account_num = AccountInfoInteger(ACCOUNT_LOGIN);                // get account number

//---
   ulong    ticket = 0;                                                 // ticket
   ENUM_DEAL_ENTRY entry = -1;                                          // entry or exit
   long     position_id = 0,  PositionID[];                             // main id
   int      type = -1,        arr_type[];                               // deal type
   int      magic = -1,       arr_magic[];                              // magic number
   ENUM_DEAL_REASON      reason = -1,      arr_reason[];                // reason

   datetime time_open = 0,    arr_time_open[];                          // deal open time
   datetime time_close = 0,   arr_time_close[];                         // close time

   string   symbol,           arr_symbol[];                             // symbol
   string   comment,          arr_comment[];                            // comment
   string   externalID,       arr_extermalID[];                         // external id

   double   stop_loss = 0,    arr_stop_loss[];                          // deal Stop Loss
   double   take_profit = 0,  arr_take_profit[];                        // deal Take Profit
   double   open = 0,         arr_open[];                               // open price
   double   close = 0,        arr_close[];                              // close price
   double   volume = 0,       arr_volume[];                             // position volume
   double   commission = 0,   arr_commission[];                         // commission
   double   swap = 0,         arr_swap[];                               // swap
   double   profit = 0,       arr_profit[];                             // profit
   double   fee = 0,          arr_fee[];                                // fee

   int res = -1;                                                        // user command

Ahora podemos implementar la recogida de datos de las operaciones en función de si el usuario desea recibir una pantalla de impresión para una operación o para todas las operaciones dentro de un periodo determinado.


Selección de datos históricos por periodo

Vamos a implementar la selección del usuario a través del operador lógico de elección 'switch', que obtiene el valor de la variable global inp_method introducida y comienza a manejar desde la variante de caso Select_period para recoger los datos de las operaciones completadas dentro de un periodo determinado.

En primer lugar, informe al usuario de que en las entradas se ha seleccionado la opción de analizar operaciones dentro de un periodo. La información se implementa utilizando la función predefinida MessageBox() para llamar a la ventana de mensajes. El tercer parámetro es la constante MB_OKCANCEL que permite al terminal interrumpir la ejecución del script tras pulsar «cancelar». Esto es conveniente ya que el usuario puede terminar prematuramente el script y no esperar a su ejecución si accidentalmente introdujo la opción incorrecta en la entrada inp_method. A continuación se presenta el código completo.

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); // wait for user confirmation 

Colocaremos el resultado de manejar el evento de pulsar el botón en la variable 'res' para implementar el mecanismo de interrupción del script. Técnicamente, la forma más sencilla de interrumpir es a través de la sentencia return, si la variable res contiene el valor IDCANCEL, lo que significa que el usuario pulsó el botón correspondiente. El bloque se representa mediante el operador de elección lógica condicional if de la siguiente forma.

         if(res == IDCANCEL)                                            // if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // notify
            return;                                                     // do not continue
           }

Si en esta fase el usuario ha confirmado la validez de la opción elegida, comience a recopilar información sobre las operaciones completadas para el periodo histórico especificado. Realizaremos la selección de operaciones históricas utilizando la función predefinida HistorySelect(). Es esta función la que recibirá los valores de inicio y fin de periodo, introducidos y confirmados por el usuario anteriormente.

Después de solicitar ofertas históricas, sería muy apropiado organizar una comprobación de la presencia de ofertas en la cuenta dentro del periodo especificado por el usuario para la optimización del código y la comodidad del usuario. Coloque el número de tratos históricos obtenidos en la variable <total> a través de la función predefinida del terminal HistoryDealsTotal():

            int total = HistoryDealsTotal();                            // got the total number of deals

Si no hay nada que analizar en el periodo especificado y no se ha encontrado ni una sola operación, se notifica al usuario y se detiene el script. El evento también se gestiona mediante el operador de elección lógica condicional if, en el que informamos al usuario de la ausencia de tratos en el periodo especificado a través del registro del EA y la ventana de información. Interrumpa el script utilizando el operador return como se muestra a continuación:

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               return;
              }

Si se han encontrado acuerdos dentro del plazo, podemos empezar a recopilar datos. Itere a través de todas las ofertas obtenidas en el historial utilizando el bucle for como se muestra a continuación:

            for(int i=0; i<total; i++)                                  // iterate through the number of deals

Seleccione y solicite datos para cada operación histórica individual utilizando su ID/ticket único. Utilice la función predefinida del terminal HistoryDealGetTicket() para obtenerlo. Sus parámetros recibirán números de serie de 0 a 'total' y obtendrán el valor de retorno como un ID de trato único como se muestra a continuación. No olvide comprobar la validez del valor devuelto.

               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the ticket

Tras recibir el ticket histórico de operaciones, solicite tres funciones principales necesarias para recopilar datos generales de posición de las operaciones. Estas características incluyen: ID de una posición a la que pertenece una operación histórica, la característica ENUM_DEAL_ENTRY, que nos informa de qué rubricó exactamente la operación: apertura o cierre de una posición y un tipo de operación con la característica DEAL_TYPE que define la dirección y el tipo de orden. Las tres peticiones se ejecutan a través de la función terminal predefinida HistoryDealGetInteger() como se muestra a continuación:

                  //--- 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?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // deal type

La principal solicitud de estos datos se debe a que sus valores determinarán los valores que utilizaremos para obtener datos para la recogida de datos sobre toda la posición. Recordemos que los datos sobre la posición general se agregan a partir de un conjunto de datos sobre varias órdenes relacionadas específicamente con esta posición y se comparan entre sí basándose en el ID de la posición.

En primer lugar, tenemos que clasificar todas las operaciones históricas que no están directamente relacionadas con las operaciones comerciales, como: reposición de saldo, retirada de fondos, acumulación de bonificaciones del broker, etc. Para ello, disponga la siguiente comprobación en el código:

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )

Una vez comprobado que la operación que hemos recibido es una operación comercial, es decir, una compra o una venta, o los cierres correspondientes, que pueden variar en función del tipo de cuenta de un corredor o de un mercado, podemos empezar a recopilar datos sobre la posición en su conjunto. Teniendo en cuenta las peculiaridades del almacenamiento de datos sobre posiciones en las órdenes correspondientes, tomaremos algunas características de posición de las órdenes relacionadas con la apertura, mientras que otras características se toman de las órdenes relacionadas con las posiciones de cierre. Por ejemplo, los datos sobre el resultado financiero de una posición pueden encontrarse, como es lógico, en las órdenes de cierre de posición. Para presentar estos datos de forma más visual y clara, recopílalos en la siguiente tabla:

# Entrada de una posición (DEAL_ENTRY_IN) Salida de una posición (DEAL_ENTRY_OUT)
 1  open (DEAL_PRICE)  close (DEAL_PRICE)
 2  time_open (DEAL_TIME)  time_close (DEAL_TIME)
 3  symbol (DEAL_SYMBOL)  reason (DEAL_REASON)
 4  stop_loss (DEAL_SL)  swap (DEAL_SWAP)
 5  take_profit (DEAL_TP)  profit (DEAL_PROFIT)
 6  magic (DEAL_MAGIC)  fee (DEAL_FEE)
 7  comment (DEAL_COMMENT)  -
 8  externalID (DEAL_EXTERNAL_ID)  -
 9  volume (DEAL_VOLUME)  -
 10   commission (DEAL_COMMISSION)  -

Tabla 1. Fuentes de obtención de datos sobre toda la posición en función del tipo de operación

En el código, la solicitud y ordenación de los datos que se muestran en la Tabla 1 se verían así:

                     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 number
                        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); // 
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// fee
                       }

Una vez obtenidos preliminarmente los datos de posición, se debe organizar un contenedor para almacenar la información relevante. Al implementar esta funcionalidad, utilizaremos matrices unidimensionales estándar para cada característica. Para comprobar la presencia de una posición en el almacén, definiremos una pequeña función de plantilla Find(). Esta función se utilizará para comprobar si una posición está presente en el contenedor. La lógica es que pasamos el contenedor y el valor que queremos encontrar en él a los parámetros de la función. Como ya he mencionado, buscaremos el ID de la posición a la que pertenece la operación. Si se encuentra la posición, la función debe devolver el índice correspondiente. Si no, devuelve -1.

Considerando que cada propiedad de una misma posición necesita ser almacenada en diferentes formatos como cadena, valores enteros o valores fraccionarios, tiene sentido declarar la función Find() como sobrecargable a través de una plantilla. El lenguaje de programación MQL5 nos permite implementar esta funcionalidad de forma flexible y bastante cómoda a través de la palabra clave template. Esto nos permitirá declarar nuestra plantilla de función con el tipo de datos sobrecargable typename una vez, mientras que el compilador sustituirá automáticamente la implementación requerida para cada tipo de datos. Dado que no vamos a pasar tipos de datos personalizados allí, no habrá ningún problema con la fundición implícita de diferentes tipos, y no habrá necesidad de hacer ninguna sobrecarga de operadores. A continuación se muestra la implementación de la plantilla de la función personalizada Find().

template<typename A>
int               Find(A &aArray[],A aValue)
  {
   for(int i=0; i<ArraySize(aArray); i++)
     {
      if(aArray[i]==aValue)
        {
         return(i);                                                     // The element exists, return the element index
        }
     }
   return(-1);                                                          // No such element, return -1
  }

Utilizando la plantilla de función Find() declarada, complete la lógica comprobando si la posición actual se encuentra en el almacén. Si la función devuelve -1, entonces no hay ninguna posición en el almacén y hay que añadirla allí. Primero debe cambiarse la dimensión de almacenamiento:

                     //--- enter data into the main storage
                     //--- check if there is such id
                     if(Find(PositionID,position_id)==-1)               // if there is no such deal yet,                       {

Si dicho número existe en el almacenamiento, entonces accedemos a los datos de posición utilizando el índice devuelto por la función Find(). Esto se debe al hecho de que las órdenes en el historial de operaciones pueden encontrarse en órdenes diferentes si se negocian varios instrumentos en la cuenta al mismo tiempo. Por ejemplo, una posición puede abrirse en un instrumento más tarde que una orden anterior en otro instrumento. En consecuencia, puede cerrarse antes que la primera posición de símbolo. En general, la lógica de búsqueda y recopilación de información sobre las posiciones del periodo se presenta y resume a continuación.

      case  Select_period:                                              			// search within a period

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); 	// wait for user confirmation

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

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

         //--- select history data
         if(HistorySelect(start_date,finish_date))                      // select the necessary period in history
           {
            int total = HistoryDealsTotal();                            // got the total number of deals

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               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 ticket
                 {
                  //--- 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?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // entry or exit?

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )
                    {
                     //--- is it 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 number
                        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); // reason
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// 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 yet,
                       {
                        //--- change the size of containers
                        ArrayResize(arr_time_open,ArraySize(arr_time_open)+1);       
                        ArrayResize(arr_time_close,ArraySize(arr_time_close)+1);     
                        ArrayResize(arr_symbol,ArraySize(arr_symbol)+1);             
                        ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1);       
                        ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);   
                        ArrayResize(arr_open,ArraySize(arr_open)+1);                 
                        ArrayResize(arr_close,ArraySize(arr_close)+1);               
                        ArrayResize(PositionID,ArraySize(PositionID)+1);             

                        ArrayResize(arr_magic,ArraySize(arr_magic)+1);               
                        ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1);     
                        ArrayResize(arr_comment,ArraySize(arr_comment)+1);           
                        ArrayResize(arr_volume,ArraySize(arr_volume)+1);             
                        ArrayResize(arr_commission,ArraySize(arr_commission)+1);     
                        ArrayResize(arr_reason,ArraySize(arr_reason)+1);             
                        ArrayResize(arr_swap,ArraySize(arr_swap)+1);                 
                        ArrayResize(arr_profit,ArraySize(arr_profit)+1);             
                        ArrayResize(arr_fee,ArraySize(arr_fee)+1);                   

                        PositionID[ArraySize(arr_time_open)-1]=position_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 number
                           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 price
                           //---
                           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 found, search for the index

                        if(entry == DEAL_ENTRY_IN)                      // if this is 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;               // close price
                           //---
                           arr_magic[index]       = magic;              // magic number
                           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()); // notify
            printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__);       // notify
            MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
            return;
           }
         break;


Conclusión de la primera parte

En este artículo, hemos considerado la importancia del análisis histórico de las operaciones para operar con seguridad y a largo plazo en los mercados financieros. Un elemento clave de este análisis es el estudio de las transacciones históricas, que hemos empezado a aplicar en el script. Hemos considerado la formación de entradas y la implementación del algoritmo para seleccionar datos históricos sobre operaciones dentro del periodo seleccionado. También hemos implementado una plantilla de función sobrecargable para simplificar el manejo de los contenedores de datos.

En el próximo artículo, completaremos el script teniendo en cuenta el algoritmo de selección de datos para una única operación, así como el dibujo de gráficos y la implementación del código para mostrar los objetos de datos en los gráficos.

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

Archivos adjuntos |
DealsPrintScreen.mq5 (104.52 KB)
Operar con noticias de manera sencilla (Parte 2): Gestión de riesgos Operar con noticias de manera sencilla (Parte 2): Gestión de riesgos
En este artículo, se introducirá la herencia en nuestro código anterior. Se implementará un nuevo diseño de base de datos para brindar eficiencia. Además, se creará una clase de gestión de riesgos para abordar los cálculos de volumen.
Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión
Este artículo es el tercero de una serie de materiales sobre los principales aspectos de la programación en MQL5. Aquí nos encargaremos de tipos de datos complejos que no describimos en el artículo anterior, como estructuras, uniones, clases y el tipo de datos "función". También veremos cómo añadir modularidad a nuestro programa utilizando la directiva #include del preprocesador.
Desarrollo de un sistema de repetición (Parte 59): Un nuevo futuro Desarrollo de un sistema de repetición (Parte 59): Un nuevo futuro
La correcta comprensión de las cosas nos permite hacer más con menos esfuerzo. En este artículo, explicaré por qué es necesario ajustar la aplicación de la plantilla antes de que el servicio comience a interactuar realmente con el gráfico. Además, ¿qué tal si mejoramos el indicador del mouse para que podamos hacer más cosas con él?
Desarrollamos un asesor experto multidivisa (Parte 14): Cambio de volumen adaptable en el gestor de riesgos Desarrollamos un asesor experto multidivisa (Parte 14): Cambio de volumen adaptable en el gestor de riesgos
El gestor de riesgos que hemos desarrollado en los últimos artículos solo contiene funciones básicas. Hoy trataremos de analizar sus posibles formas de desarrollo, lo que nos permitirá aumentar los resultados comerciales sin interferir con la lógica de las estrategias de negociación.