Descargar MetaTrader 5

LifeHack para tráders: Optimización "silenciosa" o Trazando la distribución de trades

21 octubre 2016, 15:20
Vladimir Karputov
0
1 686

Índice


¿Para qué necesitamos los gráficos de distribución?

A la hora de desarrollar una nueva estrategia comercial, no sabemos de antemano el éxito que va a tener. El robot comercial casi siempre contiene los parámetros de entrada que se encuentran en la base de las normas conforme a las cuales se generarán las señales de entrada al mercado. En este sentido, sencillamente solemos esperar que después de escribir un robot comercial, el simulador de estrategias nos ayudará a encontrar combinaciones de parámetros de entrada que demuestren buenos resultados en la historia.

Pero en este artículo, propongo echar un vistazo al proceso de creación del robot desde un punto de vista un tanto distinto. Antes de iniciar la optimización de los parámetros de entrada, simplemente podemos echar un vistazo a la distribución de beneficios y pérdidas en función de la hora de entrada. Y es que para muchas estrategias existen momentos "favorables" y "desfavorables" a la hora de entrar en el mercado. En el artículo se analiza la construcción de los gráficos de distribución de la rentabilidad de las posiciones (¡atención, no operaciones, sino precisamente posiciones!) en función de su hora de apertura. Tras estudiar estos gráficos, comenzará a ver su estrategia desde un ángulo un tanto diferente.

Los gráficos se construyen mediante la llamada de Google Charts. En cuanto al formato de representación visual, se ha elegido HTML. De forma esquemática, los gráficos de distribución en la página se representan en forma de recuadro:

 

Fig. 1. Aspecto de un informe en HTML  

Las dos primeras líneas son una estadística avanzada para la cuenta comercial al completo, las líneas siguientes representan las estadísticas de cada símbolo en un segmento de entradas por horas, días y meses.


Inicio rápido

El archivo "DistributionOfProfits.mqh" debe ubicarse en el catálogo de datos, en la carpeta ...\MQL5\Include\. El script "test_report.mq5" ejecuta la llamada de la construcción de los gráficos de análisis comenzando por la fecha indicada "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. Reconstrucción de las posiciones de la historia comercial

¿Para qué necesitamos reconstruir posiciones? El asunto es que solo así se puede saber de qué posición son parámetros tan importantes como el beneficio y la hora de apertura. Vamos a definir los términos:

beneficio de la posición — beneficio sumado de todas las operaciones en esta posición;

hora de apertura de la posición — hora del envío de la primera orden en esta posición.

En el terminal, la historia comercial se guarda en forma de historia de órdenes y operaciones. Nosotros necesitamos obtener precisamente la historia de las posiciones. Para ello, recurriremos a la historia comercial y nos orientaremos por la propiedad de la operación DEAL_POSITION_ID y la propiedad de la orden ORDER_POSITION_ID. Aclaremos por qué hemos elegido precisamente esta propiedad. El asunto es que cada posición tiene un identificador único (POSITION_IDENTIFIER), que se indica en cada orden (ORDER_POSITION_ID) y operación (DEAL_POSITION_ID) que la ha abierto, cambiado o cerrado:

position deals orders 

Fig. 2. Conexión de POSITION_IDENTIFIER, DEAL_POSITION_ID y ORDER_POSITION_ID

En otras palabras, si destacamos en la historia comercial operaciones con DEAL_POSITION_ID idénticas, podremos reconstruir la posición de forma garantizada. Aquí debemos recordar el viraje de posición. Sacado del manual de ayuda sobre el identificador de posición:

La rotación (viraje) de posición cambia su identificador al ticket de la orden, como resultado de la cual ha sucedido la rotación.

1.1. Viraje de posición

Si la posición cambia su identificador durante el viraje, entonces ya se tratará de otra posición. ¿Cómo tiene lugar la asignación del identificador en este caso? ¿La operación que ha provocado el viraje se referirá a la posición anterior o a la virada (nueva)? Para responder a esta pregunta, he escrito un ejemplo muy sencillo: el script position_reversal_v1.mq5

Este script ejecuta tres acciones comerciales:

  • buy 0.01 — apertura de posición
  • sell 0.02 — viraje de posición
  • cierre de posición

En los parámetros de entrada del script se debe indicar la hora de comienzo, este parámetro se usará para solicitar la historia comercial. En mi ejemplo, he iniciado el script el 2016.09.05 a las 10:32:59, en los parámetros de entrada he dejado un pequeño hueco y he elegido D'2016.09.05 10:32:00'.

Después de cada acción, el script imprime la posición y su identificador, así como la historia de operaciones y la DEAL_POSITION_ID de la operación. Y bien, la impresión:

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)    

Primera operación comercial — buy 0.01. Su POSITION_IDENTIFIER es igual a 96633525. Asimismo, en la historia comercial hay una operación, para ella se ha establecido una DEAL_POSITION_ID 96633525. Para esta operación, la DEAL_POSITION_ID coincide con el POSITION_IDENTIFIER de la posición, eso significa que esta operación pertenece a nuestra posición. 

Segunda operación — sell 0.02. Esta operación ha llevado al viraje de posición: teníamos la posición buy 0.01, hemos ejecutado sell 0.02, como resultado, hemos obtenido sell 0.01. El POSITION_IDENTIFIER de la posición actual ha cambiado y ahora es igual a 96633564, eso significa que hemos obtenido una nueva posición. ¿Y qué es lo que podemos ver en la historia de operaciones? Vemos que en la historia comercial ya hay dos operaciones, ambas tienen una DEAL_POSITION_ID idéntica, igual a 96633525. Además, la segunda operación tiene "profit -0.06".

Significa que la operación que ha llevado al viraje de posición se refiere a la posición anterior.

Tercera operación — buy 0.01. En esta etapa ya no hay posición (puesto que la hemos cerrado), y en la historia comercial hay tres operaciones: la DEAL_POSITION_ID de la primera y la segunda son idénticas e iguales a 96633525, pero en la tercera operación, el identificador ha cambiado a "96633564", además, ha aparecido "profit -0.10". Es decir, la tercera operación está relacionada ya con la segunda posición, obtenida como resultado del viraje con la primera.

1.2. Cálculo del beneficio de la posición

Apoyándonos en la información comprobada en el apartado 1.1. podemos definir el algoritmo de cálculo del beneficio para cada posición recosntruida. El resultado financiero sumado de todas las operaciones con DEAL_POSITION_ID idénticas será precisamente el beneficio calculado de la posición cuyo POSITION_IDENTIFIER sea igual a DEAL_POSITION_ID. Pero como a nosotros solo nos interesan las operaciones comerciales buy y sell, entonces hay que introducir una limitación del tipo de operación, en la enumeración ENUM_DEAL_TYPE habrá que quitar solamente las siguientes operaciones:

ENUM_DEAL_TYPE

Identificador

Descripción

DEAL_TYPE_BUY

Compra

DEAL_TYPE_SELL

Venta

 

Modificamos ligeramente nuestro primer script y lo guardamos con el nombre position_reversal_v2.mq5. En el script position_reversal_v2.mq5 siguen estando presentes tres bloques comerciales — buy 0.01, sell 0.02 y las posiciones cerradas. La parte nueva en este script es la función PrintProfitPositions(): en ella se calcula el beneficio de las posiciones reconstruidas. Vamos a detenernos con más detalle en esta función:

//+------------------------------------------------------------------+
//| 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; только buy и 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);
     }
  }

Al inicio se declara la estructura 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[];

En la estructura struct_positions hay dos campos:

id — identificador de la posición;

profit — beneficio de la posición.

Justo después, declaramos la matriz de estructuras arr_struct_positions[].

Más abajo viene el bloque de variables auxiliares para recurrir a la historia de operaciones:

//--- request trade history 
   HistorySelect(start,TimeCurrent());
   uint     total    =HistoryDealsTotal();
   ulong    ticket   =0;
   long     deal_id  =0;
   double   profit   =0;
   long     type     =0;

A continuación, viene el ciclo para recurrir a la historia de operaciones:

//--- for all deals 
   for(uint i=0;i<total;i++)
     {
      //--- try to get deals ticket 
      if((ticket=HistoryDealGetTicket(i))>0)
        {
...
         //--- only buy or sell; только buy и sell
         if(type==DEAL_TYPE_BUY || type==DEAL_TYPE_SELL)
           {
...
           }
        }
     }

Además, no debemos olvidar que, como ya hemos mencionado más arriba, prestaremos atención solo a las operaciones "Buy" y "Sell".

Aquí tenemos el código en el que se realiza la iteración por la matriz de estructuras arr_struct_positions[] — si en esta matriz se localiza una coincidencia entre los campos position_id y DEAL_POSITION_ID de la operación de la historia comercial, entonces en el índice correspondiente de la matriz de estructuras se suma el beneficio de la operación actual:

            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;
              }

Si en la matriz de estructuras no se han encontrado coincidencias del campo id y la DEAL_POSITION_ID de la operación, entonces la matriz de estructuras aumenta en un elemento, y este nuevo elemento se rellena de inmediato con valores.

Después de rellenarse la matriz de estructuras, se produce la iteración de esta matriz y se imprimen los valores del identificador de la posición y su beneficio: 

//---
   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. Hora de apertura de la posición

Complementamos el script position_reversal_v1.mq5: añadimos la muestra desde la historia comercial de todas las órdenes junto con sus parámetros. Guardamos el script con el nombre 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

Este script demuestra de forma muy visual la siguiente cita del manual de ayuda :

El identificador de posición es un número único que se adjudica a cada una de las posiciones abiertas de nuevo, y no se modifica a lo largo de su existencia. Se corresponde con el ticket de la orden con la que se abrió la posición

Esto significa que para definir la hora de apertura de una posición, simplemente hay que encontrar en la historia comercial la orden cuyo ticket (ORDER_TICKET) sea igual al identificador de la posición (POSITION_IDENTIFIER) y obtener la hora de la orden encontrada (ORDER_TIME_DONE).

1.4. Almacenamiento intermedio de las posiciones reconstruidas

Las posiciones reconstruidas se guardarán en la matriz de estructuras 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;}
  };

donde

id — identificador de la posición;

time — hora de apertura de la posición;

loss — péridas de la posición, además, la información en su campo se escribirá con el signo "+" (esto es necesario para que la representación visual de los gráficos sea mejor)

profit — beneficio de la posición

symbol_name — nombre del símbolo del que se ha abierto la posición.

En lo sucesivo, al construir diferentes gráficos, precisamente la matriz de estructuras struct_positions se usará como base de datos de la historia comercial, que está consolidada en la posición.


2. Google Charts

El servicio Google Charts se utilizará para representar el análisis, para ello, los gráficos se ubicarán en una página HTML. Después esta página se abrirá en el navegador establecido por defecto en el sistema operativo (con la ayuda de la función Win API ShellExecuteW).

En el artículo se usarán dos tipos de diagrama: histograma y circular. Vamos a analizarlos con más detalle.

2.1. Histograma (tipo 'bar')

El tipo 'bar' permite representar en una página HTML estos gráficos:

bar  bar1

Aquí tenemos el código para el primer gráfico. Guárdelo en un archivo con la extensión *.html (o descargue el archivo bar.html al final de artículo) e inicie este archivo en el 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 hay que respetar las normas descritas más arriba sobre la ubicación del código.

En el bloque <head> se incluyen los archivos del cargador y las bibliotecas:

    <!--Load the AJAX API-->
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">

A continuación, se indica el tipo de gráfico y la función en la que se encuentran los datos para el dibujado (drawChart):

      google.charts.load('current', {'packages':['bar']});
      google.charts.setOnLoadCallback(drawChart1);

La propia función drawChart1() contiene tres bloques:

  • "var data1" — bloque de datos sobre cuya base se construye el gráfico
  • "var options1" — bloque de opciones que concretan los parámetros del gráfico
  • bloque que indica el contenedor en el que se representará el 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);
      }

además la presencia o ausencia de la última coma en la línea

          ['SBRF-9.16', 546.00, 189.00],

no influye en el funcionamiento del código de la página HTML, lo que simplifica significativamente el algoritmo de cración del bloque de datos. Preste atención: dado que estamos construyendo un gráfico del tipo 'bar', indicaremos este tipo así:

        var chart = new google.charts.Bar(document.getElementById('chart_div1'));

El propio contenedor se escribe en <body>:

    </script>
  </head>

  <body>
    <!--Div that will hold the pie chart-->
      <div id="chart_div1"></div>
    <br/>
  </body>
</html>

2.2. Diagrama circular (tipo 'corechart')

En el artículo se usará este diagrama:

corechart 

El código de la página HTLM que genera la creación del diagrama circular se muestra más abajo. Guarde este código en un archivo con la extensión *.html (o descargue el archivo corechart.html al final del artículo) e inicie este archivo en el 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>

En cuanto a la funcionalidad, los bloques han sido ubicados de la forma descrita en el ejemplo de arriba, modificando solo la presentación de los datos. Para el diagrama circular, los datos se indican así:

        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,
        };

y puesto que estamos construyendo un gráfico del tipo 'corechart', el bloque que indica el contendor tiene el aspecto siguiente:

        var chart = new google.visualization.PieChart(document.getElementById('chart_div2'));
        chart.draw(data2, options2);

2.3. Histograma (tipo 'bar') + diagrama circular (tipo 'corechart') + histograma (tipo 'bar')

El código de este ejemplo se ha guardado en el archivo bar_corechart_bar.html y está disponible para descargar al final del artículo. El propio ejemplo tiene el aspecto siguiente (la imagen ha sido reducida): 

bar+ corechart + bar 

La configuración en la que están a la vez presentes diferentes tipos de gráfico es un caso más complejo, puesto que aquí hay que distribuir correctamente en la página las funciones function drawChart***() y los bloques que indican el contenedor en el que se representará el gráfico. Al ubicar en una página gráficos de varios tipos (por ejemplo, histogramas y un diagrama circular) su conexión tendrá el aspecto siguiente:

      google.charts.load('current', {'packages':['bar', 'corechart']});

El esquema general del archivo bar_corechart_bar.html será así:

<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>

Las funciones drawChart1 y drawChart4 dibujan los histogramas, la función drawChart2, el diagrama circular.


3. Inicio de los gráficos de análisis desde el terminal (para la cuenta comercial actual)

La variante más sencilla es un pequeño script que después de conectarse al gráfico inicia el navegador y representa los gráficos de análisis de la distribución de trades en función de la hora de entrada. Lo importante es indicar la fecha a partir de la cual se deberá construir el análisis (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();
  }
//+------------------------------------------------------------------+

Puesto que en el archivo "DistributionOfProfits.mqh" se usa la llamada del dll de sistema, al iniciar el script de prueba, hay que permitir la importación de dll:

dependencies 


4. Inicio de los gráficos de análisis desde el simulador de estrategias

Como ejemplo, tomaremos el asesor "MACD Sample.mq5", que se incluye en el suministro estándar (catálogo de datos\MQL5\Experts\Examples\MACD\MACD Sample.mq5). Copiamos este experto en una carpeta aparte, para que nuestros cambios no afecten al archivo original. Ahora lo renombramos a "MACD Sample report.mq5". Introducimos esos cambios en el asesor:

//+------------------------------------------------------------------+
//|                                           MACD Sample report.mq5 |
//|                   Copyright 2009-2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2009-2016, MetaQuotes Software Corp."
#property link        "http://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:

Al final del todo, añadimos la función OnTester():

//+------------------------------------------------------------------+
//| Tester function                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//---
   double ret=0.0;
   ExtDistribution.AnalysisTradingHistory(0);
   ExtDistribution.ShowDistributionOfProfits();
//---
   return(ret);
  }

Antes de iniciar el simulador, hay que permitir el uso de dll en los ajustes del terminal:

options ea 

Como resultado hemos obtenido una página con gráficos de análisis, en la que propongo echar un vistazo al gráfico de distribución del benefico/pérdidas por horas:

profit/loss hours

Se ve muy bien a qué horas se han abierto las posiciones rentables y a qué horas se han abierto las no rentables. En el gráfico he marcado los dos periodos temporales con pérdidas. ¿Qué pasará si limitamos el comercio en las horas desfavorables que hemos indicado? ¿O podríamos ir incluso más allá e invertir las señales comerciales para obtener benefecios en lugar de pérdidas? Le propongo al lector que compruebe esto por sí mismo.

Se pueden obtener resultados así de interesantes si iniciamos en el simulador de estrategias un experto multidivisa. Como muestra, he tomado de CodeBase el asesor Multicurrency Expert. Asimismo, en el encabezamiento se ha escrito el archivo "#include <DistributionOfProfits.mqh>", se ha declarado la variable "CDistributionOfProfits ExtDistribution", y al final del código se ha añadido la función "OnTester()". Después de una única simulación, se han obtenido estas estadísticas: "TestAnalysis.htm".

Preste atención a que todos los gráficos resultan interactivos. Por ejemplo, en el primer histograma - "Beneficio/pérdidas de los símbolos (de forma sumada)", al colocar el cursor del ratón se pueden ver los índices de cada símbolo:

profit/loss 1 profit/loss 2

Ahora, los diagramas circulares: en ellos es posible ver la contirbución que ha hecho cada símbolo a los beneficios y pérdidas del comercio:

profit percent  loss percent

En los demás diagramas se puede ver la rentabilidad de cada símbolo, en función de la hora de entrada en la posición: la distribución por horas de entrada, por días y por meses.

 

Esta es la optimización "silenciosa" y sin aspavientos

La tarea de la construcción de las distribuciones de los trades ha finalizado. En este caso, la historia comercial se ha consolidado en las posiciones, y todos los gráficos analíticos se construyen ya exclusivamente para las posiciones. Los beneficios o las pérdidas de la posición se analizan según la hora de entrada en las tres variantes: por horas, días y meses.

Con la ayuda de las fuentes propuestas en el artículo, usted podrá comprobar las horas y días favorables y desfavorables para el comercio con cualquier estrategia. Y quién sabe, puede que en lugar de una estrategia inicial usted obtenga un par de ellas nuevas, por ejemplo, una de tendencia y una de control. O quizá usted pueda comprobar qué sesión comercial (asiática, europea o americana) debería evitarse, en lugar de comerciar en ella. Pruebe usted mismo este sencillo método de optimización "silenciosa", que no necesita pasadas adicionales en el simulador de estrategias.

 

Cambios

Hablemos brevemente sobre los cambios que se han introducido en el código después de la primera publicación del artículo.

"DistributionOfProfits.mqh" v.1.027: Introducida la protección de la operación del programa en el proceso de optimización. En el constructor se contiene el nombre del archivo "Non File", y en las dos funciones públicas se realiza la comprobación de la 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: Ahora podemos analizar las posiciones en dinero, en puntos, en dinero y en puntos.

La elección del tipo de análisis se realiza así:

//+------------------------------------------------------------------+
//|                                                  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();
  }
//+------------------------------------------------------------------+


 

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

Interfaces gráficas X: Control "Gráfico estándar" (build 4) Interfaces gráficas X: Control "Gráfico estándar" (build 4)

En este artículo vamos a analizar el control de la interfaz gráfica como «Gráfico estándar». Nos permitirá crear los arrays de objetos-gráficos con posibilidad del desplazamiento horizontal sincronizado del gráfico. Aparte de eso, continuaremos optimizando el código de la librería para reducir el consumo de los recursos de CPU.

Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 3) Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 3)

En este artículo se muestra la siguiente versión de la librería Easy And Fast (versión 3). Hemos corregido algunos fallos, así como hemos añadido nuevas posibilidades. Para más información, lea a continuación.

Principios de programación en MQL5: Archivos Principios de programación en MQL5: Archivos

Artículo y estudio práctico sobre el trabajo con archivos en MQL5. Lea el artículo, ejecute las sencillas tareas, y al final usted conseguirá no solo conocimientos teóricos, sino también habilidades prácticas para trabajar con archivos en MQL5.

Asesor experto multiplataforma: Órdenes Asesor experto multiplataforma: Órdenes

MetaTrader 4 y MetaTrader 5 usan reglas diferentes en el procesamiento de solicitudes comerciales. En este artículo se discutirá la posibilidad de usar un objeto de clase para representar las operaciones procesadas por el servidor, para que en lo sucesivo el asesor pueda trabajar con ellas independientemente de la versión de la plataforma comercial y del modo ejecutado.