LifeHack para traders: otimização "silenciosa" ou traço da distribuição de negociações
Tabela de conteúdos
- Para que são necessários os gráficos de distribuição?
- Inicialização rápida
- 1. Reconstrução da posição a partir do histórico de negociação
- 1.1. Inversão de posições
- 1.2. Cálculo do lucro de posição
- 1.3. Tempo de abertura de posição
- 1.4. Armazenamento intermediário das posições reconstruídas
- 2. Google Charts
- 2.1. Histograma (tipo 'bar')
- 2.2. Gráfico de pizza (tipo 'corechart')
- 2.3. Histograma (tipo 'bar') + gráfico de pizza (tipo 'corechart') + histograma (tipo 'bar')
- 3. Execução de gráficos de análise a partir do terminal (para a conta de negociação atual)
- 4. Execução de gráficos de análise a partir do testador de estratégias
- Isto é a otimização "silenciosa" sem barulho e poeira
- Alterações
Para que são necessários os gráficos de distribuição?
Ao desenvolver uma nova estratégia de negociação, não sabemos com antecedência quão bem-sucedida ela vai resultar. Quase sempre, o robô de negociação contém os parâmetros de entrada subjacentes às regras que serão usadas na geração de sinais para a entrada no mercado. Neste caso, após criar o robô de negociação, nós simplesmente confiaremos em que o testador de estratégias nos ajudará a encontrar as combinações de parâmetros de entrada que mostrarão bons resultados no histórico.
No entanto, este artigo oferece um olhar um pouco diferente para o processo de criação do robô de negociação.
Antes de executar a otimização de parâmetros de entrada, é possível simplesmente olhar para a distribuição de lucros e perdas, dependendo da hora de entrada. Afinal, muitas estratégias têm uns momentos "favoráveis" e outros "desfavoráveis" para entrar no mercado. Este artigo considera a criação de gráficos de distribuição de rentabilidade da posição (não das transações!) dependendo de seu tempo de abertura. Tendo estudado estes gráficos, você poderá olhar para sua estratégia de um ponto de vista ligeiramente diferente.
Os gráficos são traçados chamando a Google Charts e sua exibição visual é realizada usando o formato HTML. Esquematicamente, na página, os gráficos de distribuição são exibidos em forma de tabela:
Fig. 1. Aparência do relatório HTML
As duas primeiras linhas são as estatísticas de resumo para toda a conta de negociação, as linhas subsequentes representam as estatísticas para cada símbolo em termos de entradas por horas, dias e meses.
Inicialização rápida
O arquivo "DistributionOfProfits.mqh" deve ser colocado no diretório de dados, na pasta ...\MQL5\Include\. O script "test_report.mq5" chama a construção de gráficos de análise a partir da data especificada "start":
//+------------------------------------------------------------------+ //| test_report.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //--- input datetime start=D'2010.05.11 12:05:00'; #include <DistributionOfProfits.mqh> //--- CDistributionOfProfits Analysis; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Analysis.AnalysisTradingHistory(start); Analysis.ShowDistributionOfProfits(); } //+------------------------------------------------------------------+
1. Reconstrução da posição a partir do histórico de negociação
Para que é necessário reconstruir a posição? O fato é que esta é a única maneira de saber -em cada posição- o lucro e seu tempo de abertura. Definimos os termos:
posição rentável — lucro total de todas as negociação numa determinada posição;
tempo de abertura da posição — tempo de envio da primeira ordem nesta posição.
No terminal, o histórico de negociação é armazenado em forma de histórico de ordens e transações. Precisamos obter o histórico de posições. Para este efeito, acessamos o histórico de negociação e focamos na propriedade da transação DEAL_POSITION_ID e propriedade da ordem ORDER_POSITION_ID. Deixe-me explicar por que escolhemos essas propriedades. Na verdade, cada posição tem um identificador único (POSITION_IDENTIFIER) que se especifica em cada ordem (ORDER_POSITION_ID) e transação (DEAL_POSITION_ID) que a abriu, alterou ou fechou:
Fig. 2. Ligação entre POSITION_IDENTIFIER, DEAL_POSITION_ID e ORDER_POSITION_ID
Em outras palavras, se as transações com os mesmos DEAL_POSITION_ID são separadas do histórico de negociação, a reconstrução da posição pode ser garantida. Aqui é necessário mencionar a inversão da posição. A partir da documentação sobre o identificador de posição:
A inversão de uma posição altera seu identificador para o bilhete da ordem que levou à inversão.
Isto significa que se, durante a inversão, a posição alterar seu identificador, esta será uma posição diferente. Como é que, neste caso, o identificador é atribuído às transações? Será que a transação que levou à inversão pertence à posição anterior ou à invertida (nova)? Para responder a esta pergunta, eu escrevi um exemplo simples, trata-se do script position_reversal_v1.mq5.
Esse script executa três ações de negociação:
- buy 0.01 — apertura da posição
- sell 0.02 — inversão da posição
- fechamento da posição
Nos parâmetros de entrada, é necessário especificar o tempo de inicialização, pois ele será usado para pedir o histórico de negociação. No meu caso, executei o script na data 2016.09.05 às 10:32:59, enquanto nos parâmetros de entrada tinha uma pequena diferença e selecionei D'2016.09.05 10:32:00'.
Após cada ação, o script imprime a posição e seu indicador, bem com o histórico de transações e o DEAL_POSITION_ID da transação. Eis a impressão:
10:32:59.487 position_reversal_v1 (EURUSD,M3) Buy 0.01, "EURUSD" 10:33:00.156 position_reversal_v1 (EURUSD,M3) Position EURUSD POSITION_IDENTIFIER #96633525 10:33:00.156 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96633525 profit 0.00 10:33:00.156 position_reversal_v1 (EURUSD,M3) 10:33:06.187 position_reversal_v1 (EURUSD,M3) Sell 0.02, "EURUSD" 10:33:06.871 position_reversal_v1 (EURUSD,M3) Position EURUSD POSITION_IDENTIFIER #96633564 10:33:06.871 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96633525 profit 0.00 10:33:06.871 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.02 DEAL_POSITION_ID #96633525 profit -0.06 10:33:06.871 position_reversal_v1 (EURUSD,M3) 10:33:12.924 position_reversal_v1 (EURUSD,M3) PositionClose, "EURUSD" 10:33:13.593 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96633525 profit 0.00 10:33:13.593 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.02 DEAL_POSITION_ID #96633525 profit -0.06 10:33:13.593 position_reversal_v1 (EURUSD,M3) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96633564 profit -0.10 10:33:13.593 position_reversal_v1 (EURUSD,M3)
Primeira operação de negociação, buy 0.01. Seu POSITION_IDENTIFIER é igual a 96633525. No histórico de negociação existe uma transação, para ela é definido DEAL_POSITION_ID 96633525. Para esta transação, o DEAL_POSITION_ID coincide com o POSITION_IDENTIFIER da posição, o que significa que esta transação pertence à nossa posição.
Segunda operação, sell 0.02. Esta operação levou à inversão da posição: nós tínhamos a posição buy 0.01, realizamos sell 0.02, como resultado obtivemos sell 0.01. Na posição atual, o POSITION_IDENTIFIER mudou e se tornou igual a 96633564, o que indica que obtivemos uma nova posição. E, então, o que é que está acontecendo no histórico de negociações nesse momento? Vemos que nele já existem dois transações com o mesmo DEAL_POSITION_ID e iguais a 96633525. Além disso, na segunda transação vemos "profit -0.06".
Significa que a transação -que levou à inversão da posição- tem a ver com a posição anterior.
Terceira operação, buy 0.01. Nesta fase, já não existe a posição (uma vez que foi fechada por nós), enquanto, no histórico de negociações há três transações: a primeira e a segunda têm o mesmo DEAL_POSITION_ID e são guais a 96633525, no entanto na terceira este identificador foi alterado para "96633564", além de aparecer "profit -0.10". Ou seja, a terceira transação pertence à segunda posição obtida como resultado da inversão da primeira.
1.2. Cálculo do lucro de posição
Com base na informação comprovada na seção 1.1. , é possível determinar com precisão o algoritmo de cálculo de lucro para cada uma das posições reconstruídas. O resultado financeiro total de todas as transações com o mesmo DEAL_POSITION_ID será o lucro calculado da posição cujo POSITION_IDENTIFIER seja igual ao DEAL_POSITION_ID. Mas como as únicas transações relevantes são buy e sell, é necessário introduzir uma restrição quanto ao tipo de negociação, quer dizer, a partir da numeração ENUM_DEAL_TYPE, será preciso selecionar apenas as seguintes transações:
ENUM_DEAL_TYPE
Identificador | Descrição |
DEAL_TYPE_BUY | Compra |
DEAL_TYPE_SELL | Venda |
Alteramos um pouco nosso primeiro script e armazenamo-o sob o nome position_reversal_v2.mq5. No script position_reversal_v2.mq5 como sempre existem três blocos de negociação, isto é, buy 0.01, sell 0.02 e fechamento de posição. A novidade, neste script, é a função PrintProfitPositions(): nela é calculado o lucro para as posições reconstruídas. Vamos examinar essa função em detalhe:
//+------------------------------------------------------------------+ //| Print profit positions | //+------------------------------------------------------------------+ void PrintProfitPositions(void) { //--- structure profit positions; struct struct_positions { long position_id; double position_profit; //--- constructor struct_positions() {position_id=0; position_profit=0.0;} }; struct_positions arr_struct_positions[]; //--- request trade history HistorySelect(start,TimeCurrent()); uint total =HistoryDealsTotal(); ulong ticket =0; long deal_id =0; double profit =0; long type =0; //--- for all deals for(uint i=0;i<total;i++) { //--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) { //--- get deals properties deal_id =HistoryDealGetInteger(ticket,DEAL_POSITION_ID); profit =HistoryDealGetDouble(ticket,DEAL_PROFIT); type =HistoryDealGetInteger(ticket,DEAL_TYPE); //--- only buy or sell; apenas buy e sell if(type==DEAL_TYPE_BUY || type==DEAL_TYPE_SELL) { bool seach=false; int number=ArraySize(arr_struct_positions); for(int j=0;j<number;j++) { if(arr_struct_positions[j].position_id==deal_id) { arr_struct_positions[j].position_profit+=profit; seach=true; break; } } if(!seach) { ArrayResize(arr_struct_positions,number+1); arr_struct_positions[number].position_id=deal_id; arr_struct_positions[number].position_profit+=profit; } } } } //--- int number=ArraySize(arr_struct_positions); for(int i=0;i<number;i++) { Print("id ",arr_struct_positions[i].position_id," profit ",arr_struct_positions[i].position_profit); } }
Primeiro é declarada a estrutura struct_positions:
//--- structure profit positions; struct struct_positions { long id; double profit; //--- constructor struct_positions() {id=0; profit=0.0;} }; struct_positions arr_struct_positions[];
Na estrutura struct_positions dois campos:
id — identificador de posição;
profit — lucro da posição.
Imediatamente após isto, declaramos a matriz de estrutura arr_struct_positions[].
Abaixo está o bloco de variáveis auxiliares para acessar o histórico de transações:
//--- request trade history HistorySelect(start,TimeCurrent()); uint total =HistoryDealsTotal(); ulong ticket =0; long deal_id =0; double profit =0; long type =0;
Em seguida, o ciclo para chamar o histórico de transações:
//--- for all deals for(uint i=0;i<total;i++) { //--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) { ... //--- only buy or sell; apenas buy e sell if(type==DEAL_TYPE_BUY || type==DEAL_TYPE_SELL) { ... } } }
Ao mesmo tempo, conforme mencionado acima, chamamos a atenção apenas para as transações "Buy" e "Sell".
Eis o código que itera sobre a matriz de estruturas arr_struct_positions[], se, nessa matriz, for encontrada uma correspondência dos campos position_id e o DEAL_POSITION_ID da transação a partir do histórico de negociação, no respetivo índice da matriz de estruturas, o lucro da transação atual será somado:
bool seach=false; int number=ArraySize(arr_struct_positions); for(int j=0;j<number;j++) { if(arr_struct_positions[j].id==deal_id) { arr_struct_positions[j].profit+=profit; seach=true; break; } } if(!seach) { ArrayResize(arr_struct_positions,number+1); arr_struct_positions[number].id=deal_id; arr_struct_positions[number].profit+=profit; }
Se, na matriz de estruturas, não forem encontradas coincidências do campo id e o DEAL_POSITION_ID da transação, a matriz de estruturas é aumentada um elemento, e esse novo elemento é imediatamente preenchido com valores.
Após preencher a matriz de estruturas, este código itera sobre a matriz e imprime os valores de identificador de posição e seu lucro:
//--- int number=ArraySize(arr_struct_positions); for(int i=0;i<number;i++) { Print("id ",arr_struct_positions[i].id," profit ",arr_struct_positions[i].profit); }
1.3. Tempo de abertura de posição
Complementando o script position_reversal_v1.mq5, adicionamos a saída -a partir do histórico de negociação- de todas as ordens junto com seus parâmetros. Salvamos o script sob o nome position_reversal_v3.mq5.
2016.09.06 15:05:34.399 position_reversal_v3 (USDJPY,M1) Buy 0.01, "EURUSD" 2016.09.06 15:05:35.068 position_reversal_v3 (USDJPY,M1) Position EURUSD POSITION_IDENTIFIER #96803513 2016.09.06 15:05:35.068 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96803513 profit 0.00 2016.09.06 15:05:35.068 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.01 ORDER_POSITION_ID #96803513 ORDER_TICKET 96803513 2016.09.06 15:05:35.068 position_reversal_v3 (USDJPY,M1) 2016.09.06 15:05:41.088 position_reversal_v3 (USDJPY,M1) Sell 0.02, "EURUSD" 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) Position EURUSD POSITION_IDENTIFIER #96803543 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96803513 profit 0.00 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.02 DEAL_POSITION_ID #96803513 profit -0.08 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.01 ORDER_POSITION_ID #96803513 ORDER_TICKET 96803513 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.02 ORDER_POSITION_ID #96803543 ORDER_TICKET 96803543 2016.09.06 15:05:41.767 position_reversal_v3 (USDJPY,M1) 2016.09.06 15:05:47.785 position_reversal_v3 (USDJPY,M1) PositionClose, "EURUSD" 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96803513 profit 0.00 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.02 DEAL_POSITION_ID #96803513 profit -0.08 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Deal EURUSD volume 0.01 DEAL_POSITION_ID #96803543 profit -0.05 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.01 ORDER_POSITION_ID #96803513 ORDER_TICKET 96803513 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.02 ORDER_POSITION_ID #96803543 ORDER_TICKET 96803543 2016.09.06 15:05:48.455 position_reversal_v3 (USDJPY,M1) Order EURUSD initial_volume 0.01 ORDER_POSITION_ID #96803543 ORDER_TICKET 96803561
Esse script exibe claramente a seguinte citação a partir do guia:
O identificador de posição é um número exclusivo atribuído a cada posição reaberta e não se altera durante toda sua vida. Ele corresponde ao bilhete da ordem com ajuda do qual foi aberta a posição.
Que dizer, para definir o tempo de abertura da posição, basta encontrar, no histórico de negociação, a ordem cujo bilhete (ORDER_TICKET) seja igual ao identificador de posição (POSITION_IDENTIFIER) e obter o tempo da ordem encontrada (ORDER_TIME_DONE).
1.4. Armazenamento intermediário das posições reconstruídas
As posições reconstruídas serão armazenadas na matriz de estruturas struct_positions:
struct struct_positions { long id; datetime time; double loss; double profit; string symbol_name; //--- constructor struct_positions() {id=0; time=0; loss=0.0; profit=0.0; symbol_name=NULL;} };
Onde
id — identificador de posição;
time — tempo de abertura de posição;
loss — posição desfavorável, aqui, nesse campo, o registro terá o signo "+" (o que é necessário para uma melhor representação visual do gráfico)
profit — posição rentável
symbol_name — nome do símbolo de acordo com o qual é aberta a posição.
No futuro, ao construir diferentes gráficos, a matriz de estruturas struct_positions será usada como banco de dados do histórico de negociação que é consolidado na posição.
2. Google Charts
Para exibição de análises, será usado o serviço Google Charts. Para fazer isso, os gráficos serão colocados numa página HTML. A seguir, esta página será aberta no navegador definido -por padrão- no sistema operacional (usando o Win API da função ShellExecuteW).
No artigo, serão usados dois tipos de gráficos: histograma e pizza. Vamos olhá-los em detalhe.
O tipo 'bar' permite exibir os seguintes gráficos em uma página HTML:
Aqui está o código para o primeiro gráfico. Salve-o num arquivo com a extensão *.html (ou baixe o arquivo bar.html no final do artigo) e, em seguida, execute esse arquivo no seu navegador:
<html> <head> <!--Load the AJAX API--> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript"> google.charts.load('current', {'packages':['bar']}); google.charts.setOnLoadCallback(drawChart1); function drawChart1() { var data1 = google.visualization.arrayToDataTable([ ['Symbol', 'Profit', 'Loss'], ['Si-6.16', 82.00, 944.00], ['Si-9.16', 56.00, 11.00], ['SBRF-9.16', 546.00, 189.00], ]); var options1 = { chart: { title: 'Profit/loss by Symbols', subtitle: 'Summary', }, bars: 'vertical', vAxis: {format: 'decimal'}, width: 440, height: 400, colors: ['#5b9bd5', '#ed7d31', '#7570b3'] }; var chart = new google.charts.Bar(document.getElementById('chart_div1')); chart.draw(data1, options1); } </script> </head> <body> <!--Div that will hold the pie chart--> <div id="chart_div1"></div> <br/> </body> </html>
Para usar Google Charts, é necessário respeitar as seguintes regras sobre a colocação do código.
Os arquivos de carregador e de biblioteca são incluídos no bloco <head>:
<!--Load the AJAX API--> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript">
Em seguida, especifica-se o tipo de gráfico e função que contém os dados para desenhar (drawChart):
google.charts.load('current', {'packages':['bar']}); google.charts.setOnLoadCallback(drawChart1);
A função drawChart1() é composta por três blocos:
- "var data1" — bloco de dados segundo os quais é construído o gráfico
- "var options1" — bloco de opções que especificam as configurações do gráfico
- bloco que aponta para o recipiente para exibir o gráfico:
function drawChart1() { var data1 = google.visualization.arrayToDataTable([ ['Symbol', 'Profit', 'Loss'], ['Si-6.16', 82.00, 944.00], ['Si-9.16', 56.00, 11.00], ['SBRF-9.16', 546.00, 189.00], ]); var options1 = { chart: { title: 'Profit/loss by Symbols', subtitle: 'Summary', }, bars: 'vertical', vAxis: {format: 'decimal'}, width: 440, height: 400, colors: ['#5b9bd5', '#ed7d31', '#7570b3'] }; var chart = new google.charts.Bar(document.getElementById('chart_div1')); chart.draw(data1, options1); }
neste caso, há presença ou ausência da última vírgula na cadeia de caracteres
['SBRF-9.16', 546.00, 189.00],
não afeta o desempenho do código de página HTML, e isso simplifica grandemente o algoritmo para criar o bloco de dados. Por favor, note: uma vez construído gráfico do tipo 'bar', especificamos esse tipo como:
var chart = new google.charts.Bar(document.getElementById('chart_div1'));
O recipiente é localizado no <body>:
</script> </head> <body> <!--Div that will hold the pie chart--> <div id="chart_div1"></div> <br/> </body> </html>
2.2. Gráfico de pizza (tipo 'corechart')
Neste artigo vamos usar este diagrama:
O código da página HTML, que gera a criação do gráfico de pizza, é mostrado abaixo. Salve este código num arquivo com a extensão *.html (ou baixe o arquivo corechart.html no final do artigo) e, em seguida, execute esse arquivo no seu navegador.
<html> <head> <!--Load the AJAX API--> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript"> google.charts.load('current', {'packages':['corechart']}); google.charts.setOnLoadCallback(drawChart2); function drawChart2() { var data2 = google.visualization.arrayToDataTable([ ['Symbols', 'Profit'], ['Si-6.16', 82.00], ['Si-9.16', 56.00], ['SBRF-9.16', 546.00], ]); var options2 = { title: 'Profit by Symbols, %', pieHole: 0.4, width: 440, height: 400, }; var chart = new google.visualization.PieChart(document.getElementById('chart_div2')); chart.draw(data2, options2); } </script> </head> <body> <!--Div that will hold the pie chart--> <div id="chart_div2"></div> <br/> </body> </html>
Funcionalmente, os blocos são dispostos da mesma maneira como no exemplo acima, apenas com ligeiras alterações na representação de dados. Para o gráfico de pizza, os dados são especificados como segue:
var data2 = google.visualization.arrayToDataTable([ ['Symbols', 'Profit'], ['Si-6.16', 82.00], ['Si-9.16', 56.00], ['SBRF-9.16', 546.00], ]); var options2 = { title: 'Profit by Symbols, %', pieHole: 0.4, width: 440, height: 400, };
e já que estamos construindo um gráfico do tipo 'corechart', o bloco, que especifica o recipiente, tem a seguinte aparência:
var chart = new google.visualization.PieChart(document.getElementById('chart_div2'));
chart.draw(data2, options2);
2.3. Histograma (tipo 'bar') + gráfico de pizza (tipo 'corechart') + histograma (tipo 'bar')
O código deste exemplo é armazenado no arquivo bar_corechart_bar.html e está disponível para ser baixado no final do artigo. O exemplo em si parece com isso (imagem reduzida):
A configuração, onde diferentes tipos de gráficos estão presentes em simultâneo, é mais difícil, porque aqui é necessário distribuir adequadamente, na página, as funções function drawChart***() e blocos que apontam para o recipiente em que o gráfico será exibido. Ao colocar gráficos de vários tipos (por exemplo, histograma e gráfico de pizza) numa única página, eles são incluídos da seguinte forma:
google.charts.load('current', {'packages':['bar', 'corechart']});
O esquema geral do arquivo bar_corechart_bar.html será:
<html> <head> <!--Load the AJAX API--> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript"> google.charts.load('current', {'packages':['bar', 'corechart']}); google.charts.setOnLoadCallback(drawChart1); google.charts.setOnLoadCallback(drawChart2); google.charts.setOnLoadCallback(drawChart4); function drawChart1() { var data1 = ... var options1 = ... var chart = new google.charts.Bar(document.getElementById('chart_div1')); chart.draw(data1, options1); } function drawChart2() { var data2 = ... var options2 = ... var chart = new google.visualization.PieChart(document.getElementById('chart_div2')); chart.draw(data2, options2); } function drawChart4() { var data4 = ... var options4 = ... var chart = new google.charts.Bar(document.getElementById('chart_div4')); chart.draw(data4, options4); } </script> </head> <body> <!--Div that will hold the pie chart--> <table> <tr> <td><div id="chart_div1"></div></td> <td><div id="chart_div2"></div></td> <td><div id="chart_div4"></div></td> </table> <br/> </body> </html>
As funções drawChart1 e drawChart4 desenham histogramas, enquanto a função drawChart2, gráficos de pizza.
3. Execução de gráficos de análise a partir do terminal (para a conta de negociação atual)
A opção mais fácil é um pequeno script que após se juntar ao gráficos executa o navegador e exibe os gráficos de distribuição de análise dependendo da hora de entrada. O mais importante é indicar a data a partir da qual é preciso construir da análise (parâmetro de entrada "start"):
//+------------------------------------------------------------------+ //| test_report.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //--- input datetime start=D'2010.05.11 12:05:00'; #include <DistributionOfProfits.mqh> //--- CDistributionOfProfits Analysis; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Analysis.AnalysisTradingHistory(start); Analysis.ShowDistributionOfProfits(); } //+------------------------------------------------------------------+
Como, no arquivo "DistributionOfProfits.mqh", usa-se a chamada pelo sistema dll, ao executar o script de teste, é necessário permitir a importação dll:
4. Execução de gráficos de análise a partir do testador de estratégias
Por exemplo, vamos tomar o Expert Advisor "MACD Sample.mq5", que vem no conjunto padrão (diretório de dados\MQL5\Experts\Examples\MACD\MACD Sample.mq5). Copiamos este Expert Advisor para uma pasta separada para assegurar que as nossas alterações não afectarão o arquivo original. Em seguida, mudamos seu nome para "MACD Sample report.mq5". Fazemos as seguintes alterações no Expert Advisor:
//+------------------------------------------------------------------+ //| MACD Sample report.mq5 | //| Copyright 2009-2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2009-2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "5.50" #property description "It is important to make sure that the expert works with a normal" #property description "chart and the user did not make any mistakes setting input" #property description "variables (Lots, TakeProfit, TrailingStop) in our case," #property description "we check TakeProfit on a chart of more than 2*trend_period bars" #define MACD_MAGIC 1234502 //--- #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include <DistributionOfProfits.mqh> //--- input double InpLots =0.1; // Lots input int InpTakeProfit =50; // Take Profit (in pips) input int InpTrailingStop =30; // Trailing Stop Level (in pips) input int InpMACDOpenLevel =3; // MACD open level (in pips) input int InpMACDCloseLevel=2; // MACD close level (in pips) input int InpMATrendPeriod =26; // MA trend period //--- int ExtTimeOut=10; // time out in seconds between trade operations CDistributionOfProfits ExtDistribution; //+------------------------------------------------------------------+ //| MACD Sample expert class | //+------------------------------------------------------------------+ class CSampleExpert { protected:
No final do mesmo, adicionamos a função OnTester():
//+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- double ret=0.0; ExtDistribution.AnalysisTradingHistory(0); ExtDistribution.ShowDistributionOfProfits(); //--- return(ret); }
Antes de executar o testador, é preciso, nas configurações do terminal, permitir o uso de dll:
Como resultado, obtivemos uma página com gráficos de análise na qual eu sugiro que você confira o gráfico de distribuição de limites de lucro/perdas:
É claro a que horas são abertas as posições rentáveis e as não rentáveis. O gráfico que eu identifiquei dois períodos com perdas. O que acontecerá se nós limitarmos a negociação nas horas desfavoráveis especificadas? Ou talvez consigamos ir ainda mais longe, que dizer, reverter os sinais de negociação para os opostos, a fim de obter lucros em vez de perdas? Convido o leitor a verificá-lo sozinho.
Também é possível obter resultados interessantes se, no testador de estratégias, é executado um Expert Advisor multimoeda. Como modelo, usei a partir do CodeBase o EA gratuito Multicurrency Expert. Adicionalmente, no cabeçalho do EA foi registrado o arquivo "#include <DistributionOfProfits.mqh>", declarada a variável "CDistributionOfProfits ExtDistribution", enquanto, no final do código, foi adicionada a função "OnTester()". Após o teste único, é obtida a seguinte estatística: "TestAnalysis.htm".
Observe que todos os elementos gráficos são interativos. Por exemplo, no primeiro histograma, isto é, "lucro/perda de acordo com os símbolos (total)", ao colocar o cursor do mouse, é possível ver os números para cada símbolo:
Agora, os gráficos de pizza: eles mostram que contribuição foi feita por cada símbolo para o lucro e perda durante a negociação:
No resto dos diagramas, é possível ver o lucro de acordo com cada símbolo, dependendo do tempo de entrada na posição: distribuição por horas de entrada, por dias e meses.
Isto é a otimização "silenciosa" sem barulho e poeira
A tarefa do desenho de distribuições de transações foi concluída. Com isso, o histórico de negociação se consolida na posição, e todos os gráficos analíticos são representados exclusivamente para as posições. O lucro ou perda da posição se analisa por tempo de entrada, de três maneiras: por horas, por dias da semana e por meses.
Com a ajuda dos códigos-fonte fornecidos no artigo, qualquer estratégia pode ser verificada de acordo com as horas e dias favoráveis e desfavoráveis para negociar. E quem sabe, talvez, em vez de uma estratégia inicial, você obtenha um par de novas, por exemplo, uma estratégia de tendência e outra contra-tendência, ou você note qual sessão (asiática, europeia ou norte-americana) deve ser evitada, e não negociar. Sinta-se livre para tentar este método de otimização "silenciosa", ele não exige quaisquer execuções adicionais no testador de estratégias.
Alterações
Brevemente sobre as alterações que foram feitas no código, após a primeira publicação do artigo:
"DistributionOfProfits.mqh" v.1.027: é introduzida a proteção ao trabalhar com o programa durante a otimização. No construtor, o nome do arquivo é "Non File", enquanto nas duas funções públicas é verificada a constante MQL_OPTIMIZATION:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CDistributionOfProfits::CDistributionOfProfits(void) : m_name_file("Non File"), m_color_loss("ed7d31"), m_color_profit("5b9bd5"), m_width("440"), m_height("400"), m_data_number(1) { } //+------------------------------------------------------------------+ //| Analysis Trading History | //+------------------------------------------------------------------+ bool CDistributionOfProfits::AnalysisTradingHistory(const datetime start_time=0) { //--- if(MQLInfoInteger(MQL_OPTIMIZATION)) return(false); //+------------------------------------------------------------------+ //| Show Distribution Of Profits (start of the browser) | //+------------------------------------------------------------------+ void CDistributionOfProfits::ShowDistributionOfProfits(void) { //--- if(MQLInfoInteger(MQL_OPTIMIZATION)) return;
"DistributionOfProfits.mqh" v.1.033: agora é possível analisar as posições em dinheiro, pontos, ou em ambas as duas.
A seleção do tipo de análise é definido como segue:
//+------------------------------------------------------------------+ //| test_report.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //+------------------------------------------------------------------+ //| type of report | //+------------------------------------------------------------------+ enum ANALYSIS_TYPE { ONLY_MONEY=0, // in money ONLY_POINTS=1, // in points MONEY_AND_POINTS=2, // in money and points }; //--- input datetime start=D'2016.06.28 09:10:00'; input ANALYSIS_TYPE report_type=MONEY_AND_POINTS; //--- #include <DistributionOfProfits.mqh> //--- CDistributionOfProfits Analysis; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Analysis.SetTypeOfAnalysis(report_type); Analysis.AnalysisTradingHistory(start); Analysis.ShowDistributionOfProfits(); } //+------------------------------------------------------------------+
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2626
- 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 pela contribuição,
Excelente material.