
Visualizações de negociações no gráfico (Parte 1): Escolha do período para análise
Introdução
Neste artigo, vamos programar do zero um script para a visualização das negociações na análise retrospectiva das decisões de trading. Em outras palavras, se operamos manualmente e realizamos a análise das nossas entradas passadas no mercado para melhorar a eficácia de nossas negociações, queremos gastar o mínimo de tempo possível analisando o histórico manualmente e realizando o trabalho técnico relacionado a isso: abrir gráficos, encontrar negociações no histórico, salvar capturas de tela manualmente no terminal, desenhar manualmente Stop Loss e Take Profit nas negociações realizadas, reunir informações sobre comissões e swaps pagos. O script que vamos programar neste artigo ajudará a reduzir consideravelmente todo esse trabalho mecânico. Ao utilizar o script, montando-o a partir dos códigos-fonte deste artigo, você poderá reduzir significativamente o tempo dedicado ao trabalho técnico, permitindo focar mais na análise das decisões de trading. Para quem não quiser montar o projeto, uma versão pronta do script pode ser baixada na seção Market no site MQL5. A escrita do script será dividida em duas partes, mas o código-fonte será totalmente disponibilizado em cada parte.
Por que é importante a análise retrospectiva?
O objetivo principal de qualquer pessoa que entra no mercado é obter lucro de forma sustentável, com controle de risco. Esse tipo de negócio pode ser comparado a outros em que também lidamos com riscos. No entanto, como a prática mostra, a maioria dos novos negócios na economia real acaba falindo. Nos mercados financeiros, o processo de perda de capital pode acontecer muito mais rápido devido ao uso de alavancagem e à regressão completa das perdas sobre todo o capital disponível. Em um negócio real, se algo der errado, você pode reorientar as operações para outros objetivos (se não estiverem hipotecados) e começar de novo. No trading, porém, você é responsável por todas as perdas com o seu capital.
Estudos confirmam o conceito de que investir nos mercados financeiros sem controle de risco e com o uso excessivo de alavancagem pode tornar o investimento extremamente perigoso. Uma pesquisa da "Comissão de Valores Mobiliários dos EUA" (Release No. 34-64874, File Number: S7-30-11) ao longo de 17 anos mostrou que cerca de 70% dos traders perdem dinheiro a cada trimestre e que, em média, 100% dos investimentos são perdidos nos mercados financeiros em 12 meses:
" Approximately 70% of customers lose money every quarter and on average 100% of a retail customer‟s investment is lost in less than 12 months"
Em um ciclo anterior de artigos sobre a importância de equilibrar o risco e seguir do gerenciamento de risco, discutimos que investir sem controle de risco garante perdas, e que até uma estratégia inicialmente lucrativa pode se tornar deficitária sem um bom gerenciamento de risco. Nesta série de artigos, vamos abordar o aspecto de que o mercado é uma entidade muito flexível, que muda ao longo do tempo sob a influência de vários fatores econômicos e políticos. Para simplificar esse conceito, podemos dizer que o mercado pode estar em pelo menos duas fases – lateralização e tendência. Por isso, é muito importante analisar constantemente suas atividades de trading para garantir que sua estratégia esteja alinhada com as condições de mercado.
Muitos traders algorítmicos percebem que certas estratégias funcionam bem em mercados laterais, mas começam a gerar perdas quando o mercado se move. Da mesma forma, estratégias adaptadas a tendências perdem eficácia em mercados laterais. Para criar algoritmos capazes de reconhecer a mudança nas fases do mercado antes que as perdas comecem a consumir os lucros, são necessários recursos computacionais e tempo consideráveis.
Esses problemas fazem com que os traders se perguntem constantemente: "Estou fazendo tudo certo?", "A perda de hoje é normal ou preciso ajustar os algoritmos?", "Como posso melhorar os resultados?". As respostas a essas perguntas são cruciais para o sucesso de longo prazo no mercado. Os métodos para encontrá-las variam: alguns utilizam otimizadores de estratégias, outros aplicam redes neurais profundas, e outros se baseiam em modelos matemáticos ou em anos de experiência. Todos esses enfoques podem ser eficazes, pois o mercado em si é o maior professor.
A observação do mercado frequentemente se torna uma fonte de estratégias inovadoras e ideias de investimento. Neste artigo, criaremos um script que não apenas ajudará a melhorar a eficácia das negociações, mas também oferecerá novas ideias para algoritmos com base na análise de dados. Para mim, a análise das negociações históricas é obrigatória, apesar de eu negociar há muito tempo exclusivamente por meio de EAs.
Agora, vamos começar a escrever o script que ajudará a simplificar significativamente o processo rotineiro de análise de negociações. Nosso script deverá, no final, apresentar informações em uma captura de tela, conforme mostrado na Figura 1.
Figura 1. Exemplo de exibição de informações pelo script
A implementação desse script começará com a entrada de dados do usuário.
Parâmetros de entrada do script
A funcionalidade do script que planejamos deve incluir a capacidade de exportar automaticamente os dados das negociações históricas para capturas de tela do gráfico, com a possibilidade de configurar vários timeframes para cada negociação em arquivos separados, além de permitir ao usuário escolher entre exportar os dados de uma única negociação ou de todas as negociações em um período especificado. Também tentaremos maximizar as opções de personalização dos gráficos nas capturas de tela geradas.
Para permitir que o usuário alterne entre a exportação de dados de uma única negociação ou de um período histórico, primeiro precisamos definir um tipo de dado enumerado personalizado no formato enum da seguinte maneira:
enum input_method
{
Select_one_deal,
Select_period
};
A implementação do nosso enum personalizado se limitará às duas opções mencionadas anteriormente: escolha de uma única negociação ou de um período. Agora podemos declarar o parâmetro de entrada no nível global da classe de memória input:
input group "Enter deal ticket or select the period" input input_method inp_method = Select_period; // One deal or period?
Para a conveniência do usuário, aqui previmos um bloco nomeado usando a palavra-chave group, a fim de separar visualmente cada parâmetro importante para o usuário e fornecer as explicações necessárias. Além disso, por meio de comentários nas variáveis da classe de memória input, faremos com que os nomes das variáveis sejam substituídos por comentários textuais claros para o usuário.
Na interface gráfica, a entrada de valores dessa variável será exibida conforme mostrado na Figura 2.
Figura 2. Interface do usuário para entrada das condições de exportação das informações.
Para que o usuário possa rapidamente obter informações sobre uma única negociação, ele precisará definir o valor correspondente em inp_method com a opção Select_one_deal. Após isso, o usuário deverá informar o número (ticket) da negociação sobre a qual deseja obter informações. Vamos declarar este bloco de parâmetro de entrada da seguinte maneira:
input group "For case 'Select_one_deal': Enter ticket of deal" input long inp_d_ticket = 4339491; // Ticket (global id)
Vamos definir um valor padrão na variável, como exemplo para o usuário, e, por precaução, esclarecer que isso se refere ao ticket da ordem pelo número global. Geralmente, o terminal exibe esse número no histórico de negociações, então o usuário não deverá ter problemas com isso.
Mas se o usuário desejar escolher um período para análise, de modo que todas as negociações sejam exportadas, é necessário prever variáveis de entrada correspondentes ao início e término do período de seleção de negociações no histórico. Isso pode ser implementado com o seguinte código no nível 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
Na variável start_date, o usuário introduzirá a data de início da seleção, e na variável finish_date a data de término do período de seleção. Também inicializaremos essas variáveis com valores padrão para facilitar o uso.
Agora que definimos as negociações para análise, vamos implementar um mecanismo que permita ao usuário salvar os dados de uma negociação em vários gráficos. Isso será muito útil, por exemplo, quando, em operações manuais, o trader utiliza gráficos diários para definir os níveis de negociação e gráficos de 30 minutos para encontrar o ponto de entrada. Será muito mais fácil analisar suas negociações históricas se nosso script exportar imediatamente o gráfico diário e o gráfico de 30 minutos com todas as informações.
Vamos implementar essa ideia também por meio de parâmetros de entrada e prever não dois, mas quatro gráficos por negociação, já que alguns traders utilizam mais de dois gráficos em suas operações, embora muito poucos usem mais de quatro. Em casos excepcionais, será possível executar o script várias vezes. Para isso, vamos declarar quatro variáveis do tipo enumerado padrão ENUM_TIMEFRAMES, onde a variável main_graph representará o timeframe principal da exportação, e os outros serão adicionais. Esse código será escrito da seguinte 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
Os períodos selecionados pelo usuário podem variar bastante, e por isso foram incluídos nas configurações mencionadas anteriormente. Portanto, também precisamos prever um espaçamento no gráfico à direita para garantir que as imagens gráficas sejam organizadas de forma visualmente agradável, conforme as preferências do usuário. Isso significa que, se estivermos fazendo uma captura de tela de um gráfico diário, pode ser suficiente incluir apenas algumas barras após a negociação para ver para onde o preço se moveu após a operação. Para timeframes menores, será necessária uma configuração com valores de espaçamento bem maiores. Vamos incluir esses deslocamentos nos gráficos com valores padrão, conforme mostrado no código abaixo.
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 a aparência do gráfico ao salvar as capturas de tela no terminal Metatrader 5, podemos usar a funcionalidade de templates, ou programar manualmente a configuração de todas as propriedades do gráfico, colocando cada valor como um parâmetro de entrada do script. No entanto, para manter nosso código mais elegante e eficiente, não vamos "inchar" a seção de parâmetros de entrada com as configurações gráficas. Em vez disso, utilizaremos templates prontos para configurar a visualização dos gráficos no terminal. Assim, cada timeframe terá apenas o nome de um template preparado previamente, e o script trabalhará com ele. Você também poderá usar templates já existentes, caso queira manter uma exibição mais familiar.
A visualização dos gráficos no Metatrader 5 pode ser configurada para atender praticamente qualquer gosto ou preferência do trader. Ao pressionar F8 no gráfico, é possível ajustar modos de exibição, objetos, paletas de cores e muito mais. Qualquer configuração de gráficos pode ser alterada de forma rápida e conveniente, criando templates com várias configurações de exibição. O item do menu contextual "Gráficos" -> "Templates" -> "Salvar Template"/"Carregar Template" permite que você altere rapidamente a exibição do gráfico de preços sem mudar a janela aberta. No final, muitas configurações de exibição gráfica são acomodadas em algumas variáveis para o número de timeframes a serem analisados.
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
Na interface de parâmetros de entrada do terminal, isso será exibido como mostrado na Figura 3:
Figura 3. Interface do usuário para parâmetros de entrada dos templates.
Agora que definimos todas as configurações padrão, vamos complementar as configurações relacionadas à exibição completa das informações das negociações, para fornecer ao usuário todos os dados necessários para analisar suas operações de trading. Isso incluirá principalmente objetos como o preço de abertura da posição, Stop Loss, Take Profit e a linha de conexão. A declaração das variáveis correspondentes do tipo color será a seguinte:
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
De forma geral, todos os parâmetros de entrada do nosso script serão apresentados conforme descrito abaixo:
#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
Com todas as variáveis definidas no nível global, agora podemos começar a implementar o código no ponto de entrada do script, na OnStart(), e iniciar definindo todas as variáveis necessárias para armazenar, processar e exibir as informações que serão salvas no arquivo de captura de tela. Durante todo o processo, vamos manter o usuário informado sobre o progresso do script.
Começaremos informando o usuário sobre o início da execução do script, zerando a variável de erro para podermos verificar corretamente o código de retorno caso algo dê errado. Também definiremos variáveis para todas as propriedades das posições e reservaremos os respectivos armazenamentos para coletar informações sobre todas as negociações.
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
Agora podemos avançar para a implementação da funcionalidade de coleta de informações sobre as negociações, dependendo se o usuário deseja obter uma captura de tela de uma única negociação ou de todas as negociações em um determinado período.
Seleção de dados históricos por período
A implementação da escolha do usuário será feita por meio da estrutura de seleção lógica switch, onde passaremos o valor da variável global inp_method e começaremos o processamento com o caso Select_period, para coletar dados das negociações concluídas em um determinado período.
Primeiro, informaremos o usuário que a opção de análise de negociações por período foi selecionada nos parâmetros de entrada. Isso será feito por meio da função pré-definida MessageBox(). No terceiro parâmetro, usaremos a constante MB_OKCANCEL, para que, ao clicar no botão "cancelar", o terminal interrompa a execução do script. Isso será muito conveniente para que o usuário possa interromper a execução antecipadamente, caso tenha selecionado acidentalmente a opção errada no parâmetro de entrada inp_method. A estrutura completa está representada abaixo.
res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); // wait for user confirmation
O resultado da ação do botão clicado pelo usuário será armazenado na variável res, para podermos implementar o mecanismo de interrupção do script. A maneira mais simples de fazer isso tecnicamente é usando a instrução return, caso a variável res contenha o valor IDCANCEL, indicando que o usuário clicou no botão correspondente. Esse bloco será implementado por meio de uma estrutura condicional if, conforme mostrado a seguir.
if(res == IDCANCEL) // if interrupted by user { printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // notify return; // do not continue }
Se, nesse ponto, o usuário confirmar a escolha da opção desejada, podemos prosseguir com a coleta de informações sobre as negociações concluídas no período histórico especificado. A seleção das negociações históricas será executada com a função pré-definida HistorySelect(), à qual passaremos os valores de início e término do período que o usuário informou e confirmou anteriormente.
Após solicitar as negociações históricas, será muito útil realizar uma verificação para garantir que existam negociações na conta para o período especificado, visando otimizar o código e melhorar a experiência do usuário. Para isso, armazenaremos na variável total o número de negociações históricas obtidas, por meio da função pré-definida do terminal HistoryDealsTotal():
int total = HistoryDealsTotal(); // got the total number of deals
Se, no período informado pelo usuário, não houver negociações para analisar e nenhuma negociação for encontrada, será necessário informar o usuário e interromper a execução do script. A manipulação desse evento também será feita usando uma estrutura condicional if, na qual informaremos o usuário sobre a ausência de negociações no período especificado por meio do log do Expert e de uma janela de mensagem. A interrupção da execução do script será realizada com o operador return, conforme mostrado abaixo:
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; }
Caso negociações tenham sido encontradas para o período, podemos iniciar a coleta de informações. Para isso, vamos iterar por todas as negociações encontradas no histórico usando um loop for, conforme mostrado abaixo:
for(int i=0; i<total; i++) // iterate through the number of deals
A seleção e solicitação de dados para cada negociação histórica serão feitas com base no seu número identificador único - o ticket. Para obter esse número, utilizaremos a função pré-definida do terminal HistoryDealGetTicket(), passando os números sequenciais de 0 a total, e receberemos o valor retornado como o número identificador único da negociação, conforme mostrado a seguir. Não vamos esquecer de verificar se o valor retornado é válido.
//--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) // took the ticket
Após obter o ticket da negociação histórica, solicitaremos três características principais dela, sem as quais não poderemos reunir informações sobre a posição na totalidade. São elas: o número identificador da posição à qual a negociação histórica está vinculada, o sinal ENUM_DEAL_ENTRY, que nos indica se a negociação foi uma abertura ou fechamento de posição, e o tipo de negociação com o sinal DEAL_TYPE, que caracteriza a direção e o tipo da ordem. As três solicitações serão feitas por meio da função pré-definida HistoryDealGetInteger(), conforme mostrado a seguir:
//--- 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
A solicitação prioritária desses dados se deve ao fato de que os valores deles determinarão os dados que usaremos para coletar informações sobre a posição completa. Lembremos que as informações sobre uma posição são agregadas a partir da soma de informações de várias ordens relacionadas a essa posição e que se correlacionam com base no identificador da posição.
Primeiro, precisamos filtrar todas as negociações históricas que não estão diretamente relacionadas a operações de trading, como: depósitos, retiradas, bônus de corretoras, etc. Para isso, organizaremos a seguinte verificação no 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 )
Após verificarmos que a negociação obtida está relacionada a uma operação de trading (compra ou venda), ou os fechamentos correspondentes, dependendo do tipo de conta ou da plataforma de negociação, podemos começar a reunir dados sobre a posição na totalidade. Levando em consideração as particularidades do armazenamento de dados sobre posições nas ordens correspondentes, algumas informações sobre a posição serão obtidas nas ordens de abertura e outras nas ordens de fechamento. Por exemplo, os dados sobre o resultado financeiro da posição logicamente e previsivelmente serão encontrados nas ordens de fechamento dessa posição. Para apresentar esses dados de forma mais clara e visual, reuniremos as informações na seguinte tabela:
№ p/p | Entrada na posição (DEAL_ENTRY_IN) | Saída da posição (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) | - |
Tabela 1. Fontes de obtenção de informações sobre toda a posição, dependendo do tipo de negociação.
No código, a solicitação e a ordenação das informações, mostradas na Tabela 1, serão implementadas da seguinte maneira:
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 }
Quando os dados da posição forem obtidos preliminarmente, será necessário organizar um contêiner para armazenar as informações correspondentes. Para implementar essa funcionalidade, utilizaremos arrays unidimensionais padrão para cada característica, e para verificar a existência de uma posição no armazenamento, definiremos uma pequena função de modelo chamada Find(). Essa função servirá para verificar se a posição já está presente no contêiner. A lógica é que passaremos o contêiner como parâmetro e o valor que desejamos encontrar nele. O valor a ser procurado será o número identificador da posição à qual a negociação pertence. Se a posição for encontrada, a função retornará o índice correspondente; se não for encontrada, retornará -1.
Dado que cada propriedade de uma posição individual precisa ser armazenada em diferentes formatos (como string, valores inteiros ou decimais), faz sentido declarar a função Find() como uma função sobrecarregada usando um template. A linguagem de programação MQL5 permite implementar essa funcionalidade de forma flexível e conveniente usando a palavra-chave template. Isso nos permitirá declarar o template da função uma vez, com o tipo de dado sobrecarregado typename, e o compilador se encarregará de fornecer a implementação correta para cada tipo de dado. Como não passaremos tipos de dados personalizados, não haverá problemas com a conversão implícita de diferentes tipos, e não será necessário fazer sobrecarga de operadores. A implementação do template da função personalizada Find() é apresentada abaixo.
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 }
Usando o template declarado da função Find(), completaremos a lógica com a verificação da presença da posição atual no contêiner. Se a função retornar -1, isso significa que a posição não está no armazenamento, e então ela deverá ser adicionada, ajustando previamente o tamanho do contêiner:
//--- 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, {
Se o número já estiver presente no contêiner, acessaremos os dados dessa posição usando o índice retornado pela função Find(). Isso ocorre porque, na história de negociações, as ordens podem estar em ordem diferente se houver várias negociações ocorrendo simultaneamente em diferentes instrumentos. Por exemplo, uma posição aberta em um instrumento pode aparecer após uma ordem anterior de outro instrumento, sendo que a primeira posição pode ser fechada antes da posição relacionada ao segundo símbolo. A lógica geral de busca e coleta de informações sobre posições em um período é apresentada e resumida abaixo.
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;
Conclusão da primeira parte
Neste artigo, enfatizamos a importância da análise retrospectiva do trading para uma operação segura e de longo prazo nos mercados financeiros. O elemento-chave dessa análise é o estudo das negociações no histórico, que começamos a implementar neste script. Discutimos a criação dos parâmetros de entrada e a implementação do algoritmo de seleção de dados históricos das negociações em um período especificado. Também criamos um template de função sobrecarregada para facilitar o trabalho com contêineres de dados.
No próximo artigo, concluiremos a escrita do script, abordando o algoritmo de seleção de dados de uma única negociação, o processo de desenho de gráficos e o código para exibir objetos informativos nos gráficos.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/14903





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Obrigado pelo artigo!
Veja como usar estruturas para armazenar conjuntos semelhantes de informações. Em vez de matrizes separadas para cada parâmetro de transação, você poderia definir um tipo de dados diferente para cada parâmetro que inclua todos os parâmetros e, em seguida, armazenar os elementos do nosso novo tipo em uma única matriz. Isso pode ser mais conveniente.
Muito obrigado por seu comentário! Concordo que, por meio de classes ou tipos de dados de estrutura personalizada, será mais conciso e provavelmente mais correto. Com certeza usarei esse conselho no futuro :)
Uau, será que é realista fazer upload de ofertas para, digamos, um canal do Telegram? Telas)
Aqui está uma bíblia, muito conveniente para enviar mensagens ou capturas de tela para o canal do Telegram. Eu o utilizo há muito tempo.
Mas agora o ChartScreenShot está quebrado, os objetos que são rotulados são movidos para o lado.
Tenho que pensar em como criar telas com algum software de terceiros em vez do padrão.
Então, aqui está uma bíblia, muito conveniente para enviar mensagens ou capturas de tela para o carrinho. Eu a utilizo há muito tempo.
Mas agora o ChartScreenShot está quebrado, os objetos que são rotulados são movidos para o lado.
Tenho que pensar em como criar telas com algum software de terceiros em vez do padrão.
Obrigado pelo link. Com certeza vou ler.
Brilhante
Obrigado, senhor!