
Visualización de transacciones en un gráfico (Parte 1): Seleccionar un periodo para el análisis
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
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 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
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





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso