Análisis de los gráficos del Balance/Equidad usando los símbolos y ORDER_MAGIC de los Asesores Expertos

Vladimir Karputov | 29 mayo, 2017


Contenido


Planteamiento del problema

Cuando la cobertura (hedging) fue introducida en MetaTrader 5, apareció una perfecta oportunidad de negociar simultáneamente con varios Asesores Expertos (o varias estrategias) en la misma cuenta. Se puede monitorear la cuenta comercial entera en el servicio Señales, y obtener la estadística para ella. Me quedaba una pregunta sumamente importante para mí: ¿Cómo visualizar la aportación de cada estrategia en los gráficos del Balance y Equidad?

Es que, es bien probable la situación cuando una estrategia resulta rentable, mientras que la otra trae pérdidas, y al final el monitoreo general mostrará el meneo alrededor de cero. En este caso, es muy deseable diseñar los gráficos del Balance y Equidad para cada estrategia comercial por separado.


1. Comisión. Swap. Beneficio

El resultado financiero final se forma con la suma de tres parámetros:

 Result=Deal commission +Cumulative swap on close+ Deal profit

Estas propiedades de las transacciones se obtienen usando HistoryDealGetDouble() con los siguientes identificadores:

DEAL_COMMISSION

Deal commission

double

DEAL_SWAP

Cumulative swap on close

double

DEAL_PROFIT

Deal profit

double


Aquí tenemos el ejemplo de la obtención de las propiedades de las transacciones a partir del historial comercial para un determinado intervalo de tiempo en el script "HistoryDealGetTicket.mq5".

Es el resultado del trabajo del script (han sido eliminadas las transacciones con el tipo DEAL_ENTRY_IN, ya que no contienen el resultado financiero):

...
  4: deal #36774600 at 2017.02.15 10:17:50 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 1.52 NZDUSD.m (order #47802989, position ID 47770449)
...
12: deal #36798157 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.01 profit: 2.98 EURUSD.m (order #47827771, position ID 47685190)
13: deal #36798161 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.02 profit: 5.99 EURUSD.m (order #47827785, position ID 47665575)
14: deal #36798176 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.02 profit: 5.93 EURUSD.m (order #47827805, position ID 47605603)
15: deal #36798185 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.03 profit: 5.98 EURUSD.m (order #47827821, position ID 47502789)
16: deal #36798196 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.03 profit: 8.96 EURUSD.m (order #47827832, position ID 47419515)
17: deal #36798203 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.06 profit: 8.92 EURUSD.m (order #47827835, position ID 47130461)
18: deal #36798212 at 2017.02.15 16:44:19 Entry out, sell vol: 0.01 comm: 0 swap: -0.48 profit: -21.07 EURUSD.m (order #47827845, position ID 46868574)
...
25: deal #36824799 at 2017.02.15 19:57:57 Entry out, sell vol: 0.01 comm: 0 swap: 0 profit: 2.96 NZDUSD.m (order #47855548, position ID 47817757)
26: deal #36824800 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0 profit: 3.01 NZDUSD.m (order #47855549, position ID 47790966)
27: deal #36824801 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 3.07 NZDUSD.m (order #47855550, position ID 47777495)
28: deal #36824802 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 3 NZDUSD.m (order #47855551, position ID 47759307)
29: deal #36824803 at 2017.02.15 19:57:59 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 1.52 NZDUSD.m (order #47855552, position ID 47682775)
...
33: deal #36832775 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 2.96 NZDUSD.m (order #47863883, position ID 47826616)
34: deal #36832776 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 3.05 NZDUSD.m (order #47863884, position ID 47803010)
35: deal #36832777 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 2.98 NZDUSD.m (order #47863885, position ID 47792294)
36: deal #36832778 at 2017.02.16 00:58:42 Entry out, sell vol: 0.01 comm: 0 swap: 0.07 profit: 2.88 NZDUSD.m (order #47863886, position ID 47713741)
...

Como vemos, el swap y el beneficio pueden tener tanto el signo "+", como el signo "-". Por eso en la fórmula del resultado final, para el swap y el beneficio se utiliza la adición.


2. Cálculo de la equidad y del balance a base del historial

El esquema general del trabajo es el siguiente: formamos la lista «posiciones abiertas», dividimos el historial de trading en segmentos de 5-10 minutos (aclararemos exactamente este parámetro más tarde). Luego, consecutivamente, para cada segmento:

  • buscamos las transacciones de salida del mercado. Si han sido encontradas, entonces basándose en el resultado financiero final de estas transacciones, recalculamos el valor para los gráficos «Balance» y «Equidad». Corregimos la lista «Posiciones abiertas».
  • según las posiciones abiertas, calculamos el valor para el gráfico «Equidad». 

2.1. Lista «Posiciones abiertas»

Para llevar la cuenta de las posiciones abiertas en el intervalo seleccionado, necesitaremos la clase de transacciones. Vamos a implementar eso en la clase "CHistoryDeal", en el archivo de inclusión "HistoryDeal.mqh":

Método Valor
TicketDeal Obtiene/establece la propiedad de la transacción «Ticket de la transacción».
PosIDDeal Obtiene/establece la propiedad de la transacción «Identificador de la posición».
SymbolDeal Obtiene/establece la propiedad de la transacción «Símbolo de la transacción».
TypeDeal Obtiene/establece la propiedad de la transacción «Tipo de la transacción» a partir de la enumeración ENUM_DEAL_TYPE
EntryDeal Obtiene/establece la propiedad de la transacción «Dirección de la transacción» a partir de la enumeración ENUM_DEAL_ENTRY
VolumeDeal Obtiene/establece la propiedad de la transacción «Volumen de la transacción».
PriceDeal Obtiene/establece la propiedad de la transacción «Precio de apertura de la transacción».


2.2. Fórmula del beneficio flotante

La fórmula del beneficio se coge desde la descripción de la enumeración ENUM_SYMBOL_CALC_MODE. Además, hay que recordar siempre en qué divisa se calcula el beneficio para este símbolo. La divisa en la que se calcula el beneficio se puede obtener:

SymbolInfoString(m_name,SYMBOL_CURRENCY_BASE);

  • o simplemente abriendo la especificación del símbolo en el terminal:

rts specification

Fig. 1. Especificación RTS-3.17

2.3. Asegurándose de que todos los símbolos están disponibles

Posibles problemas:

  • el símbolo desde el historial de trading no está presente en la «Observación del mercado» (la búsqueda se realiza repasando la lista general de los símbolos);
  • la divisa del beneficio del símbolo no puede ser recalculada en la divisa del depósito, es decir en la «Observación del mercado» no hay símbolo para ser recalculado.

Para la cuenta de posibles errores, en el EA a nivel de variables globales del programa (atención: ¡programa, no terminal!) se introduce el array para los símbolos «malos» que no están presentes en la «Observación del mercado» o para los cuales es imposible recalcular la divisa del beneficio en la divisa del depósito:

string m_arr_defective_symbols[];                        // array for defective symbols

La secuencia de la ejecución del del proceso de aseguramiento (todas las fases se realizan en la función "SearchDefectiveSymbols"):

  1. creamos el array temporal (auxiliar) de todos los símbolos disponibles en la «Observación del mercado»;
  2. creamos el array temporal (auxiliar) de todos los símbolos del historial de trading (en un determinado intervalo de ... a ...);
  3. buscamos los símbolos desde el historial de trading en la «Observación del mercado»; Si alguno de los símbolos no se encuentra, se introduce en el array de símbolos «malos»;
  4. añadimos (mostramos) los símbolos desde el historial de trading a la «Observación del mercado»; Si la operación falla, el símbolo se introduce en el array de símbolos «malos»;
  5. obtenemos la divisa del beneficio a partir de las propiedades del símbolo. Si la divisa del beneficio se diferencia de la divisa del depósito, vamos a punto 5.1.
    1. En la «Observación del mercado», tratamos de encontrar el símbolo a recalcular que corresponde a «divisa del depósito» + «divisa del beneficio» o «divisa del beneficio» + «divisa del depósito». Si la operación falla, el símbolo se introduce en el array de símbolos «malos»

Después de que la función "SearchDefectiveSymbols" termine su trabajo, se formará el array con símbolos «malos» del historial de trading. Los símbolos «malos» no van a participar en los cálculos posteriores (durante el cálculo del gráfico «Balance» y gráfico «Equidad»).

2.4. Cálculo de las posiciones abiertas y cerradas a base del historial

Todos los cambios del balance los mostramos en el array bidimencional "m_arr_balance_equity".

Solicitamos el historial en el intervalo de fechas "from date" y "to date" (son parámetros de entrada del EA) y lo dividimos por el número de los ciclos con duración "timer (minutes)". El trabajo se realiza en la función "CreateBalanceEquity". Escribimos todas las transacciones en el array dinámico de punteros "ArrOpenPositions". A continuación, trabajamos con este array: añadimos y eliminamos las transacciones.

Dependiendo del parámetro de la transacción comercial «Dirección de la transacción» —ENUM_DEAL_ENTRY— vamos a calcular los gráficos «Balance» y «Equidad» de maneras diferentes. Si desea comprobar el comportamiento de la apertura, cierre, cierre parcial o la reversa de la posición en las cuentas de cobertura (hedging) y compensación (netting), puede iniciar el script "HistorySelect.mq5".

  • ‌DEAL_ENTRY_IN — Entrada en el mercado. Escribimos la transacción en el array "ArrOpenPositions"
  • DEAL_ENTRY_OUT — Salir del mercado. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • si la búsqueda ha devuelto false, entonces no introducimos cambios en los gráficos «Balance» y «Equidad»
    • si la búsqueda ha devuelto true, comparamos el volumen (en lotes)
      • los volúmenes son iguales, eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")
      • volumen de la transacción del array "ArrOpenPositions" es más grande, corregimos el volumen de la transacción en el array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")
      • el volumen de la transacción del array "ArrOpenPositions" es menos, eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")
  • DEAL_ENTRY_INOUT — Reversa. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • si la búsqueda ha devuelto false, entonces no introducimos cambios en los gráficos «Balance» y «Equidad»
    • si la búsqueda ha devuelto true, corregimos el volumen de la transacción en el array "ArrOpenPositions", corregimos el tipo de la transacción en el array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")
  • DEAL_ENTRY_OUT_BY — Cierre de la posición opuesta. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • los volúmenes son iguales, eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")
    • los volúmenes son diferentes, corregimos el volumen de la transacción en el array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity")

2.5. Cómo se calcula el beneficio flotante (Equidad)

Hay que recorrer el array "ArrOpenPositions" y calcular el beneficio flotante para todas las transacciones en este array. Si la transacción se encuentra en el array "ArrOpenPositions", entonces tenemos los siguientes datos para cada transacción de los cuales necesitaremos:

  • símbolo
  • tipo (BUY or SELL)
  • volumen
  • precio (precio por el que ha sido abierta la transacción),

así mismo tenemos la fecha final hasta la cual ha sido solicitada el historial de trading. Sólo faltan los precios para el símbolo de esta transacción para la fecha final hasta la cual ha sido solicitada el historial de trading. Para obtener este precio usando CopyTicks en "CalculationFloatingProfit". Recordaré que la fórmula del beneficio se coge desde la descripción de la enumeración ENUM_SYMBOL_CALC_MODE .

En el proceso del perfilado (profiling), ha resultado que el método CopyTicks requiere mucho tiempo, sobre todo cuando en el array "CalculationFloatingProfit" se encuentran varias transacciones para el mismo símbolo. En este caso, resulta que los mismos datos van a solicitarse varias veces: el símbolo es el mismo y la fecha es la misma. Para acelerarse, ha sido introducido el caching de los resultados CopyTicks 

//--- caching results "CopyTicks"
   static string arr_name[];
   static double arr_ask[];
   static double arr_bid[];
   static datetime prev_time=0;
   int number=-1;
   if(time>prev_time)
     {
      prev_time=time;
      ArrayFree(arr_name);
      ArrayFree(arr_ask);
      ArrayFree(arr_bid);
     }
   found=false;
   size=ArraySize(arr_name);
   if(size>0)
      for(int i=0;i<size;i++)
        {
         if(name==arr_name[i])
           {
            number=i;
            found=true;
            break;
           }
        }
   if(found)
     {
      ArrayResize(ticks_array,1);
      ticks_array[0].ask=arr_ask[number];
      ticks_array[0].bid=arr_bid[number];
     }
   else
     {
      //---
      int copy_ticks=-1;
      int count=0;
      while(copy_ticks==-1 && count<5)
        {
         copy_ticks=CopyTicks(name,ticks_array,COPY_TICKS_INFO,1000*(ulong)time,1);
         //if(copy_ticks==-1)

— los resultados bid y ask se guardan en los arrays locales: si en este intervalo temporal ya han sido obtenidos los ticks para el símbolo, entonces estos ticks simplemente se cogen de los arrays locales. Este enfoque permite acelerarse 10-15 veces. 


3. Integramos el Asesor Experto en el panel de diálogos

Ahora, cuando la base del EA ya es funcional, se pude proveer al EA con una interfaz cómoda. Partiendo del título del artículo «Análisis de los gráficos del Balance/Equidad usando los símbolos y ORDER_MAGIC de los Asesores Expertos», es necesario prever varias posibilidades de la filtración:

  • mostrar todos los símbolos/mostrar uno o varios símbolos seleccionados;
  • mostrar por todos los magic/mostrar sólo por uno o varios símbolos seleccionados.

Para contar todos los símbolos de trading ya tenemos el array "m_arr_all_trade_symbols". Está declarado a nivel global de programa. Hay que introducir otro array, "m_arr_all_magics", para contar todos los magic. Para eso actualizamos la función "FillArrayTradeSymbols": ahora en ella también va a rellenarse el array "m_arr_all_magics".

3.1. Vista general del panel de diálogos

panel

Fig. 2. Vista general del panel

Después de formar los arrays de los símbolos «malos», todos los símbolos y los magic, en el panel se rellenan dos listas (controles a base de la clase CComboBox): la lista izquierda con todos los símbolos de trading y la lista derecha, con todos los magic. En estas listas, el primer puesto ocupa la selección de todos los símbolos y todos los magic:

panel combo box

Fig. 3. Listas desplegables

La lógica del trabajo del EA del panel será la siguiente: después y sólo después de pulsar el botón «Start», va a comprobarse qué es lo que exactamente está seleccionado en estas dos listas desplegables. Dependiendo de los parámetros seleccionados, en estas listas va a realizarse la construcción de los gráficos Balance/Equidad a base del historial de trading.

3.2. Interacción con el panel

He decidido que el traspaso del código entero del EA a la clase del panel ("APHDialog.mqh") es un proceso demasiado largo. Una solución alternativa es introducir las variables internas m_ready, m_symbol y m_magic en la clase del panel:

//+------------------------------------------------------------------+
//| Class CAPHDialog                                                 |
//| Usage: main dialog of the Controls application                   |
//+------------------------------------------------------------------+
class CAPHDialog : public CAppDialog
  {
private:
   CLabel            m_label_symbols;                 // the label object
   CComboBox         m_combo_box_symbols;             // the combo boxp object
   CLabel            m_label_magics;                  // the label object
   CComboBox         m_combo_box_magics;              // the combo box object
   CButton           m_button_start;                  // the button object
   //---
   bool              m_ready;                         // true -> you can build graphics
   string            m_symbol;
   ulong             m_magic;

public:

La decisión sobre el estado de la variable m_ready va a tomarse durante el procedimiento del procesamiento del clic en el botón "Start":

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CAPHDialog::OnClickButtonStart(void)
  {
   if(m_combo_box_symbols.Select()!=" " && m_combo_box_magics.Select()!=" ")
      m_ready=true;
   else
      m_ready=false;
//Comment(__FUNCTION__+" ButtonStartclick"+"\n"+
//        "Symbols: "+"\""+m_combo_box_symbols.Select()+"\""+"\n"+
//        "Magic: "+"\""+m_combo_box_magics.Select()+"\""+"\n"+
//        "m_ready: "+IntegerToString(m_ready));
  }

Preste atención en la línea resaltada: justo después de crear y rellenar las listas desplegables (para nosotros son los controles m_combo_box_symbolsm_combo_box_magics), en estas listas tenemos establecido el elemento con el valor " ", o sea el espacio.

La decisión sobre el estado de las variables m_symbol m_magic va a tomarse durante los procedimientos del procesamiento del clic en las listas desplegables correspondientes:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CAPHDialog::OnChangeComboBoxSymbols(void)
  {
//Comment(__FUNCTION__+" \""+m_combo_box_symbols.Select()+"\"");
   if(m_combo_box_symbols.Select()=="All symbols")
      m_symbol="";
   else
      m_symbol=m_combo_box_symbols.Select();
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CAPHDialog::OnChangeComboBoxMagics(void)
  {
//Comment(__FUNCTION__+" \""+m_combo_box_magics.Select()+"\"");
   if(m_combo_box_magics.Select()=="All magics")
      m_magic=-1;
   else
      m_magic=StringToInteger(m_combo_box_magics.Select());
  }

De esta manera, tras el clic en el botón "Start" van a rellenarse tres variables: m_ready, m_symbol m_magic. Nos queda inventar la forma de avisar al EA sobre el hecho de que en el panel ha sido realizada la selección de parámetros. La solución es simple: en el EA iniciamos el temporizador con el intervalo de 3 segundos que va a preguntar el panel. Para eso, escribimos el método "CAPHDialog::IsReady" en el panel.

//+------------------------------------------------------------------+
//| On the panel there are chosen parameters                         |
//+------------------------------------------------------------------+
bool CAPHDialog::IsReady(string &symbol,ulong &magic)
  {
   if(m_ready)
     {
      symbol=m_symbol;
      magic=m_magic;
      m_ready=false;
      return(true);
     }
   else
      return(false);
  }

En este método, escribimos el valor de las variables internas en las variables traspasadas por referencia, y reseteamos la variable interna m_ready.

3.3. Una pequeña corrección: registro del magic seleccionado

La selección de las transacciones se realiza conforme a las condiciones establecidas: según el símbolo o según todos los símbolos y según el magic o según todos los magic, en "GetHistory":

//--- for all deals 
   for(int i=0;i<deals;i++)
     {
      deal_ticket          = HistoryDealGetTicket(i);
      deal_position_ID     = HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID);
      deal_symbol          = HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
      deal_type            = (ENUM_DEAL_TYPE)HistoryDealGetInteger(deal_ticket,DEAL_TYPE);
      deal_entry           = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY);
      deal_volume          = HistoryDealGetDouble(deal_ticket,DEAL_VOLUME);
      deal_price           = HistoryDealGetDouble(deal_ticket,DEAL_PRICE);
      deal_commission      = HistoryDealGetDouble(deal_ticket,DEAL_COMMISSION);
      deal_swap            = HistoryDealGetDouble(deal_ticket,DEAL_SWAP);
      deal_profit          = HistoryDealGetDouble(deal_ticket,DEAL_PROFIT);
      deal_magic           = HistoryDealGetInteger(deal_ticket,DEAL_MAGIC);
      if(sSymbol!="")
         if(deal_symbol!=sSymbol)
            continue;
      if(uMagic!=ULONG_MAX)
         if(deal_magic!=uMagic)
            continue;
      //--- onle BUY or SELL

Nótese que para la variable uMagic el valor ULONG_MAX significa «todos los magic», y para la variable sSymbol el valor "" significa «Todos los símbolos».

El vídeo de trabajo de la construcción del balance y beneficio flotante a base del historial de trading (primero, para todos los símbolos, luego, sólo para uno):


Vídeo


4. Gráfico de distribución de MFE y MAE

Para cada posición abierta, durante su vida, se guardan los valores del benefició máximo (MFE) y pérdidas máximas (MAE). Estos valores caracterizan adicionalmente cada posición cerrada usando los valores del potencial máximo no realizado y riesgo máximo permitido. En los gráficos de distribución MFE/Profit y MAE/Profit cada posición se muestra como un punto que por la horizontal recibe el valor del beneficio/pérdida obtenido, y por la vertical recibe los valores máximos del beneficio potencial (MFE) y pérdidas potenciales (MAE).

4.1. Principio del cálculo de MFE y MAE

A nivel global de programación se declaran dos arrays: "m_arr_pos_id" para el cálculo de posiciones y "m_arr_mfe_mae" para el cálculo de MFE, resultado financiero final y MAE: 

long   m_arr_pos_id[];
double m_arr_mfe_mae[][3];  // [][0] - mfe, [][1] - final financial result,[][2] - mae

Posteriormente, a base del array "m_arr_mfe_mae", van a construirse los gráficos de distribución por puntos de MFE y MAE.

Los arrays "m_arr_pos_id" para el cálculo de posiciones y "m_arr_mfe_mae" para el cálculo de mfe siempre tienen el mismo tamaño en la primera dimensión, así, para la posición con el índice "i" (m_arr_pos_id[i]) siempre habrá una coincidencia explícita m_arr_mfe_mae[i][][][]. Los tamaños de estos dos arrays se establecen en "GetHistory":

         if(deal_symbol=="")
            DebugBreak();
         ArrOpenPositions.Add(HistoryDeal);
         //--- mfe, mae
         int size=ArraySize(m_arr_pos_id);
         ArrayResize(m_arr_pos_id,size+1,10);
         ArrayResize(m_arr_mfe_mae,size+1,10);
         m_arr_pos_id[size]=deal_position_ID;
         // [][0] - mfe, [][1] - final financial result,[][2] - mae
         m_arr_mfe_mae[size][0]=0.0;
         m_arr_mfe_mae[size][1]=0.0;
         m_arr_mfe_mae[size][2]=0.0;
         continue;
        }
      //--- 
      if(deal_entry==DEAL_ENTRY_OUT)

La función que se encarga de calcular el beneficio máximo y la pérdida máxima para cada posición es ésta:

//+------------------------------------------------------------------+
//| Add Result Mfe Mae                                               |
//+------------------------------------------------------------------+
void AddResultMfeMae(const long pos_id,const double floating_profit,const double financial_result)
  {
// [][0] - mfe (profit), [][1] - final financial result,[][2] - mae (loss)
//--- search pos_id
   int position=-1;
   int size=ArraySize(m_arr_pos_id);
   for(int i=0;i<size;i++)
      if(m_arr_pos_id[i]==pos_id)
        {
         position=i;
         break;
        }
   if(position==-1)
      return;

//---
   if(floating_profit==0.0)
      return;

   if(floating_profit>0.0) // profit
     {
      if(m_arr_mfe_mae[position][0]<floating_profit)
         m_arr_mfe_mae[position][0]=floating_profit;
     }
   else // loss
     {
      if(m_arr_mfe_mae[position][2]>floating_profit)
         m_arr_mfe_mae[position][2]=floating_profit;
     }
   m_arr_mfe_mae[position][1]=financial_result;
  }

Aquí la regla es la siguiente: hay que traspasar los tres parámetros sí o sí. Es decir, si nuestra posición virtual sigue estando en la lista de posiciones abiertas "ArrOpenPositions" y su beneficio flotante es igual a "-20.2", entonces la llamada será la siguiente:

    AddResultMfeMae(pos_id,-20.2,0.0);

si nuestra posición virtual sigue estando en la lista de posiciones abiertas "ArrOpenPositions" y su beneficio flotante es igual a "+5.81", entonces la llamada será la siguiente:

    AddResultMfeMae(pos_id,5.81,0.0);

y si nuestra posición virtual se elimina de la lista de posiciones abiertas "ArrOpenPositions" y su resultado financiero final es igual a "-3.06", entonces la llamada tendrá el siguiente aspecto:

    AddResultMfeMae(pos_id,0.0,-3.06);

O sea, si hay beneficio flotante, entonces el resultado financiero final es igual a "0", si la posición se cierra, el beneficio flotante es igual a"0".

4.2. Procesamiento de posiciones

  • ‌DEAL_ENTRY_IN — Entrada en el mercado. Escribimos la transacción en el array "ArrOpenPositions" Creamos nuevo control en "m_arr_pos_id" y "m_arr_mfe_mae"
  • DEAL_ENTRY_OUT — Salir del mercado. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • si la búsqueda ha devuelto false, entonces no introducimos cambios en los gráficos «Balance» y «Equidad»
    • si la búsqueda ha devuelto true, comparamos el volumen (en lotes)
      • los volúmenes son iguales - eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambios en el array "m_arr_balance_equity" - en la columna «Balance». En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].
      • el volumen de la transacción del array "ArrOpenPositions " es más grande, corregimos el volumen de la transacción en el array "ArrOpenPositions" e introducimos los cambios en  el array "m_arr_balance_equity la columna «Balance»") En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].
      • el volumen de la transacción del array "ArrOpenPositions" es menos, eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambios en  la columna «Balance» (array "m_arr_balance_equity") En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].
  • DEAL_ENTRY_INOUT - Reversa. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • si la búsqueda ha devuelto false, entonces no introducimos cambios en los gráficos «Balance» y «Equidad»
    • si la búsqueda ha devuelto true, corregimos el volumen de la transacción en el array "ArrOpenPositions", corregimos el tipo de la transacción en el array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» (array "m_arr_balance_equity ") En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].
  • DEAL_ENTRY_OUT_BY — Cierre de la posición opuesta. En el array "ArrOpenPositions" buscamos la transacción con el tipo DEAL_ENTRY_IN con el mismo DEAL_POSITION_ID
    • los volúmenes son iguales - eliminamos la transacción del array "ArrOpenPositions" e introducimos los cambiosenel array "m_arr_balance_equity" - en la columna«Balance». En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].
    • los volúmenes son diferentes, corregimos el volumen de la transacción en el array "ArrOpenPositions" e introducimos los cambios en la columna «Balance» del array "m_arr_balance_equity" En el array "m_arr_mfe_mae" se escribe el [beneficio flotante 0.0][resultado financiero final].

Además, en "GetHistory", si es necesario realizar el recálculo del beneficio flotante, en el array "m_arr_mfe_mae" escribimos [beneficio flotante][resultado financiero final 0.0] para cada posición.

4.3. Panel modificado

Para mostrar el gráfico Balance/Equidad y MFE y MAE, el panel quedará un poco modificado:

panel 2

Fig. 4. Panel modificado

Los gráficos MFE (ganancia máxima) y MAE (pérdida máxima) se construyen por dos coordenadas: la coordenada "X" - el valor del resultado financiero final, y el eje "Y" - valor MFE o MAE, respectivamente.

MFE

Fig. 5. MFE

MAE

Fig. 6.


Conclusión

Ahora durante el trading simultáneo de varios EAs, en las cuentas hedge, se puede observar las estadísticas del balance y equidad para cada símbolo y cada magic- es decir, se puede determinar visualmente cuál ha sido la aportación de cada EA determinado (ORDER_MAGIC) en el balance común, y lo más importante, qué reducciones (drawdown) ha tenido cada EA.

Programas usados en el artículo:

#
 Nombre
Tipo
Descripción
1
HistoryDeal.mqh Librería Clase para llevar la cuenta de posiciones abiertas en el intervalo seleccionado
2
HistoryDealGetTicket.mq5 Asesor Experto Ejemplo de la obtención de las propiedades de las transacciones a partir del historial comercial para un determinado intervalo de tiempo
3
APHDialog.mqh Librería Clase del panel de diálogo del Asesor Experto
4
Accounting_positions_on_history.mq5 Asesor Experto Asesor Experto según el artículo.
5
MQL5.zip Archivo  Archivo con el EA principal y sus archivos de inclusión.