Representación personalizada de la historia comercial y creación de gráficos para los informes

28 septiembre 2018, 09:02
Andrey Azatskiy
0
3 092

Introducción

No es un secreto que al comerciar en los mercados financieros es importante monitorear el rendimiento y saber analizar la historia de nuestro propio trading. Con la ayuda de los datos pasados, podemos no solo monitorear la dinámica de las transacciones, sino también valorar la efectividad general de la estrategia comercial. Esto resulta útil a todos los tráders: tanto a los que prefieren el comercio manual, como a los tráders algorítmicos. En el presente artículo vamos a crear herramientas que implementan esta función. 

En la base de cualquier trading encontramos un algoritmo comercial que forma la curva de Profit/Loss. Este algoritmo se puede comparar con un cierto activo sintético cuyo coste se forma con respecto al activo básico (instrumento comercial). Por ejemplo, para las opciones hay una fórmula (modelo BSM), según la cual en cualquier momento se calcula el coste de este activo sintético partiendo del precio del básico. Pero, para el algoritmo comercial, esta fórmula no existe. Propiamente, el inicio del algoritmo se puede comparar con la posición larga de un instrumento sintético cuya curva de PL se forma según una lógica programada. El beneficio formado por este «activo» puede ser inestable en diferentes periodos de tiempo. Incluso si es dicho beneficio el que debe ser valorado con un modelo econométrico unificado, este modelo no se puede unificar. Surge la pregunta: ¿cómo monitorear este activo y los diferentes estadios de nuestro comercio? Se nos ofrece como solución conveniente monitorear la retrospectiva del comercio según este algoritmo y detectar las desviación respecto a los resultados esperados.

No vamos a dar consejos sobre el análisis de algoritmos, sino solo un conjunto de métodos con cuya ayuda se puede representar con bastante detalle una imagen general de la historia de transacciones. Basándonos en los datos obtenidos, podremos construir modelos econométricos complejos, calcular las características de la probabilidad y sacar otras conclusiones.

Este artículo se dividirá en 2 partes. En el primer apartado (técnico), hablaremos sobre los métodos de formación de informes sobre comercio a partir de la masa de información que se conserva en su terminal. Este apartado se relaciona con los datos para el análisis. En el segundo apartado, formaremos los índices básicos según los cuales valoraremos la retrospectiva del comercio a partir de los datos elegidos. La muestra de datos se podrá variar: según todos los activos o uno elegido, con toda la historia disponible o con un intervalo de tiempo concreto. Los resultados del análisis se muestrarán en un archivo aparte y se visualizarán brevemente en el terminal.

Hemos tomado los ejemplos de los datos para el análisis de nuestra historia comercial real. Los ejemplos de implementación del código se han realizado en el periodo de prueba, que se ha "acumulado" especialmente para este objetivo en la cuenta demo.


Capítulo 1. Preparación de datos para el análisis de la estadística comercial

Cualquier análisis comercial comienza con la preparación de los datos de origen. En nuestro caso, se trata de la historia comercial en el periodo temporal establecido. En la memoria del terminal MetaTrader 5 se guardan informes detallados. Pero a veces hay demasiada información, y un nivel de detalle demasiado elevado puede molestar en lugar ayudar. En este apartado intentaremos crear un "extracto" de la historia comercial que sea breve, informativo y cómodo de leer, y que además podamos procesar y analizar.

Historia comercial en MetaTrader 5 y métodos para trabajar con ella

Normalmente, la descarga de la historia comercial viene acompañada de una gran cantidad de registros de entrada. Para empezar, cada posición se divide en transacciones de entrada/salida, además, tanto unas como otras pueden pasar varias etapas. En segundo lugar, en ocasiones sucede que las posiciones son re-compradas o escaladas. En tercer lugar, en la historia hay líneas especiales para las «transacciones supuestas». Estas pueden ser:

  • posiciones abiertas/cerradas durante el ingreso/retirada del margen de variación (FORTS);
  • todo tipo de posibles correcciones de las posiciones existentes;
  • operaciones de depósito y retirada de fondos de la cuenta comercial.

La incomodidad de la lectura de los informes empeora además porque la historia se clasifica según la hora de apertura de las transacciones. Debido a que la vida útil de las transacciones se diferencia, esto puede deformar la secuencia de presentación de la información en cuanto al momento en que se fijó el resultado de la misma. Por ejemplo, la primera transacción se abre el 1 de junio y se cierra el día 5. La segunda se abre el 2 de junio y se cierra el mismo día. Obtendremos el benficio/pérdidas de ella antes que el de la primera, pero en el recuadro se encontrará posteriormente, puesto que se abrió más tarde.

Por consiguiente, a la hora de analizar el comercio, esta forma de registro de la historia de transacciones no nos convendrá. Sin embargo, sí que refleja por completo la historia del funcionamiento de la cuenta analizada. Para trabajar con la matriz de datos descrita, en Mql5 existe un paquete de instrumentos muy útil, que ya hemos utilizado para convertir la historia de transacciones en un tipo más legible. Lo primero que vamos a necesitar es organizar toda la información como historia comercial, clasificada por activos y transacciones. Para ello, crearemos las siguientes estructuras:

//+------------------------------------------------------------------+
//| Estructura de datos de una transacción seleccionada              |
//+------------------------------------------------------------------+
struct DealData
  {
   long              ticket;            // Ticket de la transacción
   long              order;             // Número de la orden que abre la transacción
   datetime          DT;                // Fecha de apertura de la posición
   long              DT_msc;            // Fecha de apertura de la posición en milisegundos 
   ENUM_DEAL_TYPE    type;              // Tipo de la posición abierta
   ENUM_DEAL_ENTRY   entry;             // Tipo de entrada en la posición
   long              magic;             // Número único de la posición
   ENUM_DEAL_REASON  reason;            // Desde dónde se ha colocado la orden
   long              ID;                // ID de la posición
   double            volume;            // Volumen de la posición (lotes)
   double            price;             // Precio de entrada en la posición
   double            comission;         // Comisión pagada
   double            swap;              // Swap
   double            profit;            // Beneficio/pérdidas
   string            symbol;            // Símbolo
   string            comment;           // Comentario indicado en el momento de apertura
   string            ID_external;       // ID externa 
  };
//+------------------------------------------------------------------+
//| Estructura que contiene todas las transacciones de una posición  |
//| determinada seleccionada según la ID                             |
//+------------------------------------------------------------------+
struct DealKeeper
  {
   DealData          deals[];           /* Lista completa de transacciones para esta posición (o varias, si la transacción ha virado)*/
   string            symbol;            // Símbolo
   long              ID;                // ID de la posición (posiciones)
   datetime          DT_min;            // fecha de apertura 
   datetime          DT_max;            // fecha de cierre 
  };

Podemos ver por el código que la estructura DealData contiene una descripción excesiva de los parámetros de la transacción.

Nuestra estructura principal será DealKeeper. En ella se encontrará la descripción de la posición y todas las transacciones que entran en la misma. El principal filtro de la posición en el terminal es su ID, un índice que permanece inmutable para todas las transacciones de esta posición. Por eso, si tiene lugar el viraje de la posición, esta conservará la ID inicial, pero en este caso, además, cambiará a la opuesta, por eso, en la estructura DealKeeper se guardarán dos posiciones. El rellenado de DealKeeper en nuestro código es filtrado usando como base la ID de la posición.

Vamos a analizar con detalle el método de rellanado de la estructura. La responsable de ello es la clase CDealHistoryGetter, concretamente, su función getHistory:

//+-----------------------------------------------------------------------+
//| Clase que extrae la historia de transacciones del terminal y le       |
//| confiere un aspecto cómodo para el análisis                           |
//+-----------------------------------------------------------------------+
class CDealHistoryGetter
  {
public:
   bool              getHistory(DealKeeper &deals[],datetime from,datetime till);                // retorna todos los datos de las transacciones históricas   
   bool              getIDArr(ID_struct &ID_arr[],datetime from,datetime till);                  // retorna la matriz única de la ID de las transacciones
   bool              getDealsDetales(DealDetales &ans[],datetime from,datetime till);            // retorna la matriz de transacciones, donde cada línea es una transacción concreta
private:

   void              addArray(ID_struct &Arr[],const ID_struct &value);                          // añadir a una matriz dinámica
   void              addArray(DealKeeper &Arr[],const DealKeeper &value);                        // añadir a una matriz dinámica
   void              addArray(DealData &Arr[],const DealData &value);                            // añadir a una matriz dinámica
   void              addArr(DealDetales &Arr[],DealDetales &value);                              // añadir a una matriz dinámica
   void              addArr(double &Arr[],double value);                                         // añadir a una matriz dinámica

/*
    Si hay salidas InOut, en inputParam habrá más de una posición. 
    Si no hay salidas InOut, en inputParam habrá solo una transacción ! 
*/
   void              getDeals_forSelectedKeeper(DealKeeper &inputParam,DealDetales &ans[]);      // se forma una entrada única de una posición elegida para todas las posiciones, que es precisamente inputParam
   double            MA_price(double &prices[],double &lots[]);                                  // se calcula el precio medio ponderado de apertura
   bool              isBorderPoint(DealData &data,BorderPointType &type);                        // obtenemos la información sobre si es un punto extremo, y qué tipo de punto es precisamente
   ENUM_DAY_OF_WEEK  getDay(datetime DT);                                                        // obtenemos el día de la fecha
   double            calcContracts(double &Arr[],GetContractType type);                          // obtenemos la información sobre el volumen de la última posición
  };

Vamos a ver su implementación:

//+------------------------------------------------------------------+
//| retorna todos los datos de las transacciones históricas          |
//+------------------------------------------------------------------+
bool CDealHistoryGetter::getHistory(DealKeeper &deals[],datetime from,datetime till)
  {
   ArrayFree(deals);                                     // limpiamos la matriz de resultados
   ID_struct ID_arr[];
   if(getIDArr(ID_arr,from,till))                        // obtenemos las ID únicas
     {
      int total=ArraySize(ID_arr);
      for(int i=0;i<total;i++)                           // iteramos por las ID
        {
         DealKeeper keeper;                              // acumulador de transacciones por posición
         keeper.ID=ID_arr[i].ID;
         keeper.symbol=ID_arr[i].Symb;
         keeper.DT_max = LONG_MIN;
         keeper.DT_min = LONG_MAX;
         if(HistorySelectByPosition(ID_arr[i].ID))       // elegimos todas las transacciones con la ID establecida
           {

Primero debemos obtener las ID únicas de las posiciones. Para ello, en el ciclo completo de la historia se analizan las ID de las transacciones y se forma una estructura con dos campos: Símbolo y ID de la posición. Vamos a necesitar los datos obtenidos para que la función getHistory funcione. 

En el lenguaje MQL5 hay una función muy útil, HistorySelectByPosition, que elige toda la historia de transacciones con las ID transmitidas. Con su ayuda, descartaremos directamente de la lista las operaciones que no necesitamos (procesamiento de la cuenta, ingreso y retirada de fondos, etc.). Como resultado, en la memoria se introducirá la historia formada de todos los cambios sucedidos con la posición mientras esta se encontraba abierta. Los datos son clasificados por fecha y hora. Solo nos queda iterarlos por orden con la ayuda de la función HistoryDealsTotal, que retorna el número total de todas las transacciones con la ID establecida que hayan sido introducidas previamente en la memoria.

int total_2=HistoryDealsTotal();
            for(int n=0;n<total_2;n++)                        // Ciclo por las transacciones elegidas
              {
               long ticket=(long)HistoryDealGetTicket(n);
               DealData data;
               data.ID=keeper.ID;
               data.symbol=keeper.symbol;
               data.ticket= ticket;

               data.DT=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
               keeper.DT_max=MathMax(keeper.DT_max,data.DT);
               keeper.DT_min=MathMin(keeper.DT_min,data.DT);
               data.order= HistoryDealGetInteger(ticket,DEAL_ORDER);
               data.type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE);
               data.DT_msc=HistoryDealGetInteger(ticket,DEAL_TIME_MSC);
               data.entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);
               data.magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
               data.reason= (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON);
               data.volume= HistoryDealGetDouble(ticket,DEAL_VOLUME);
               data.price = HistoryDealGetDouble(ticket,DEAL_PRICE);
               data.comission=HistoryDealGetDouble(ticket,DEAL_COMMISSION);
               data.swap=HistoryDealGetDouble(ticket,DEAL_SWAP);
               data.profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);
               data.comment=HistoryDealGetString(ticket,DEAL_COMMENT);
               data.ID_external=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);

               addArray(keeper.deals,data);                  // Añadiendo transacciones
              }

            if(ArraySize(keeper.deals)>0)
               addArray(deals,keeper);                       // Añadiendo posición
           }
        }
      return ArraySize(deals) > 0;
     }
   else
      return false;                                          // Si no hay ID únicas
  }

De esta forma, pasando por cada ID única, formamos una matriz de datos que describe la posición. La matriz de estructuras DealKeeper representa una historia comercial detallada de la cuenta actual en el periodo temporal solicitado.

Preparando los datos para el análisis

En el apartado anterior hemos obtenido los datos. Vamos a exportarlos a un archivo. Aquí tenemos el aspecto de la lista de transacciones de una de las posiciones analizadas:

Ticket Order DT DT msc Type Entry Magic Reason ID Volume Price Comission Swap Profit Symbol Comment ID external
10761601 69352663 23.11.2017 17:41 1,51146E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 506789 DEAL_REASON_EXPERT 69352663 1 58736 -0,5 0 0 Si-12.17 Open test position 23818051
10761602 69352663 23.11.2017 17:41 1,51146E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 506789 DEAL_REASON_EXPERT 69352663 1 58737 -0,5 0 0 Si-12.17 Open test position 23818052
10766760 0 24.11.2017 13:00 1,51153E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58682 0 0 -109 Si-12.17 [variation margin close]
10766761 0 24.11.2017 13:00 1,51153E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58682 0 0 0 Si-12.17 [variation margin open]
10769881 0 24.11.2017 15:48 1,51154E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58649 0 0 -66 Si-12.17 [variation margin close]
10769882 0 24.11.2017 15:48 1,51154E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58649 0 0 0 Si-12.17 [variation margin open]
10777315 0 27.11.2017 13:00 1,51179E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58420 0 0 -458 Si-12.17 [variation margin close]
10777316 0 27.11.2017 13:00 1,51179E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58420 0 0 0 Si-12.17 [variation margin open]
10780552 0 27.11.2017 15:48 1,5118E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58417 0 0 -6 Si-12.17 [variation margin close]
10780553 0 27.11.2017 15:48 1,5118E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58417 0 0 0 Si-12.17 [variation margin open]
10790453 0 28.11.2017 13:00 1,51187E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58589 0 0 344 Si-12.17 [variation margin close]
10790454 0 28.11.2017 13:00 1,51187E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58589 0 0 0 Si-12.17 [variation margin open]
10793477 0 28.11.2017 15:48 1,51188E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58525 0 0 -128 Si-12.17 [variation margin close]
10793478 0 28.11.2017 15:48 1,51188E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58525 0 0 0 Si-12.17 [variation margin open]
10801186 0 29.11.2017 13:00 1,51196E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58515 0 0 -20 Si-12.17 [variation margin close]
10801187 0 29.11.2017 13:00 1,51196E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58515 0 0 0 Si-12.17 [variation margin open]
10804587 0 29.11.2017 15:48 1,51197E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58531 0 0 32 Si-12.17 [variation margin close]
10804588 0 29.11.2017 15:48 1,51197E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58531 0 0 0 Si-12.17 [variation margin open]
10813418 0 30.11.2017 13:00 1,51205E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58843 0 0 624 Si-12.17 [variation margin close]
10813419 0 30.11.2017 13:00 1,51205E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58843 0 0 0 Si-12.17 [variation margin open]
10816400 0 30.11.2017 15:48 1,51206E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58609 0 0 -468 Si-12.17 [variation margin close]
10816401 0 30.11.2017 15:48 1,51206E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58609 0 0 0 Si-12.17 [variation margin open]
10824628 0 01.12.2017 13:00 1,51213E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58864 0 0 510 Si-12.17 [variation margin close]
10824629 0 01.12.2017 13:00 1,51213E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58864 0 0 0 Si-12.17 [variation margin open]
10828227 0 01.12.2017 15:48 1,51214E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58822 0 0 -84 Si-12.17 [variation margin close]
10828228 0 01.12.2017 15:48 1,51214E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58822 0 0 0 Si-12.17 [variation margin open]
10838074 0 04.12.2017 13:00 1,51239E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59093 0 0 542 Si-12.17 [variation margin close]
10838075 0 04.12.2017 13:00 1,51239E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59093 0 0 0 Si-12.17 [variation margin open]
10840722 0 04.12.2017 15:48 1,5124E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59036 0 0 -114 Si-12.17 [variation margin close]
10840723 0 04.12.2017 15:48 1,5124E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59036 0 0 0 Si-12.17 [variation margin open]
10848185 0 05.12.2017 13:00 1,51248E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58793 0 0 -486 Si-12.17 [variation margin close]
10848186 0 05.12.2017 13:00 1,51248E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58793 0 0 0 Si-12.17 [variation margin open]
10850473 0 05.12.2017 15:48 1,51249E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58881 0 0 176 Si-12.17 [variation margin close]
10850474 0 05.12.2017 15:48 1,51249E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58881 0 0 0 Si-12.17 [variation margin open]
10857862 0 06.12.2017 13:00 1,51257E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59181 0 0 600 Si-12.17 [variation margin close]
10857863 0 06.12.2017 13:00 1,51257E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59181 0 0 0 Si-12.17 [variation margin open]
10860776 0 06.12.2017 15:48 1,51258E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59246 0 0 130 Si-12.17 [variation margin close]
10860777 0 06.12.2017 15:48 1,51258E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59246 0 0 0 Si-12.17 [variation margin open]
10869047 0 07.12.2017 13:00 1,51265E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59325 0 0 158 Si-12.17 [variation margin close]
10869048 0 07.12.2017 13:00 1,51265E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59325 0 0 0 Si-12.17 [variation margin open]
10871856 0 07.12.2017 15:48 1,51266E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59365 0 0 80 Si-12.17 [variation margin close]
10871857 0 07.12.2017 15:48 1,51266E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59365 0 0 0 Si-12.17 [variation margin open]
10879894 0 08.12.2017 13:01 1,51274E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59460 0 0 190 Si-12.17 [variation margin close]
10879895 0 08.12.2017 13:01 1,51274E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59460 0 0 0 Si-12.17 [variation margin open]
10882283 0 08.12.2017 15:48 1,51275E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59421 0 0 -78 Si-12.17 [variation margin close]
10882284 0 08.12.2017 15:48 1,51275E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59421 0 0 0 Si-12.17 [variation margin open]
10888014 0 11.12.2017 13:00 1,513E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59318 0 0 -206 Si-12.17 [variation margin close]
10888015 0 11.12.2017 13:00 1,513E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59318 0 0 0 Si-12.17 [variation margin open]
10890195 0 11.12.2017 15:48 1,51301E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59280 0 0 -76 Si-12.17 [variation margin close]
10890196 0 11.12.2017 15:48 1,51301E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59280 0 0 0 Si-12.17 [variation margin open]
10895808 0 12.12.2017 13:00 1,51308E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58920 0 0 -720 Si-12.17 [variation margin close]
10895809 0 12.12.2017 13:00 1,51308E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58920 0 0 0 Si-12.17 [variation margin open]
10897839 0 12.12.2017 15:48 1,51309E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58909 0 0 -22 Si-12.17 [variation margin close]
10897840 0 12.12.2017 15:48 1,51309E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58909 0 0 0 Si-12.17 [variation margin open]
10903172 0 13.12.2017 13:00 1,51317E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59213 0 0 608 Si-12.17 [variation margin close]
10903173 0 13.12.2017 13:00 1,51317E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59213 0 0 0 Si-12.17 [variation margin open]
10905906 0 13.12.2017 15:48 1,51318E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59072 0 0 -282 Si-12.17 [variation margin close]
10905907 0 13.12.2017 15:48 1,51318E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59072 0 0 0 Si-12.17 [variation margin open]
10911277 0 14.12.2017 13:00 1,51326E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58674 0 0 -796 Si-12.17 [variation margin close]
10911278 0 14.12.2017 13:00 1,51326E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58674 0 0 0 Si-12.17 [variation margin open]
10912285 71645351 14.12.2017 14:48 1,51326E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 506789 DEAL_REASON_EXPERT 69352663 1 58661 -0,5 0 -13 Si-12.17 PartialClose position_2 25588426
10913632 0 14.12.2017 15:48 1,51327E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58783 0 0 109 Si-12.17 [variation margin close]
10913633 0 14.12.2017 15:48 1,51327E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58783 0 0 0 Si-12.17 [variation margin open]
10919412 0 15.12.2017 13:00 1,51334E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58912 0 0 129 Si-12.17 [variation margin close]
10919413 0 15.12.2017 13:00 1,51334E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58912 0 0 0 Si-12.17 [variation margin open]
10921766 0 15.12.2017 15:48 1,51335E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58946 0 0 34 Si-12.17 [variation margin close]
10921767 0 15.12.2017 15:48 1,51335E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58946 0 0 0 Si-12.17 [variation margin open]
10927382 0 18.12.2017 13:00 1,5136E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58630 0 0 -316 Si-12.17 [variation margin close]
10927383 0 18.12.2017 13:00 1,5136E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58630 0 0 0 Si-12.17 [variation margin open]
10929913 0 18.12.2017 15:48 1,51361E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58664 0 0 34 Si-12.17 [variation margin close]
10929914 0 18.12.2017 15:48 1,51361E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58664 0 0 0 Si-12.17 [variation margin open]
10934874 0 19.12.2017 13:00 1,51369E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58635 0 0 -29 Si-12.17 [variation margin close]
10934875 0 19.12.2017 13:00 1,51369E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58635 0 0 0 Si-12.17 [variation margin open]
10936988 0 19.12.2017 15:48 1,5137E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58629 0 0 -6 Si-12.17 [variation margin close]
10936989 0 19.12.2017 15:48 1,5137E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58629 0 0 0 Si-12.17 [variation margin open]
10941561 0 20.12.2017 13:00 1,51377E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58657 0 0 28 Si-12.17 [variation margin close]
10941562 0 20.12.2017 13:00 1,51377E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58657 0 0 0 Si-12.17 [variation margin open]
10943405 0 20.12.2017 15:48 1,51378E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58684 0 0 27 Si-12.17 [variation margin close]
10943406 0 20.12.2017 15:48 1,51378E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58684 0 0 0 Si-12.17 [variation margin open]
10948277 0 21.12.2017 13:00 1,51386E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58560 0 0 -124 Si-12.17 [variation margin close]
10948278 0 21.12.2017 13:00 1,51386E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58560 0 0 0 Si-12.17 [variation margin open]
10949780 0 21.12.2017 15:45 1,51387E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_CLIENT 69352663 1 58560 0 0 0 Si-12.17 [instrument expiration] 26163141

Este ejemplo se ha tomado de la historia de una cuenta demo en el mercado FORTS de futuros con la pareja USDRUR. En el recuadro se puede ver que la entrada en la posición se ha dividido en dos órdenes con un lote igual. De la misma forma, la salida también está dividida en dos partes. Las IDs de las órdenes que han abierto las transacciones para cada entrada y salida (con la excepción del margen de variación) han sido diferentes a cero. Pero la última transacción que ha cerrado la posición ha llegado con una ID de la orden igual a cero. Esto ha sucedido porque la posición se ha cerrado por la expiración del activo, y no manualmente.

Esta historia comercial ya es más legible y representa las transacciones (incluidas las "supuestas") realizadas dentro de una posición. Pero sigue siendo incómoda para el análisis final de la posición, y necesita mejoras. Necesitamos un recuadro en el que cada línea refleje plenamente el resultado financiero de la posición, además de proporcionar información adicional.

Vamos a crear esta nueva estructura:

//+------------------------------------------------------------------+
//| Estructura que guarda el resultado financiero y                  |
//| la información básica de la posición elegida.                    |
//+------------------------------------------------------------------+
struct DealDetales
  {
   string            symbol;                // símbolo 
   datetime          DT_open;               // fecha de apertura
   ENUM_DAY_OF_WEEK  day_open;              // día de la semana de la apertura
   datetime          DT_close;              // fecha de cierre
   ENUM_DAY_OF_WEEK  day_close;             // día de la semana de cierre
   double            volume;                // volumen (lotes)
   bool              isLong;                // signo long/short
   double            price_in;              // precio de entrada a la posición
   double            price_out;             // precio de salida de la posición
   double            pl_oneLot;             // beneficio/pérdidas si hemos comerciado con un lote
   double            pl_forDeal;            // beneficio/pérdidas reales obtenidos teniendo en cuenta la comisión
   string            open_comment;          // comentario en el momento de apertura
   string            close_comment;         // comentario en el momento de cierre
  };

Esta estructura se forma con el método getDealsDetales. En este se usan 3 métodos "private" clave: 

  • isBorderPoint, que determina si la transacción actual es «fronteriza» (si ha sido colocada por el tráder y si abre/cierra una posición);
  • MA_price, que calcula el precio real de apertura/cierre de la posición;
  • calcContracts, que calcula el número de contratos que estaban en el mercado en el momento de existencia de la posición.

Vamos a analizar con más detalle el método MA_price. Por ejemplo, si la posición se ha abierto en varios contratos a diferentes precios, resultará incorrecto fijar el precio de la primera transacción como precio de apertura de la posición. Eso provocará errores en cálculos posteriores. Usaremos el valor medio ponderado. Promediaremos los precios de apertura/cierre de las posiciones, y los ponderaremos según el volumen de las transacciones abiertas. La implementación de esta lógica tiene el aspecto siguiente:

//+------------------------------------------------------------------+
//| Calculamos el precio medio ponderado de apertura                 |
//+------------------------------------------------------------------+
double CDealHistoryGetter::MA_price(double &prices[],double &lots[])
  {
   double summ=0;                           // Suma de los precios ponderados
   double delemetr=0;                       // Suma de los pesos
   int total=ArraySize(prices);
   for(int i=0;i<total;i++)                 // Ciclo de suma
     {
      summ+=(prices[i]*lots[i]);
      delemetr+=lots[i];
     }
   return summ/delemetr;                    // Cálculo de la media
  }
Con respecto a los lotes comerciados: este caso es más enrevesado, pero analizarlo no será complicado. Vamos a analizar un ejemplo en el que gestionamos dinámicamente las posiciones, añadiendo o acortando paulatinamente el número de lotes:

In Out
Open (t1) 1 0
t2 2 0
t3 5 3
t4 1 0
t5 0 1
t6 1 0
t7 0 1
t8 1 0
t9 0 1
Close (t10) 0 5
Total  11  11 

Mientras existe la posición, hemos estado o bien comprando o bien vendiendo el activo comerciado (a veces por turnos, en ocasiones casi simultáneamente). Como resultado, hemos acumulado 11 lotes de compra y venta. Pero esto no significa que el volumen máximo de la posición alcanzado sea igual a 11 lotes. Vamos a anotar todas las entradas y salidas en la serie: el incremento de la posición y las entradas con el signo +, y la disminución y las salidas con el signo -. Las sumamos como se suma la curva de PL.


Volumen de la transacción Volumen de la posición
deal 1 1 1
deal  2 2 3
deal 3 5 8
deal 4 -3 5
deal 5 1 6
deal 6 -1 5
deal 7 1 6
deal 8 -1 5
deal 9 1 6
deal 10 -1 5
deal 11  -5  0

En el recuadro podemos ver que la suma máxima de lotes alcanzada ha sido igual a 8. Esta es precisamente la magnitud buscada.

Veamos cómo se ha implementado en el código la función que muestra el número de lotes:

//+------------------------------------------------------------------+
//| Calculamos qué volumen de la posición había en realidad,         |
//| y obtenemos información sobre el volumen de la última posición   |
//+------------------------------------------------------------------+
double CDealHistoryGetter::calcContracts(double &Arr[],GetContractType type)
  {
   int total;

   if((total=ArraySize(Arr))>1)                        // si el tamaño de la matriz es superior a uno
     {
      double lotArr[];
      addArr(lotArr,Arr[0]);                           // añadimos el primer lote a la matriz 
      for(int i=1;i<total;i++)                         // ciclo desde el segundo elemento de la matriz
         addArr(lotArr,(lotArr[i-1]+Arr[i]));          // añadimos la suma de los lotes pasado y actual a la matriz (lotArr[i-1]+Arr[i]))

      if(type==GET_REAL_CONTRACT)
         return lotArr[ArrayMaximum(lotArr)];          // retornamos el lote máximo comerciado realmente
      else
         return lotArr[ArraySize(lotArr)-1];           // retornamos el último lote comerciado
     }
   else
      return Arr[0];
  }

Aparte del cálculo simple de los lotes, la función también muestra el último lote activo (en nuestro ejemplo es igual a 5).

Veamos la función isBorderPoint, que ha sido creada para filtrar las transacciones innecesarias. Partiendo de la estructura de nuestros datos, podemos determinar la importancia de la transacción usando 4 indicadores:

  • ID de la orden;
  • ENUM_DEAL_ENTRY — entrada, salida, viraje;
  • ENUM_DEAL_TYPE — compra/venta;
  • ENUM_DEAL_REASON — método de colocación de la orden.

Creamos la enumeración:

//+-------------------------------------------------------------------------+
//| Enumeración auxiliar que indica el tipo de registro de entrada concreto |
//| El propio registro se presenta en la estructura DealData                |
//+-------------------------------------------------------------------------+
enum BorderPointType
  {
   UsualInput,          // tipo habitual de entrada (DEAL_ENTRY_IN) - Comienzo de la transacción
   UsualOutput,         // tipo habitual de salida (DEAL_ENTRY_OUT) - finalización de la transacción
   OtherPoint,          // operación de balance, corrección de la posición, retirada, margen de variación — todo lo que ignoramos
   InOut,               // viraje de posición (DEAL_ENTRY_INOUT)
   OutBy                // salida de la posición por orden opuesta (DEAL_ENTRY_OUT_BY)
  };
De las cinco variantes presentadas, nos interesan solo cuatro. Todas las transacciones innecesarias, entran en la variante OtherPoint. Las combinaciones de parámetros que satisfacen cada una de las variantes de enumeración se presentan en el recuadro. Podrá mirar el código de la función en los archivos adjuntos al artículo. 
Variante de enumeración ID de la orden ENUM_DEAL_ENTRY ENUM_DEAL_TYPE ENUM_DEAL_REASON
UsualInput  >0 DEAL_ENTRY_IN DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



UsualOut >=0(=0 si ha sido por expiración) DEAL_ENTRY_OUT DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_REASON_WEB



DEAL_TYPE_SELL DEAL_REASON_MOBILE


DEAL_REASON_SL



DEAL_REASON_TP



DEAL_REASON_SO



OtherPoint  0 DEAL_ENTRY_IN DEAL_TYPE_BUY DEAL_REASON_ROLLOVER
DEAL_TYPE_SELL



DEAL_TYPE_BALANCE



DEAL_TYPE_CREDIT



DEAL_TYPE_CHARGE



DEAL_TYPE_CORRECTION



DEAL_TYPE_BONUS DEAL_REASON_VMARGIN


DEAL_TYPE_COMMISSION



DEAL_TYPE_COMMISSION_DAILY



DEAL_ENTRY_OUT DEAL_TYPE_COMMISSION_MONTHLY


DEAL_TYPE_COMMISSION_AGENT_DAILY



DEAL_TYPE_COMMISSION_AGENT_MONTHLY



DEAL_TYPE_INTEREST DEAL_REASON_SPLIT


DEAL_TYPE_BUY_CANCELED



DEAL_TYPE_SELL_CANCELED



DEAL_DIVIDEND



DEAL_DIVIDEND_FRANKED



DEAL_TAX



InOut  >0 DEAL_ENTRY_INOUT  DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



OutBy  >0 DEAL_ENTRY_OUT_BY  DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



Para formar la historia necesaria, usaremos el método getDeals_forSelectedKeeper. Analizaremos su lógica general, y a continuación estudiaremos al detalle las acciones para cada una de las variantes de las enumeraciones expuestas más arriba (comenzando por la línea 303).

//+------------------------------------------------------------------------+
//| Se forma un registro de entrada de cada posición elegida en inputParam |
//+------------------------------------------------------------------------+

void CDealHistoryGetter::getDeals_forSelectedKeeper(DealKeeper &inputParam,DealDetales &ans[])
  {
   ArrayFree(ans);
   int total=ArraySize(inputParam.deals);
   DealDetales detales;                                          // variable en la que ubicamos los resultados del siguiente ciclo
   detales.symbol=inputParam.symbol;
   detales.volume= 0;
   detales.pl_forDeal=0;
   detales.pl_oneLot=0;
   detales.close_comment= "";
   detales.open_comment = "";
   detales.DT_open=0;

// Bandera que indica si hay que añadir la posición al conjunto
   bool isAdd=false;
   bool firstPL_setted=false;
// Matrices de precios de entrada, precios de salida, lotes de entrada, lotes de salida, contratos
   double price_In[],price_Out[],lot_In[],lot_Out[],contracts[]; // línea 404

   for(int i=0;i<total;i++)                                      // ciclo por todas las transacciones (todas las transacciones tienen una ID única, pero puede haber más de una posición, si el tipo de la transacción es InOut)
     {
      BorderPointType type;                                      // tipo de transacción, línea 408
      double pl_total=0;

      if(isBorderPoint(inputParam.deals[i],type))                // averiguar si esta transacción es fronteriza, y su tipo, línea 301
        {
          // línea 413
        } // línea 414
      else
        {
/*
         Si la transacción no es fronteriza, simplementa fijamos el resultado financiero.
         Aquí solo puede encontrarse el margen de variación y diferentes correcciones.
         Los depósitos y la retirada de la cuenta se filtran al obtener los datos originales 
*/
         detales.pl_forDeal+=(inputParam.deals[i].profit+inputParam.deals[i].comission);
         detales.pl_oneLot+=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT);
        }
     }

// Filtramos las posiciones activas y no las guardamos
   if(isAdd && PositionSelect(inputParam.symbol))                 // línea 541
     {
      if(PositionGetInteger(POSITION_IDENTIFIER)==inputParam.ID)
         isAdd=false; 
     }                                                            // línea 546

// Guardamos las posiciones fijadas y ya inactivas
   if(isAdd)
     {
      detales.price_in=MA_price(price_In,lot_In);
      detales.price_out=MA_price(price_Out,lot_Out);

      detales.volume=calcContracts(contracts,GET_REAL_CONTRACT);
      addArr(ans,detales);
     }
  }

En la línea 404 de la función se declaran las matrices. En lo sucesivo, se usarán en la condición designada en las líneas 411-414. Dentro de esta condición se analizan solo los puntos extremos (las transacciones que conllevan la apertura/cierre o el incremento/cierre parcial de la posición estudiada).

Si la transacción no entra en la primera condición, la única acción necesaria será calcular su beneficio/pérdidas. La historia transmitida por nosotros contiene en realidad el beneficio/pérdidas que hemos obtenido, dividido en transacciones. Cada transacción muestra el cambio de PL comenzando por la transacción anterior hasta el momento de ejecución de la analizada. El beneficio general de la posición se compara con la suma de PL de todas estas transacciones. Si simplemente calculamos el beneficio/pérdidas como la diferencia entre los precios de apertura y cierre, pasaremos por alto una serie de factores: el deslizamiento, el cambio de volumen dentro de la posición, las comisiones y las tasas.

En las líneas de la 541 a la 546 del código tiene lugar el filtrado de las posiciones abiertas, que no guardamos como resultado. Al final de la función se calculan los precios de apertura y cierre, así como el volumen de posiciones máximo existente en el mercado.

La variable type sirve para filtrar los tipos de puntos extremos. Si en el momento actual se está abriendo o incrementando una posición, entraremos en la siguiente condición, que comienza a partir de la línea 413 (ver el texto completo del método en los archivos adjuntos).

if(type==UsualInput)                                                   // si es la entrada inicial o el incremento de la posición
  {
   if(detales.DT_open==0)                                              // asignamos la fecha de apertura de la posición
     {
      detales.DT_open=inputParam.deals[i].DT;
      detales.day_open=getDay(inputParam.deals[i].DT);
     }
   detales.isLong=inputParam.deals[i].type==DEAL_TYPE_BUY;             // determinamos la dirección de la posición
   addArr(price_In,inputParam.deals[i].price);                         // guardamos el precio de entrada 
   addArr(lot_In,inputParam.deals[i].volume);                          // guardamos el número de lotes

   pl_total=(inputParam.deals[i].profit+inputParam.deals[i].comission);
   detales.pl_forDeal+=pl_total;                                       // beneficio/pérdidas de la transacción incluyendo la comisión
   if(!firstPL_setted)
     {
      detales.pl_oneLot=pl_total/inputParam.deals[i].volume;           // Beneficio/pérdidas de la transacción incluyendo la comisión, si comerciamos con un lote
      firstPL_setted=true;
     }
   else
      detales.pl_oneLot=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT);                 

   if(StringCompare(inputParam.deals[i].comment,"")!=0)                // comentario de la transacción
      detales.open_comment+=(StringCompare(detales.open_comment,"")==0 ?
                             inputParam.deals[i].comment :
                             (" | "+inputParam.deals[i].comment));
   addArr(contracts,inputParam.deals[i].volume);                       // añadimos el volumen de la transacción con el signo "+"
  }

En él asignamos la fecha de apertura de posición y calculamos el beneficio (general y por un lote). El cierre de posición tiene lugar según otra condición:

if(type==UsualOut || type==OutBy)                         // cierre de posición
  {
/*
           No lo guardamos directamente, puesto que puede haber varias salidas (una tras otra, paulatinamente)
           Por eso, para evitar la pérdida de parte de los datos, solo tenemos que establecer la bandera activada 
*/
   if(!isAdd)isAdd=true;                                  // bandera para guardar la posición

   detales.DT_close=inputParam.deals[i].DT;               // fecha de cierre
   detales.day_close=getDay(inputParam.deals[i].DT);      // día de cierre
   addArr(price_Out,inputParam.deals[i].price);           // guardamos el precio de salida
   addArr(lot_Out,inputParam.deals[i].volume);            // guardamos el volumen de salida

   pl_total=(inputParam.deals[i].profit+inputParam.deals[i].comission);          // Beneficio/pérdida de la transacción incluyendo la comisión
   detales.pl_forDeal+=pl_total;

   if(i==total-1)
      detales.pl_oneLot+=pl_total/calcContracts(contracts,GET_LAST_CONTRACT);    // Beneficio/pérdidas de la transacción incluyendo la comisión, si comerciamos con un lote
   else
      detales.pl_oneLot+=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT); // Beneficio/pérdidas de la transacción incluyendo la comisión, si comerciamos con un lote

   if(StringCompare(inputParam.deals[i].comment,"")!=0)                          // Comentario de la transacción
      detales.close_comment+=(StringCompare(detales.close_comment,"")==0 ?
                              inputParam.deals[i].comment :
                              (" | "+inputParam.deals[i].comment));
   addArr(contracts,-inputParam.deals[i].volume);                                // Añadimos el volumen de la transacción con el signo "-"
  }

Esta condición asigna la fecha de salida de la posición, calcula el beneficio de la transacción. Puede haber varias entradas y salidas, eso significa que estas condiciones pueden darse varias veces.

La condición InOut es una combinación de la segunda y la primera condición. Esta se da solo una vez y termina con el viraje de la posición.

if(type==InOut)                                                                 // Viraje de posición
  {
/*
           Parte 1:
           Guarda la posición anterior
*/
   firstPL_setted=true;
   double closingContract=calcContracts(contracts,GET_LAST_CONTRACT);           // contrato cerrado
   double myLot=inputParam.deals[i].volume-closingContract;                     // contrato abierto

   addArr(contracts,-closingContract);                                          // añadimos el volumen de la transacción con el signo "-"
   detales.volume=calcContracts(contracts,GET_REAL_CONTRACT);                   // obtenemos el volumen de transacción máximo que existe realmente

   detales.DT_close=inputParam.deals[i].DT;                                     // fecha de cierre
   detales.day_close=getDay(inputParam.deals[i].DT);                            // día de cierre
   addArr(price_Out,inputParam.deals[i].price);                                 // precio de salida
   addArr(lot_Out,closingContract);                                             // volumen de salida

   pl_total=(inputParam.deals[i].profit*closingContract)/inputParam.deals[i].volume;        // calculamos el PL de la transacción de cierre
   double commission_total=(inputParam.deals[i].comission*closingContract)/inputParam.deals[i].volume;
   detales.pl_forDeal+=(pl_total+commission_total);
   detales.pl_oneLot+=pl_total/closingContract;                                 // beneficio/pérdidas de la transacción incluyendo la comisión, si comerciamos con un lote
   if(StringCompare(inputParam.deals[i].comment,"")!=0)                         // guardamos el comentario de cierre
      detales.open_comment+=(StringCompare(detales.open_comment,"")==0 ?
                             inputParam.deals[i].comment :
                             (" | "+inputParam.deals[i].comment));

   detales.price_in=MA_price(price_In,lot_In);                                  // obtenemos el precio (promediado) de entrada en la posición
   detales.price_out=MA_price(price_Out,lot_Out);                               // obtenemos el precio (promediado) de salida de la posición
   addArr(ans,detales);                                                         // añadimos la posición formada
   if(isAdd)isAdd=false;                                                        // reseteamos la bandera si estaba activada

                                                                                // Limpiando parte de los datos
   ArrayFree(price_In);
   ArrayFree(price_Out);
   ArrayFree(lot_In);
   ArrayFree(lot_Out);
   ArrayFree(contracts);
   detales.close_comment="";
   detales.open_comment="";
   detales.volume=0;

/*
           Parte 2:
           Guardamos la nueva posición, eliminando preliminarmente parte de los datos de la matriz "detales"
*/

   addArr(contracts,myLot);                                                     // Añadimos el lote de apertura de posición            

   pl_total=((inputParam.deals[i].profit+inputParam.deals[i].comission)*myLot)/inputParam.deals[i].volume; // Beneficio/pérdida de la transacción incluyendo la comisión
   detales.pl_forDeal=pl_total;
   detales.pl_oneLot=pl_total/myLot;                                            //Beneficio/pérdidas de la transacción incluyendo la comisión, si comerciamos con un lote
   addArr(lot_In,myLot);                                                        // Añadimos el lote de entrada

   detales.open_comment=inputParam.deals[i].comment;                            // Guardamos el comentario

   detales.DT_open=inputParam.deals[i].DT;                                      // Guardamos la fecha de apertura
   detales.day_open=getDay(inputParam.deals[i].DT);                             // Guardamos el día de apertura
   detales.isLong=inputParam.deals[i].type==DEAL_TYPE_BUY;                      // Determinamos la dirección de la transacción
   addArr(price_In,inputParam.deals[i].price);                                  // Guardamos el precio de entrada
  }

Como resultado de los cálculos descritos, obtendremos un recuadro donde cada línea refleja los parámetros básicos de la posición. Ahora podemos realizar todos los cálculos necesarios operando ya con posiciones, y no con las transacciones de las que constan.

Instrument Open DT Open day Close DT Close day Contracts Direction Price open Price close PL for 1 lot PL for position Open comment Close comment
RTS-12.17 17.11.2017 19:53 FRIDAY 17.11.2017 19:54 FRIDAY 2.00000000 Long 113200.00000000 113180.00000000 -25.78000000 -55.56000000
RTS-12.17 17.11.2017 19:54 FRIDAY 17.11.2017 19:54 FRIDAY 2.00000000 Short 113175.00000000 113205.00000000 -58.47000000 -79.33000000
RTS-12.17 17.11.2017 19:58 FRIDAY 17.11.2017 19:58 FRIDAY 1.00000000 Short 113240.00000000 113290.00000000 -63.44000000 -63.44000000
RTS-12.17 17.11.2017 19:58 FRIDAY 17.11.2017 19:58 FRIDAY 1.00000000 Long 113290.00000000 113250.00000000 -51.56000000 -51.56000000
Si-12.17 17.11.2017 20:00 FRIDAY 17.11.2017 20:00 FRIDAY 10.00000000 Long 59464.40000000 59452.80000000 -23.86000000 -126.00000000
Si-12.17 17.11.2017 20:00 FRIDAY 17.11.2017 20:00 FRIDAY 5.00000000 Short 59453.20000000 59454.80000000 -5.08666667 -13.00000000
Si-12.17 17.11.2017 20:02 FRIDAY 17.11.2017 20:02 FRIDAY 1.00000000 Short 59460.00000000 59468.00000000 -9.00000000 -9.00000000
Si-12.17 17.11.2017 20:02 FRIDAY 17.11.2017 20:03 FRIDAY 2.00000000 Long 59469.00000000 59460.00000000 -14.50000000 -20.00000000
Si-12.17 21.11.2017 20:50 TUESDAY 21.11.2017 21:06 TUESDAY 2.00000000 Long 59467.00000000 59455.00000000 -13.00000000 -26.00000000
Si-12.17 23.11.2017 17:41 THURSDAY 21.12.2017 15:45 THURSDAY 2.00000000 Long 58736.50000000 58610.50000000 -183.00000000 -253.50000000 Open test position | Open test position PartialClose position_2 | [instrument expiration]
RTS-12.17 23.11.2017 18:07 THURSDAY 14.12.2017 14:45 THURSDAY 1.00000000 Short 115680.00000000 114110.00000000 1822.39000000 1822.39000000 Open test position_2
RTS-3.18 30.01.2018 20:22 TUESDAY 30.01.2018 20:22 TUESDAY 2.00000000 Short 127675.00000000 127710.00000000 -61.01000000 -86.68000000
RTS-3.18 30.01.2018 20:24 TUESDAY 30.01.2018 20:24 TUESDAY 1.00000000 Long 127730.00000000 127710.00000000 -26.49000000 -26.49000000
RTS-3.18 30.01.2018 20:24 TUESDAY 30.01.2018 20:25 TUESDAY 1.00000000 Long 127730.00000000 127680.00000000 -60.21000000 -60.21000000
RTS-3.18 30.01.2018 20:25 TUESDAY 30.01.2018 20:25 TUESDAY 1.00000000 Long 127690.00000000 127660.00000000 -37.72000000 -37.72000000
RTS-3.18 30.01.2018 20:25 TUESDAY 30.01.2018 20:26 TUESDAY 1.00000000 Long 127670.00000000 127640.00000000 -37.73000000 -37.73000000
RTS-3.18 30.01.2018 20:29 TUESDAY 30.01.2018 20:30 TUESDAY 1.00000000 Long 127600.00000000 127540.00000000 -71.45000000 -71.45000000

Capítulo 2. Formando un informe comercial personalizado

Ahora vamos a crear una clase que genere el informe comercial. Primero, definiremos los requisitos del informe.

  1. El informe debe contener tanto gráficos estándar PL como incrementados, para valorar de forma más visual el rendimiento. La construcción de los gráficos se comienza desde cero (independientemente del capital inicial), esto aumentará la objetividad de la valoración.
  2. También se formará un gráfico de la estrategia Buy and hold, comparable con el gráfico PL (los calculará una misma función). Ambos gráficos se construirán para todos los activos que se encuentren en el informe.
  3. Los coeficientes y resultados principales del trading deberán representarse en forma de recuadro.
  4. Asimismo, deberemos construir gráficos adicionales: por ejemplo, Pl por días.

Al final, presentaremos los parámetros analizados en forma de gráficos variables y descargaremos todos los resultados en archivos csv. Los ejemplos mostrados aquí se han creado según la historia real en un intervalo temporal determinado. Esta historia se adjunta al final del artículo. El script para la visualización y la descarga de los datos contendrá funciones: la primera mostrará los ejemplos de la historia de transacciones adjunta, y la segunda mostrará los de su propia historia, descargada de su terminal.

En esta parte del artículo habrá muy poco código. En lugar de ello, nos concentraremos en el análisis de los datos obtenidos y la descripción de su sentido. Todas las partes de la clase que crea el informe se comentan con detalle, así que podrá comprenderlas con facilidad. Para orientarse con mayor comodidad en las funciones descritas, ofrecemos la estructura de la clase analizada.

class CReportGetter
  {
public:
                     CReportGetter(DealDetales &history[]);                      // solo se puede dar la historia en el formato preparado
                     CReportGetter(DealDetales &history[],double balance);       // se puede dar tanto la historia como el balance con respecto al cual se realizarán los cálculos
                    ~CReportGetter();

   bool              get_PLChart(PL_chartData &pl[],
                                 PL_chartData &pl_forOneLot[],
                                 PL_chartData &Indicative_pl[],
                                 string &Symb[]);                                // графики PL

   bool              get_BuyAndHold(PL_chartData &pl[],
                                    PL_chartData &pl_forOneLot[],
                                    PL_chartData &Indicative_pl[],
                                    string &Symb[]);                             // gráficos buy and hold

   bool              get_PLHistogram(PL_chartData &pl[],
                                     PL_chartData &pl_forOneLot[],
                                     string &Symb[]);                            // histograma de acumulación de beneficio y pérdidas

   bool              get_PL_forDays(PLForDaysData &ans,
                                    DailyPL_calcBy calcBy,
                                    DailyPL_calcType type,
                                    string &Symb[]);                             // PL por días de la semana

   bool              get_extremePoints(PL_And_Lose &total,
                                       PL_And_Lose &forDeal,
                                       string &Symb[]);                          // puntos extremos (valores máximos y mínimos de la transacción y con acumulación) 

   bool              get_AbsolutePL(PL_And_Lose &total,
                                    PL_And_Lose &average,
                                    string &Symb[]);                             // valores absolutos (PL acumulados y promediados)

   bool              get_PL_And_Lose_percents(PL_And_Lose &ans,string &Symb[]);  // diagrama de distribución de transacciones rentables y con pérdidas

   bool              get_totalResults(TotalResult_struct &res,string &Symb[]);   // recuadro de variables principales

   bool              get_PLDetales(PL_detales &ans,string &Symb[]);              // breve síntesis del gráfico de PL

   void              get_Symbols(string &SymbArr[]);                             // obtener la lista de los símbolos de la historia

private:
   DealDetales       history[];                                                  // guarda la historia de transacciones
   double            balance;                                                    // guarda el valor del balance

   void              addArray(DealDetales &Arr[],DealDetales &value);            // añadir a una matriz dinámica de datos
   void              addArray(PL_chartData &Arr[],PL_chartData &value);          // añadir a una matriz dinámica de datos
   void              addArray(string &Arr[],string value);                       // añadir a una matriz dinámica de datos
   void              addArray(datetime &Arr[],datetime value);                   // añadir a una matriz dinámica de datos

   void              sortHistory(DealDetales &arr[],bool byClose);               // Clasificación de la historia según la fecha de apertura o cierre

   void              cmpDay(int i,ENUM_DAY_OF_WEEK day,ENUM_DAY_OF_WEEK etaloneDay,PLDrowDown &ans);      // Rellenar la estructura de PLDrowDown

   bool              isSymb(string &Symb[],int i);                               // Comprobar si existe el i-ésimo símbolo de la historia en la matriz Symb

   bool              get_PLChart_byHistory(PL_chartData &pl[],
                                           PL_chartData &pl_forOneLot[],
                                           PL_chartData &Indicative_pl[],
                                           DealDetales &historyData[]);          // gráficos de PL de la historia transmitida

   ENUM_DAY_OF_WEEK  getDay(datetime DT);                                        // Obtener el día de las transacciones a partir de la fecha 

  };

3 tipos del gráfico de PL

Para construir el gráfico de beneficio y pérdidas, dibujaremos los datos fuente según la fecha de cierre de la posición. No vamos a entrar en los detalles de la construcción, esta será estándar. El gráfico constará de dos gráficos con las fechas de cierre de las posiciones en el eje Х.  El primero será propiamente el gráfico PL, y el segundo, la reducción acumulada con respecto al valor máximo de PL, en tanto por ciento. Veamos las 3 variantes del gráfico.

  1. El gráfico estándar de beneficio acumulado.
  2. El gráfico de beneficio acumulado sin contar el volumen de las transacciones, como si comerciáramos con un lote en todos los símbolos, a través de la historia completa.
  3. El gráfico de beneficio y pérdidas normalizado con el lote máximo con pérdidas (o beneficio). Si el gráfico PL se encuentra en la zona roja, dividimos la pérdida i-ésima por el lote máximo con beneficios. Si el gráfico se encuentra en la zona verde, dividimos el beneficio i-ésimo por el lote máximo con pérdidas.

Los dos primeros gráficos nos dan una idea de cómo el cambio del volumen de las transacciones influye en el beneficio/pérdidas. El tercer gráfico permite imaginar una situación extrema: si de repende comienza una serie de pérdidas, y estas fueran igual a las pérdidas máximas históricas posibles por un lote(lo que prácticamente no puede suceder). Muestra cuántas pérdidas máximas seguidas puede soportar nuestro gráfico de PL antes de que llegue a 0 (todo el beneficio se perderá); o al contrario, lo que sucederá cuando las pérdidas sean cubiertas por una serie de beneficios máximos. Más abajo se muestran los tipos de gráfico analizados:

Gráfico de PL real

Gráfico de PL en 1 lote

PL indicativo

Los gráficos de PL, al comerciar con un lote, tienen un aspecto mucho más atractivo que el gráfico real. Además, en las transacciones reales, el beneficio ha superado al hipotético "un lote" en solo un 30 %. El tamaño del lote varía de 1 a 10. La reducción en tanto por ciento llegó al 30 000 %. Suena terrible, pero no todo es tan malo. Como ya hemos dicho, el gráfico de PL se construye desde la marca cero, y la reducción se calcula con respecto al valor máximo de la curva de PL. En uno de los momentos, mientras las pérdidas aún no han alcanzado el máximo (zona roja en el gráfico), el beneficio ha aumentado varios rublos, y después ha caído hasta - 18 000 rublos. De aquí se toman estos valores enormes. En realidad, la reducción no ha sido superior al 25 % en un lote, y al 50 % en realidad.

Reducción al comerciar en 1 lote

Reducción total

El gráfico normalizado de PL también se ha atascado en un valor. Esto indica la necesidad de cambiar el enfoque sobre el comercio (supongamos, cambiar el método de gestión de lotes) y, como variante, reoptimizar los algoritmos comerciales. El gráfico de PL considerando un cambio de lotes tiene peor aspecto que el de "un lote" en el mismo periodo, pero a pesar de todo, la gestión de posiciones ha traído sus frutos: los saltos de reducción se han hecho más débiles.

La clase que compone el informe comercial se llama CReportGetter, y los gráficos son construidos por el método get_PLChart_byHistory. Este método se comenta detalladamente en los archivos con el código fuente. Funciona de la forma siguiente:

  1. Se realiza la pasada por la historia transmitida.
  2. Cada posición participa en el cálculo de PL, el beneficio máximo y la reducción.
  3. La reducción se calcula según la acumulación con respecto al beneficio máximo alcanzado. Si el gráfico de PL ha comenzado a descender de golpe a la zona roja, la reducción se recalcula con cada auctualización de los mínimos. En este caso, el benefico máximo será igual a cero, y la reducción en el i-ésimo y todos los demás elementos se recalculará según las pérdidas máximas. La reducción en este momento es igual al 100 %. En cuanto el beneficio máximo resulta superior a cero (la curva de PL pasa a la parte positiva del gráfico), la reducción siempre se recalcula con respecto al beneficio máximo.

Vamos a realizar un análisis comparando los 3 gráficos. Con este método se detectan bastantes más defectos que si analizáramos un gráfico estándar de beneficio y pérdidas. Los gráficos de la estrategia de PL "buy and hold", como ya hemos mencionado, se construyen según el mismo algoritmo. Como ejemplo, presentaremos solo uno de los gráficos. El resto se construirán automáticamente al inciar el script de visualización.

PL según la estrategia Buy and hold

El gráfico muestra que si el comercio con los mismos activos se realizara según la estrategia Buy and hold con los mismos volúmenes, el gráfico de PL difícilmente llegaría a cero y la reducción sería del orden de los 80 000 rub. (en lugar de 70 000, el beneficio máximo alcanzado).

Histograma de acumulación de PL

Este tipo de gráfico supone dos histogramas superpuestos uno sobre otro. Su construcción se diferencia un poco del gráfico PL estándar. El histograma de beneficio se crea con la suma paulatina de las transacciones rentables. Si la i-ésima posición tiene pérdidas, ignoraremos su valor y anotaremos el valor anterior en el i-ésimo intervalo temporal. El histograma de las transacciones con pérdidas se construye con una lógica inversa. Más abajo se muestran los gráficos de beneficio y pérdidas según nuestros datos.

Histograma de PL reales

Histograma de PL al comerciar con un lote

El gráfico PL que conocemos es la diferencia simple del histograma de beneficio y pérdidas. Lo primero que salta a la vista al realizar el análisis es la gran diferencia entre el beneficio obtenido y el comerciado. Si bien el beneficio realmente obtenido ha superado al beneficio del comercio en un lote en aproximadamente un 30%, el análisis del gráfico según los histogramas de beneficio da una diferencia de casi un 50 %. Ese 20% restante del beneficio se oculta en la diferencia de la dinámica de crecimiento del beneficio sobre las pérdidas en los gráficos primero y segundo. Vamos a ilustrar lo descrito. Construiremos según estos histogramas dos gráficos con la dinámica de beneficio y pérdidas. La fórmula es muy sencilla:

Fórmula del Factor de Beneficio

Dinámica del Factor de Beneficio

Dinámica real del Factor de Beneficio

Podemos ver que la dinámica de beneficio en comparación con las pérdidas aumentaba al inicio. Este es el desarrollo ideal de los eventos: con cada nueva transacción, nuestro histograma rentable se separa cada vez más del histograma de transacciones con pérdidas. Y si durante toda la negociación el gráfico de la dinámica de crecimiento de los beneficios sobre las pérdidas tiene una inclinación positiva, nos encontraremos con uno de los mejores resultados. El beneficio se acumula cada vez más rápido, y las pérdidas se incrementan con la misma tasa o una más baja en relación a los beneficios.

El siguiente segmento de los gráficos muestra que la diferencia porcentual entre los histogramas de beneficio y pérdidas ha dejado de aumentar, quedándose atascada en un movimiento lateral. Si el movimiento lateral de incremento del beneficio sobre las pérdidas tiene cualquier intervalo mayor a la unidad, esto será también una buena señal. Si el intervalo es igual a la unidad, la curva de PL se aproximará a cero (cada nuevo beneficio será igual a cada nueva pérdida). Si el intervalo "lateral" de nuestro histograma es menor a 1, habremos empezado a perder nuestro dinero. Dicho de otra forma, los gráficos analizados son gráficos de cambio paulatino del factor de beneficio. Vamos a ilustrar esto:

Histograma de PL en dependencia de Profit factor

Gráfico de PL en dependencia de Profit factor

El histograma azul muestra la acumulación lineal de las pérdidas (en esencia, es un gráfico de 0 a 100).

  • La línea verde es el histograma de pérdidas * 1,3,
  • La línea gris es el histograma de pérdidas * 1,
  • La línea roja es el histograma de pérdidas * 0,7.

Los gráficos muestran que el coeficiente de superación del beneficio sobre las pérdidas (factor de beneficio) siempre debe ser superior a uno. La variante ideal es que crezca de forma paulatina. En este caso, además, el PL crecerá exponencialmente. Por desgracia, esto resulta poco realista a largo plazo, pero esta situación puede suceder en un periodo de tiempo corto y afortunado, si usted incrementa el lote tras cada transacción rentable. El ejemplo analizado nos da derecho a afirmar con el 100% de seguridad que lo más importante en el comercio no es cómo cambia el gráfico de PL, sino cómo se comporta el histograma de transacciones rentables y con pérdidas, y por consiguiente, cuál es la dinámica de cambio del factor de beneficio.

Ahora vamos a hablar del 20% de beneficios perdidos. En el gráfico de la dinámica de crecimiento del beneficio en un lote, podemos ver que los incrementos se han atascado en la marca [1,4 — 1,6]. Esto es superior a [1.2 — 1.4]. Con el tiempo, precisamente esta diferencia se "ha comido" el 20% de beneficio potencial. Dicho con otras palabras, la gestión del tamaño de las posiciones ha dado buen resultado. Si usted usa métodos como martingale/anti-martingale, estos gráficos, especialmente las métricas calculadas sobre su base, pueden darle mucha información útil para el análisis. Estos gráficos son construidos por la función get_PLHistogram. 

Beneficio y pérdidas por días

Un gráfico así indica que el robot ha sido puesto a prueba aunque sea una vez en el simulador del terminal. El sentido y la metodología de construcción de este histograma permanecen sin cambios, pero hemos añadido la posibilidad de construirlo según los datos «absolutos» (con el método sencillo de suma simple de los beneficios y pérdidas por días) y los datos promediados. En la clase adjunta se usa la promediación con el método del valor medio simple. Pero otros medios de promediación (moda/mediana, promediación ponderada) pueden dar resultados más interesantes. Podrá implementar estos tipos de promediación por sí mismo, si así lo desea.

Asimismo, hemos añadido la suma de transacciones (positivas y negativas): dicha suma muestra el número de transacciones por día. El análisis conjunto de este histograma y el histograma de PL por días nos da una idea más fiable del comercio en el contexto de una semana. Por ejemplo, hemos obtenido 50 pequeñas pérdidas y 3 beneficios que cubrirán de sobra todas las pérdidas. Como ejemplo, mostraremos un gráfico construido según los datos absolutos y los precios de cierre de las posiciones.

PL por días

Número diario de transacciones

Como podemos ver en el gráfico de todos los días de la semana, las transacciones positivas han cubierto las negativas, sin embargo, el número de negativas siempre ha sido mayor (y esto, en general, es normal para el trading algorítmico). Asimismo, el número de transacciones negativas nunca ha superado al de positivas en más de un 25 %, lo que también es normal.

Gráfico de puntos extremos y valores absolutos

El gráfico de puntos extremos es un histograma de 4 columnas, divididas en dos grupos. Los puntos extremos son las desviaciones máximas y mínimas en el gráfico de PL (el beneficio máximo alcanzado y la reducción máxima acumulada). Hay dos columnas más: la posición con mayor beneficio y la posición con mayores pérdidas. Ambos grupos de datos se construyen según las transacciones reales, no según los datos de beneficio y pérdidas por una transacción. El cálculo del beneficio máximo de la curva de PL es el punto más alto en ella. La metodología de cálculo de la reducción máxima ya la hemos descrito más arriba. Para ser más concretos, vamos a verlo en una fórmula:

Min Max

Gráfico de puntos extremos

Este gráfico ilustra cuál ha sido la dispersión máxima del beneficio y las pérdidas. La parte de reducción máxima en comparación con el beneficio máximo ha sido del 30 %, y la transacción con mayores pérdidas en comparación con la más rentable, del 60%. En el código esto se implementa como un ciclo con comparación paulatina de los datos de la curva PL de acuerdo con las condiciones indicadas.

En lo que respecta a los valores absolutos, lo mejor es presentarlos como recuadro. También encontramos dos grupos de datos: el primero comprende la suma de todos los beneficios y la suma de todas las pérdidas; el segundo, el valor medio de los beneficios y pérdidas de la historia analizada.

Total Promedio
Beneficio 323237 2244,701
Reducción 261534 1210,806

Recuadros con una breve síntesis del gráfico PL y valores principales de los resultados comerciales

En estos recuadros se caracteriza el rendimiento del comercio en cifras. El código de creación del recuadro contiene las siguientes estructuras:

//+------------------------------------------------------------------+
//| Estructura de los resultados comerciales                         |
//+------------------------------------------------------------------+
struct TotalResult_struct
  {
   double            PL;                                // Beneficio o pérdidas totales
   double            PL_to_Balance;                     // Relación de PL con respecto al balance actual
   double            averagePL;                         // Beneficio o pérdidas medios
   double            averagePL_to_Balance;              // Relación de averagePL con respecto al balance actual
   double            profitFactor;                      // factor de beneficio
   double            reciveryFactor;                    // factor de recuperación
   double            winCoef                            // Coeficiente de ganancia
   double            dealsInARow_to_reducePLTo_zerro;/* Si ahora hay beneficio, número de transacciones seguidas con pérdidas máximas por transacción,
                                                        para llevar el Beneficio actual a cero.
                                                        Si ahora hay pérdidas, número de transacciones seguidas con beneficio máximo por transacción,
                                                        para llevar las pérdidas actuales a cero*/

   double            maxDrowDown_byPL;                  // Reducción máxima de PL
   double            maxDrowDown_forDeal;               // Reducción máxima por transacción
   double            maxDrowDown_inPercents;            // Reducción máxima de PL en tanto por ciento con respecto al balance actual
   datetime          maxDrowDown_byPL_DT;               // Fecha de reducción máxima de PL
   datetime          maxDrowDown_forDeal_DT             //Fecha de reducción máxima por transacción

   double            maxProfit_byPL;                    // Beneficio máximo de PL
   double            maxProfit_forDeal;                 // Beneficio máximo por transacción
   double            maxProfit_inPercents;              // Beneficio máximo de PL en tanto por ciento con respecto al balance actual
   datetime          maxProfit_byPL_DT;                 // Fecha del beneficio máximo de PL
   datetime          maxProfit_forDeal_DT;              // Fecha del beneficio máximo por transacción
  };
//+------------------------------------------------------------------+
//| Parte de la estructura PL_detales (se declara más abajo)         |
//+------------------------------------------------------------------+
struct PL_detales_item
  {
   int               orders;                            // número de transacciones
   double            orders_in_Percent;                 // número de transacciones en % con respecto al número total de órdenes
   int               dealsInARow;                       // Transacciones consecutivas
   double            totalResult;                       // Resultado total en la divisa del depósito
   double            averageResult;                     // Resultado medio en la divisa del depósito
  };
//+--------------------------------------------------------------------+
//| Breve síntesis del gráfico de PL dividido en 2 bloques principales |
//+--------------------------------------------------------------------+
struct PL_detales
  {
   PL_detales_item   profits;                           // Información sobre transacciones rentables
   PL_detales_item   loses;                             // Información sobre transacciones con pérdidas
  };

La primera estructura es TotalResult_struct, que supone una síntesis de los principales valores de toda la historia comercial solicitada. En el mismo entran los valores necesarios (beneficio y pérdidas por transacción, etc.), y los coeficientes calculados de rendimiento del trading.

Las estructuras segunda y tercera están estrechamente relacionadas. La principal de ellas es PL_detales, en esta se concentra una breve síntesis del histograma de beneficio y pérdidas. Según la historia analizada por nosotros, se han obtenido los siguientes valores:

Índice Valor
PL 65039
PL respecto al balance 21,8986532
PL medio 180,1634349
PL medio respecto al balance 0,06066109
Factor de beneficio 1,25097242
Factor de recuperación 3,0838154
Coeficiente de ganancia 1,87645863
Transacciones hasta la reducción de PL a cero 24,16908213
Reducción de PL -23683
Reducción por transacción con 1 lote -2691
Reducción respecto al balance -0,07974074
Fecha de la reducción de PL 24.07.2017 13:04
Fecha de la reducción por transacción con 1 lote 31.01.2017 21:53
Beneficio de PL 73034
Beneficio por transacción con 1 lote 11266
Beneficio respecto al balance 0,24590572
Fecha del beneficio de PL 27.06.2017 23:42
Fecha del beneficio por transacción con 1 lote 14.12.2016 12:51

El segundo recuadro tiene el aspecto que sigue:

Beneficios Pérdidas
Resultado medio 2244,701 -1210,81
Transacciones seguidas 5 10
Transacciones totales 144 216
Transacciones en % 0,398892 0,598338
Resultado general: 323237 -261534

La distribución de posiciones rentables y con pérdidas se puede presentar en forma de diagrama circular:

% de beneficios y reducciones

Como podemos ver por el resultado, el 40 % de las transacciones ha sido positivo.

Conclusión

No es un secreto que al comerciar con la ayuda de algoritmos no basta con optimizarlos e iniciarlos. El resultado del inicio súbito y a toda máquina de un algortimo justo después de la optimización, sin ajustarlo a la estructura del portafolio, puede resultar más que inesperado. Sin embargo, hay muchos tráders razonables que prefieren pensar en su próximo paso. La técnica del análisis histórico usada en el artículo ofrece a estos tráders una posibilidad interesante: les permite comprobar la conveniencia de los pesos elegidos asignados a cada algoritmo comercial. 

Una de las esferas de aplicación más interesantes es el análisis de diferentes métodos de cálculo del portafolio y la prueba en la historia comercial disponible, para luego comparar el rendimiento del portafolio nuevo con los resultados reales. Esto se puede lograr comerciando con un solo lote, que se calcula en función de la historia comercial real en el primer capítulo de este artículo. Sin embargo, hay que tener en cuenta que los datos obtenidos en dichos cálculos serán aproximados, pues no tienen en cuenta la liquidez de los activos, las comisiones y todo tipo de imprevistos.


Al artículo se adjuntan los siguientes archivos:

  1. El archivo CSV dealHistory.csv con la historia de transacciones (se encuentra en la carpeta MQL5\Files\article_4803), los ejemplos mostrados en el presente artículo se basan en ella.
  2. Los archivos fuente para MetaTrader 5 que se encuentran en la carpeta MQL5\Scripts\Get_TradigHistory
  • El proyecto del script de prueba, dividido en las partes indicadas más abajo
Archivos auxiliares:
  • El archivo ListingFilesDirectory.mqh, una bilioteca de funciones WinAPI, que permite operar con los archivos en cualquier lugar de la computadora.
  • Los archivos Tests.mqh y Tests.mq5, que guardan el informe obtenido en un archivo, creando preliminarmente carpetas según las rutas transmitidas.
  • El archivo PlotCharts.mqh, que construye los gráficos de visualización de los informes obtenidos en el propio terminal.
  • El propio script de prueba Get_TradingHistory.mq5, en el que se encuentran dos funciones:
    1. La primera, “test_1” adopta como primer parámetro la ruta al archivo con la historia de prueba, y como segundo parámetro, la ruta a la carpeta con los resultados. Esta función crea un informe de los archivos de prueba transmitidos en el primer parámetro de la función.
    2. La segunda es “test_2”, que adopta como parámetro solo la ruta a la segunda carpeta (las rutas deberán ser distintas), donde se guardará el archivo con el comercio realizado por usted.

Archivos principales:

  • Archivo DealHistoryGetter.mqh,  – aquí se encuentran las funciones que implementan el mecanismo de descarga de datos, descrito en el primer capítulo de este artículo.
  • Archivo ReportGetter.mqh  – en este se implementa el mecanismo de creación de los propios informes de las transacciones. La clase contenida en este archivo usa la historia formada y la procesa.


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

Archivos adjuntos |
MQL5.zip (266.19 KB)
950 sitios web transmiten el calendario económico de MetaQuotes 950 sitios web transmiten el calendario económico de MetaQuotes

La adición del widget proporciona a los sitios web un horario de publicación detallado de 500 índices e indicadores de las mayores economías mundiales. De esta forma, los tráders, aparte del contenido principal del sitio web, reciben de manera operativa información actual sobre todos los eventos importantes, complementada con explicaciones y gráficos.

Análisis comparativo de 10 estrategias de flat Análisis comparativo de 10 estrategias de flat

En el artículo se analizan las ventajas y desventajas del comercio con flat (mercado plano). Asimismo, se han creado y probado 10 estrategias basadas en el monitoreo del movimiento del precio dentro del canal. Cada estrategia está provista de un mecanismo de filtrado, para descartar las señales falsas de entrada en el mercado.

50 000 trabajos ejecutados en la bolsa Freelance de MQL5.com 50 000 trabajos ejecutados en la bolsa Freelance de MQL5.com

Hasta el mes de octubre de 2018, los participantes del servicio Freelance oficial para las plataformas MetaTrader han ejecutado más de 50 000 encargos. Se trata de la bolsa más grande del mundo de trabajo a distancia para programadores de MQL: más de mil desarrolladores, decenas de nuevos encargos diarios por parte de tráders y localización en 7 idiomas.

Neuroredes profundas (Parte VIII). Aumentando la calidad de la clasificación de los conjuntos bagging Neuroredes profundas (Parte VIII). Aumentando la calidad de la clasificación de los conjuntos bagging

En el artículo se analizan tres métodos con cuya ayuda podemos aumentar la calidad de clasificación de los conjuntos bagging y valorar su efectividad. Se ha evaluado cómo influye la optimización de los hiperparámetros de las redes neuronales ELM y los parámetros de post-procesado en la calidad de clasificación del conjunto.