English Русский 中文 Deutsch 日本語 Português
Recetas MQL5 – Obteniendo las propiedades de una posición de cobertura abierta

Recetas MQL5 – Obteniendo las propiedades de una posición de cobertura abierta

MetaTrader 5Ejemplos | 20 diciembre 2018, 15:54
916 0
Denis Kirichenko
Denis Kirichenko

Introducción

Hace relativamente poco, en el terminal comercial MetaTrader 5 apareció la posibilidad de abrir órdenes multidireccionales. Este sistema de registro de posiciones se denomina de «cobertura». La presencia de este sistema de órdenes permite trasladar cómodamente los algoritmos comerciales de MetaTrader 4 a la quinta versión de la plataforma, utilizando todas las ventajas de la última. Podrá leer más detalles sobre la cobertura de MetaTrader 5 en el artículo "Se ha añadido a MetaTrader 5 el sistema de cobertura de registro de posiciones".

En este artículo, hablaremos de las propiedades de la posición general con la que se trabajará en el sistema de «cobertura».


1. Posición de cobertura, tipos

La posición de cobertura (conjunta) es una posición de mercado formada por varias órdenes de mercado. En un sentido más restringido, la posición de cobertura (hedge) incluye varias órdenes en direcciones opuestas (compra y venta). Sin embargo, también proponemos usar el término «hedge» en un sentido más amplio. En este caso, además, estaremos de acuerdo en que una posición de cobertura también generaliza las órdenes en una misma dirección. Este enfoque viene condicionado por las posibilidades del terminal MetaTrader 5: podemos abrir órdenes en una dirección, y también en distintas.

Para clasificar una posición conjunta, podemos usar varios métodos. Probablemente, el más popular sea el criterio que distingue las posiciones según el tipo de órdenes de mercado que forman esta o aquella posición. Bien, ¿con qué órdenes puede ser rellenada esta posición? Más abajo, en el Recuadro 1, se muestran diferentes combinaciones.

Tipo Descripción
1 Hedge buy Solo compra
2 Hedge netting buy Compra "Líquida"
3 Hedge sell Solo ventas
4 Hedge netting sell Venta "Líquida"
5 Hedge locked Bloqueo (cobertura total)

Recuadro 1. Tipo

Descripción breve. Si en la posición conjunta hay órdenes (en los términos de MetaTrader 4) solo de compra o solo de venta, dicha posición será considerada o bien hedge buy, o bien hedge sell. Si en las posiciones hay órdenes mixtas (tanto de compra, como de venta), veremos de qué tipo de órdenes hay más. Si hay más órdenes de compra, la posición será hedge netting buy. Si hay más órdenes de venta, será hedge netting sell. Además, siendo más precisos, deberemos hablar no de número de órdenes, sino de sus volúmenes. Supongamos que en una posición hay 1 orden de compra con un volumen de 1,25 lote y dos órdenes de venta con 0,5 y 0,6 lotes, respectivamente. Como resultado, tendremos una posición líquida de compra (hedge netting buy) de 0,15 lotes:

1,25 – (0,5 + 0,6) = 0,15.

Un tipo especial de orden mixta es el bloqueo, en el que las compras y las ventas se equilibran según su volumen comercial.

Vamos a formalizar los tipos descritos de cobertura en la lista siguiente:

//+------------------------------------------------------------------+
//| Hedge type                                                       |
//+------------------------------------------------------------------+
enum ENUM_HEDGE_TYPE
  {
   HEDGE_BUY=0,          // buy
   HEDGE_SELL=1,         // sell   
   HEDGE_NETTING_BUY=2,  // netting buy   
   HEDGE_NETTING_SELL=3, // netting sell
   HEDGE_LOCKED=4,       // lock
  };


Si recurrimos al sistema de "compensación", en este, por definición, una posición puede pertenecer solo a uno de los dos tipos, o bien buy, o bien sell. Uno de los valores de la lista, ENUM_POSITION_TYPE, servirá como identificador:

e1) POSITION_TYPE_BUY;

2) POSITION_TYPE_SELL.

En el sistema de "cobertura", obtenemos 5 tipos de posiciones conjuntas.

En el siguiente apartado crearemos una clase para procesar las propiedades de la posición de cobertura.


2. Clase CHedgePositionInfo

Debemos destacar que en la Biblioteca Estándar existe la clase CPositionInfo, que ofrece acceso a las propiedades de una posición de mercado abierta. En nuestro caso, esta clase nos vendrá bien solo en parte, pues la posición que procesa se presentará en forma de orden de mercado aparte (en los términos de MetaTrader 4), mientras que nosotros necesitamos una clase que procese de golpe todas las posiciones que conforman una posición conjunta (hedge).

Vamos a utilizar los recursos de la POO para crear la clase CHedgePositionInfo:

//+------------------------------------------------------------------+
//| Class CHedgePositionInfo                                         |
//| Purpose: Class for access to a hedge position info.              |  
//|              Derives from class CObject.                         |
//+------------------------------------------------------------------+
class CHedgePositionInfo : public CObject
  {
   //--- === Data members === --- 
private:
   ENUM_HEDGE_TYPE   m_type;
   double            m_volume;
   double            m_price;
   double            m_stop_loss;
   double            m_take_profit;
   ulong             m_magic;
   //--- objects
   CArrayLong        m_tickets;
   CSymbolInfo       m_symbol;
   CPositionInfo     m_pos_info;

   //--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CHedgePositionInfo(void){};
   void             ~CHedgePositionInfo(void){};
   //--- initialization
   bool              Init(const string _symbol,const ulong _magic=0);
   //--- get methods
   CSymbolInfo      *Symbol(void)       {return GetPointer(m_symbol);};
   CArrayLong       *HedgeTickets(void) {return GetPointer(m_tickets);};
   CPositionInfo    *PositionInfo(void) {return GetPointer(m_pos_info);};
   ulong             Magic(void) const  {return m_magic;};
   //--- fast access methods to the integer hedge properties
   datetime          Time(void);
   ulong             TimeMsc(void);
   datetime          TimeUpdate(void);
   ulong             TimeUpdateMsc(void);
   ENUM_HEDGE_TYPE   HedgeType(void);
   //--- fast access methods to the double hedge properties
   double            Volume(double &_buy_volume,double &_sell_volume);
   double            PriceOpen(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL);
   double            StopLoss(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL);
   double            TakeProfit(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL);
   double            PriceCurrent(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL);
   double            Commission(const bool _full=false);
   double            Swap(void);
   double            Profit(void);
   double            Margin(void);
   //--- fast access methods to the string hedge properties
   string            TypeDescription(void);
   //--- info methods
   string            FormatType(string &_str,const uint _type) const;
   //--- select
   bool              Select(void);
   //--- state
   void              StoreState(void);
   bool              CheckState(void);

private:
   //--- calculation methods
   bool              AveragePrice(
                                  const SPositionParams &_pos_params,
                                  double &_avg_pr,
                                  double &_base_volume,
                                  double &_quote_volume
                                  );
   int               CheckLoadHistory(ENUM_TIMEFRAMES period,datetime start_date);
  };
//+------------------------------------------------------------------+


Vamos a hablar brevemente sobre los datos-miembros de clase.

En primer lugar, se trata de un símbolo único. Es decir, la posición de cobertura puede incluir cualquier posición de un símbolo. El responsable del símbolo es el campo m_symbol, que representa el ejemplar de clase CSymbolInfo.

En segundo lugar, podemos utilizar el número mágico (m_magic) como filtro de las órdenes necesarias. De esta forma, podemos crear una posición atendida por un asesor comercial concreto. Así, podremos crear varios "hedges" en un mismo símbolo.

También existe una matriz dinámica para registrar órdenes (m_tickets). En esta entrarán los tickets de las órdenes de la posición de cobertura.

Las funciones para obtener las propiedades de una cierta posición elegida (órdenes de mercado, en los términos de MetaTrader 4) están implementadas en el ejemplar de la clase CPositionInfo (m_pos_info).

El resto de propiedades de cobertura son necesarias para valorar su estado:

  • tipo (m_type);
  • volumen (m_volume);
  • precio de apertura (m_price);
  • precio del stop-loss (m_stop_loss);
  • precio del take-profit (m_take_profit).

Debemos destacar que hemos tomado como base para la construcción de la clase la lógica de la clase CPositionInfo. Y esto es completamente natural. Por eso, en la nueva clase hay métodos que retornan propiedades enteras, propiedades dobles, etc. Y, por supuesto, también hay métodos que serán específicos.


2.1 Método de inicialización

Antes de usar las posibilidades de la clase, debemos inicializar el ejemplar correspondiente. El método comprueba, primero, si el asesor funciona precisamente dentro del sistema de cobertura; y segundo, si ha sido elegido el símbolo, además del posible establecimiento del número mágico.

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
bool CHedgePositionInfo::Init(const string _symbol,const ulong _magic=0)
  {
//--- account margin mode
   ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(margin_mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      Print(__FUNCTION__+": no retail hedging!");
      return false;
     }
   if(!m_symbol.Name(_symbol))
     {
      Print(__FUNCTION__+": a symbol not selected!");
      return false;
     }
   ENUM_SYMBOL_CALC_MODE  symbol_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(_symbol,SYMBOL_TRADE_CALC_MODE);
   if(symbol_calc_mode!=SYMBOL_CALC_MODE_FOREX)
     {
      Print(__FUNCTION__+": only for Forex mode!");
      return false;
     }
   m_magic=_magic;
//---
   return true;
  }
//+------------------------------------------------------------------+

Este método es obligatorio para el posterior uso de las posibilidades de la clase de cobertura. Debemos notar que en el método se comprueba el modo de cálculo del margen. Si este no se corresponde con la cobertura, el método retornará false. Asimismo, se comprueba el método de cálculo del precio del contrato. Solo trabajaremos con contratos de fórex.


2.2 Propiedades enteras

El acceso a las propiedades enteras se realiza con los métodos:

  1. datetime                   Time(void);
  2. ulong                        TimeMsc(void);
  3. datetime                   TimeUpdate(void);
  4. ulong                        TimeUpdateMsc(void);
  5. ENUM_HEDGE_TYPE   HedgeType(void).

Vamos a ver, por ejemplo, el código del método CHedgePositionInfo::Time():

//+------------------------------------------------------------------+
//| Get  the hedge open time                                         |
//+------------------------------------------------------------------+
datetime CHedgePositionInfo::Time(void)
  {
   datetime hedge_time=WRONG_VALUE;
   int hedge_pos_num=m_tickets.Total();
//--- if any positions
   if(hedge_pos_num>0)
     {
      //--- find the first opened position
      for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++)
        {
         ulong curr_pos_ticket=m_tickets.At(pos_idx);
         if(curr_pos_ticket<LONG_MAX)
            if(m_pos_info.SelectByTicket(curr_pos_ticket))
              {
               datetime curr_pos_time=m_pos_info.Time();
               if(curr_pos_time>0)
                 {
                  if(hedge_time==0)
                     hedge_time=curr_pos_time;
                  else
                    {
                     if(curr_pos_time<hedge_time)
                        hedge_time=curr_pos_time;
                    }
                 }
              }
        }
     }
//---
   return hedge_time;
  }
//+------------------------------------------------------------------+

Para obtener la hora de apertura de la barra, y en esencia la primera posición de cobertura, necesitamos iterar todas sus posiciones y encontrar la más antigua.

Pero, para obtener la hora de cambio de cobertura, y en esencia la posición que ha sido modificada en último lugar, debemos cambiar ligeramente el método anterior:

//+------------------------------------------------------------------+
//| Get  the hedge update time                                       |
//+------------------------------------------------------------------+
datetime CHedgePositionInfo::TimeUpdate(void)
  {
   datetime hedge_time_update=0;
   int hedge_pos_num=m_tickets.Total();
//--- if any positions
   if(hedge_pos_num>0)
     {
      //--- find the first opened position
      for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++)
        {
         ulong curr_pos_ticket=m_tickets.At(pos_idx);
         if(curr_pos_ticket<LONG_MAX)
            if(m_pos_info.SelectByTicket(curr_pos_ticket))
              {
               //--- get the current position update time
               datetime curr_pos_time_update=m_pos_info.TimeUpdate();
               if(curr_pos_time_update>0)
                  if(curr_pos_time_update>hedge_time_update)
                     hedge_time_update=curr_pos_time_update;
              }
        }
     }
//---
   return hedge_time_update;
  }
//+------------------------------------------------------------------+

El método de definición del tipo de cobertura se ha codificado de la forma siguiente:

//+------------------------------------------------------------------+
//| Get  the hedge type                                              |
//+------------------------------------------------------------------+
ENUM_HEDGE_TYPE CHedgePositionInfo::HedgeType(void)
  {
   ENUM_HEDGE_TYPE curr_hedge_type=WRONG_VALUE;
   int hedge_pos_num=m_tickets.Total();
//--- if any positions
   if(hedge_pos_num>0)
     {
      //--- get the volumes      
      double total_vol,buy_volume,sell_volume;
      buy_volume=sell_volume=0.;
      total_vol=this.Volume(buy_volume,sell_volume);
      //--- define a hedge type
      if(buy_volume>0. && sell_volume>0.)
        {
         if(buy_volume>sell_volume)
            curr_hedge_type=HEDGE_NETTING_BUY;
         else if(buy_volume<sell_volume)
            curr_hedge_type=HEDGE_NETTING_SELL;
         else
            curr_hedge_type=HEDGE_LOCKED;
        }
      else if(buy_volume>0. && sell_volume==0.)
         curr_hedge_type=HEDGE_BUY;
      else if(buy_volume==0. && sell_volume>0.)
         curr_hedge_type=HEDGE_SELL;
     }
//---
   return curr_hedge_type;
  };
//+------------------------------------------------------------------+

El tipo de cobertura depende de la diferencia entre los volúmenes de las compras y las ventas. Primero comprobamos si realmente la cobertura es tal en sentido estricto. Si los volúmenes de las compras y las ventas son iguales, entonces la cobertura es completa. Si existe desigualdad, entonces nos encontraremos ante una cobertura parcial.

A continuación, comprobamos si la cobertura está representada solo por compras o solo por ventas.


2.3 Propiedades dobles

El acceso a las propiedades dobles se realiza con los métodos:

  1.    double            Volume(double &_buy_volume,double &_sell_volume);
  2.    double            PriceOpen(void);
  3.    double            StopLoss(void);
  4.    double            TakeProfit(void);
  5.    double            PriceCurrent(void);
  6.    double            Commission(void);
  7.    double            Swap(void);
  8.    double            Profit(void);
  9.    double            Margin(void).

Se ve fácilmente que el método de determinación del volumen de cobertura tiene parámetros en forma de enlaces. Esta implementación permite obtener al mismo tiempo tanto la magnitud del volumen de la propia cobertura, como sus componentes (compras y ventas).

//+------------------------------------------------------------------+
//| Get  the hedge volume                                            |
//+------------------------------------------------------------------+
double CHedgePositionInfo::Volume(double &_buy_volume,double &_sell_volume)
  {
   double total_vol=0.;
   int hedge_pos_num=m_tickets.Total();
//--- if any positions
   if(hedge_pos_num>0)
     {
      _buy_volume=_sell_volume=0.;
      //--- get the buy\sell volumes      
      for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++)
        {
         ulong curr_pos_ticket=m_tickets.At(pos_idx);
         if(curr_pos_ticket<LONG_MAX)
            if(m_pos_info.SelectByTicket(curr_pos_ticket))
              {
               ENUM_POSITION_TYPE curr_pos_type=m_pos_info.PositionType();
               double curr_pos_vol=m_pos_info.Volume();
               if(curr_pos_vol>0.)
                 {
                  //--- for a buy position
                  if(curr_pos_type==POSITION_TYPE_BUY)
                     _buy_volume+=curr_pos_vol;
                  //--- else for a sell position
                  else if(curr_pos_type==POSITION_TYPE_SELL)
                     _sell_volume+=curr_pos_vol;
                 }
              }
        }
      total_vol=_buy_volume-_sell_volume;
     }
//---
   return total_vol;
  }
//+------------------------------------------------------------------+

Para trabajar con las propiedades de precio, se ha creado el método auxiliar CHedgePositionInfo::AveragePrice(). Vamos a mostrar el bloque de código donde esta calcula el precio medio de cobertura dependiendo del tipo de nivel de precio:

//--- if the hedge volumes calculated
if(hedge_base_volume!=0. && hedge_quote_volume!=0.)
  {
   _avg_pr=fabs(hedge_quote_volume/hedge_base_volume);
   _base_volume=hedge_base_volume;
   _quote_volume=hedge_quote_volume;
   return true;
  }

En este caso, se usa un enfoque basado en el precio, la relación del coste total de la cobertura en la divisa de la cotización con respecto a la cobertura en la divisa básica.

Más tarde analizaremos el cálculo concreto, cuando trabajemos con un ejemplo.

Los métodos de obtención del swap y el beneficio suman las lecturas correspondientes de cada posición de la cobertura, y después retornan el valor final. El método de obtención de la comisión analiza las transacciones que han participado en la apertura de la posición. En este caso, además, podemos elegir con qué parámetro calcular el tamaño de la comisión. Si queremos obtenerlo solo para las transacciones de entrada, dejaremos el parámetro por defecto. Y si tenemos que calcular la comisión por la entrada y la salida, el parámetro debe ser igual a true. Debemos destacar que el último método conlleva un caráter aproximativo por varios motivos. En primer lugar, podemos cerrar las posiciones elegidas con posiciones opuestas. Entonces aparecerá una transacción del tipo DEAL_ENTRY_OUT_BY. Y por estas no se cobra comisión. En segundo lugar, si la divisa de la cuenta no coincide con la divisa básica, al cambiar los cursos, el coste de la entrada y la salida puede diferenciarse.

//+------------------------------------------------------------------+
//| Get  the hedge commission                                        |
//+------------------------------------------------------------------+
double CHedgePositionInfo::Commission(const bool _full=false)
  {
   double hedge_commission=0.;
   int hedge_pos_num=m_tickets.Total();
//--- if any positions
   if(hedge_pos_num>0)
      for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++)
        {
         ulong curr_pos_ticket=m_tickets.At(pos_idx);
         if(curr_pos_ticket<LONG_MAX)
            if(m_pos_info.SelectByTicket(curr_pos_ticket))
              {
               long curr_pos_id=m_pos_info.Identifier();
               if(curr_pos_id>0)
                  //--- retrieve the history of deals associated with the selected position 
                  if(HistorySelectByPosition(curr_pos_id))
                    {
                     CDealInfo curr_deal;
                     int deals_num=HistoryDealsTotal();
                     for(int deal_idx=0;deal_idx<deals_num;deal_idx++)
                        if(curr_deal.SelectByIndex(deal_idx))
                          {
                           ENUM_DEAL_ENTRY curr_deal_entry=curr_deal.Entry();
                           if(curr_deal_entry==DEAL_ENTRY_IN)
                             {
                              double curr_deal_commission=NormalizeDouble(curr_deal.Commission(),2);
                              if(curr_deal_commission!=0.)
                                {
                                 double fac=1.;
                                 if(_full) fac=2.;
                                 hedge_commission+=(fac*curr_deal_commission);
                                }
                             }
                          }

                    }
              }
        }
//---
   return hedge_commission;
  }
//+------------------------------------------------------------------+

Notemos también que en la clase existe el método CHedgePositionInfo::Margin(), que permite determinar la magnitud del margen para una posición de cobertura. En realidad, este método ha resultado el más complicado a la hora de programar. Podríamos dedicar un artículo completo a cómo determinar correctamente el tamaño del margen para las posiciones abiertas y las órdenes pendientes.


2.3.1 Margen para una posición de cobertura

Como indica el desarrollador, en presencia de posiciones bidireccionales, hay 2 métodos de cálculo del margen, determinados por el bróker. El primero toma el cálculo básico, y el segundo se implementa en el lado mayor.

Debemos reconocer que no nos hemos encontrado aún con el segundo método de cálculo. No obstante, también lo programaremos. Pero antes, será mejor que echemos un vistazo al primer método, cuyo algoritmo es más complejo e incluye el cálculo del margen:

  1. Para el volumen no cubierto;
  2. Para el volumen cubierto (si se indica el tamaño del margen cubierto);
  3. Para las órdenes pendientes.

El margen en este artículo se calcula para el modelo Retail Forex, Futures. El cálculo del margen para las órdenes pendientes no lo vamos a analizar.

Para calcular de forma completa el margen de la posición cubierta, necesitaremos la información de los siguientes parámetros:

  1. Divisa del depósito. Normalmente, las cuentas se denominan en dólares americanos (USD), euros (EUR), libras (GBP), francos (CHF).
  2. Divisa del margen. Normalmente, es la divisa básica del símbolo. Por ejemplo, para la pareja EURUSD, será el euro (EUR), y para AUDNZD, el dólar australiano (AUD).
  3. Tamaño del apalancamiento.

Además, destacaremos que en la línea de balance, en la pestaña «Comercio» del terminal, el valor del margen se indicará en la divisa del depósito. Por eso, el resultado del cálculo deberá ser el valor del margen precisamente en la divisa del depósito.

Y puesto que la divisa del margen podría no coincidir con la divisa del depósito, nos encontramos con diferentes variantes del algoritmo de cálculo:

  1. Cuando la divisa del depósito se encuentra en el símbolo de la posición de cobertura en forma de divisa básica. Supongamos que usted comercia con USDCHF en una cuenta de dólares.
  2. Cuando la divisa del depósito está presente en el símbolo de la posición de cobertura en forma de divisa cotizada. Supongamos que usted comercia con EUR USD en una cuenta de dólares.
  3. Cuando la divisa del depósito no está presente en el símbolo de la posición de cobertura. Supongamos que usted comercia con AUDNZD en una cuenta de dólares.

La primera variante será la más sencilla para el cálculo, y la última, la más compleja. Vamos a entrar un poco en temas de contabilidad y analizar ejemplos para cada variante.

Primera variante

Supongamos que tenemos una cuenta de dólares (depósito), en la que hay abiertas 5 posiciones del símbolo USDCHF (Fig.1).



Fig.1 Posiciones de mercado de la pareja USDCHF


Parámetros básicos:

Divisa de la cuenta - USD.

Divisa del margen - USD.

Tamaño del apalancamiento - 1:100.

Tenemos 3 posiciones de compra. El volumen total es igual a 5,55 lotes, que en términos de valor suponen $555 000. Además, tenemos 2 posiciones de venta. El volumen total es igual a 7,5 lotes, que entérminos de valor suponen $750 000.

А) cálculo para el volumen no cubierto

El volumen no cubierto es igual a 1,95 lotes o $195 000. Pertenece a las ventas, ya que se ha comprado más de lo que se ha vendido. Aunque, en este caso, no es tan importante si se trata de compras o ventas, ya que no es necesario calcular el precio medio ponderado.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$195 000 / 100 = $1 950.

B) cálculo para el volumen cubierto

El volumen cubierto es de 5,55 lotes o $555 000.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$555 000 / 100 = $5 550.

El valor de margen resultante se calcula como la suma del margen para el volumen no cubierto y el margen para el volumen cubierto:

$1 950 + $5 550 = $7 500.

Precisamente este valor es el que vemos en nuestro terminal comercial en el indicador "Margen".

Segunda variante

Supongamos que tenemos una cuenta de dólares (depósito), en la que hay abiertas 5 posiciones del símbolo EURUSD (Fig.2).



Fig.2 Posiciones de mercado de la pareja EURUSD

Parámetros básicos:

  • Divisa de la cuenta - USD.
  • Divisa del margen - EUR.
  • Tamaño del apalancamiento - 1:300.

Tenemos 3 posiciones de compra. El volumen total de las posiciones es igual a 5,55 lotes, que en términos de valor suponen $555 000 o $645 617.20. El precio medio ponderado de las compras es igual a $1.163274.

Tenemos 2 posiciones de venta. El volumen total de las posiciones es igual a 7,50 lotes, que en términos de valor suponen €750 000 o $872 409. El precio medio ponderado de las ventas es igual a $1.163212.

Todas las posiciones se muestran en el Recuadro 2.

Type Volume Price Value, $
buy 1.75 1.16329 203 575.75
buy 2.55 1.16329 296 638.95
buy 1.25 1.16322 145 402.50
sell 3.00 1.16323 348 969.00
sell 4.50 1.16320 523 440.00
Total 13.05 1.1632385 1 518 026.20

Recuadro.2 Posiciones de mercado de la pareja EURUSD


En total hay 5 posiciones. El volumen total de las posiciones es igual a 13,05 lotes, que en términos de valor suponen €1 305 000 o $1 518 026.20. El precio medio ponderado de las posiciones es igual a $1.16324.

А) cálculo para el volumen no cubierto

El volumen no cubierto es igual a 1,95 lotes o €195 000. Pertenece a las ventas, ya que se ha comprado más de lo que se ha vendido. Por eso, para determinar el coste de este volumen vamos a tomar el precio medio ponderado de las ventas:

$1.163212 * €195 000 = $226 826.34.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$226 826.34 / 300 = $756.09.

B) cálculo para el volumen cubierto

El volumen cubierto es de 5,55 lotes o €555 000. Para determinar el coste de este volumen vamos a tomar el precio medio ponderado de todas las posiciones:

$1.1632385 * €555 000 = $645 597.35.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$645 597.35 / 300 = $2 151,99.

Entonces, en teoría, el margen total deberá ser:

$756.09 + $2 151.99 = $2 908.08.

Sin embargo, en el terminal vemos el valor $1 832.08.

El asunto es que para el volumen cubierto se tiene en cuenta el valor del parámetro "Margen de cobertura" de la especificación del instrumento. Si resulta menor que el tamaño del contrato, obtenemos un cierto tipo de multiplicador. En este caso, la especificación para el parámetro establecido indica el valor 50000. Entonces:

Coste para el volumen cubierto = $1.1632385 * €555 000 / (100 000 / 50 000) = $322 798,67.

Margen para el volumen cubierto = $322 798,67 / 300 = $1 076.00.

En suma: $756.09 + $1 076.00 = $1 832.08. Precisamente este valor se corresponde con el valor del terminal.

Tercera variante

Supongamos que tenemos una cuenta de dólares (depósito), en la que hay abiertas 5 posiciones del símbolo AUDNZD (Fig.3).



Fig.3 Posiciones de mercado de la pareja AUDNZD

Parámetros básicos:

  • Divisa de la cuenta - USD.
  • Divisa del margen - AUD.
  • Tamaño del apalancamiento - 1:300.

Tenemos 3 posiciones de compra. El volumen total de las posiciones es igual a 5,55 lotes, que en términos de valor suponen A$555 000 o $400 442.35. El precio medio ponderado de las compras es igual a $0.7215178.

Tenemos 2 posiciones de venta. El volumen total de las posiciones es igual a 7,50 lotes, que en términos de valor suponen A$750 000 o $541 035.00. El precio medio ponderado de las ventas es igual a $0.72138.

Todas las posiciones se muestran en el Recuadro 3.

Type Volume Price Value, $
buy 1.75 0.72152 126 266.00
buy 2.55 0.72152 183 987.60
buy 1.25 0.72151 90 188.75
sell 3.00 0.72144 216 432.00
sell 4.50 0.72134 324 603.00
Total 13.05 0.72144 941 477.35

Fig.3 Posiciones de mercado de la pareja AUDNZD


Se ve fácilmente que en la columna Price se muestran los precios de apertura de las posiciones no del propio símbolo AUDNZD, sino del símbolo AUDUSD. Esto se ha hecho así para valorar de inmediato el volumen en la divisa de la cuenta. En este caso, además, surge la necesidad de recurrir a la historia de ticks y cotizaciones de la pareja en la que se presenta la divisa de la cuenta y la divisa del margen. Por eso, los valores calculados pueden diferenciarse ligeramente de los reales.

En total hay 5 posiciones. El volumen total de las posiciones es igual a 13,05 lotes, que en términos de valor suponen A$1 305 000 o $941 477.35. El precio medio ponderado de las posiciones es igual a $0.72144.

А) cálculo para el volumen no cubierto

El volumen no cubierto es igual a 1,95 lotes o A$195 000. Pertenece a las ventas, ya que se ha comprado más de lo que se ha vendido. Por eso, para determinar el coste de este volumen vamos a tomar el precio medio ponderado de las ventas:

$0.72138 * A$195 000 = $140 669.10.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$140 669.10 / 300 = $468.90.

B) cálculo para el volumen cubierto

El volumen cubierto es de 5,55 lotes o A$555 000. Para determinar el coste de este volumen vamos a tomar el precio medio ponderado de todas las posiciones, teniendo en cuenta el valor del parámetro "Margen de cobertura":

$0.72144 * A$555 000 / (100 000 / 50 000) = $200 199.21.

El margen de esta suma se toma teniendo en cuenta el apalancamiento:

$200 199.21/ 300 = $667.33.

En suma: $468.90 + $667.33 = $1 136.23. Comprobamos de acuerdo con la Fig.3: coincide con el terminal.


2.4 Otras propiedades

En la clase hay otros métodos que trabajan con el estado de la cobertura: StoreState() y CheckState(). Al igual que sucede con la posición normal, el estado se determina con los valores de tipo, volumen, precio de apertura, precio de stop-loss y take-profit.

El único método de propiedad de texto, TypeDescription(), retorna el tipo de cobertura en forma de línea.

Merece la pena destacar en especial el método de selección de posición de cobertura Select(). El código del método se muestra más abajo:

//+------------------------------------------------------------------+
//| Selects hedge positions                                          |
//+------------------------------------------------------------------+
bool CHedgePositionInfo::Select(void)
  {
   string hedge_symbol=m_symbol.Name();
//--- clear all positions 
   m_tickets.Shutdown();
//--- collect positions
   int pos_num=PositionsTotal();
   for(int pos_idx=0;pos_idx<pos_num;pos_idx++)
      if(m_pos_info.SelectByIndex(pos_idx))
        {
         string curr_pos_symbol=m_pos_info.Symbol();
         //--- select by symbol
         if(!StringCompare(hedge_symbol,curr_pos_symbol))
           {
            //--- if to select by magic
            bool is_the_same_magic=true;
            if(m_magic>0)
              {
               long curr_pos_magic=m_pos_info.Magic();
               if(m_magic!=curr_pos_magic)
                  is_the_same_magic=false;
              }
            if(is_the_same_magic)
              {
               ulong curr_pos_ticket=m_pos_info.Ticket();
               if(curr_pos_ticket>0)
                  if(!m_tickets.Add(curr_pos_ticket))
                    {
                     PrintFormat(__FUNCTION__+": failed to add #%d ticket!",curr_pos_ticket);
                     return false;
                    }
              }
           }
        }
//---
   return m_tickets.Total()>0;
  }
//+------------------------------------------------------------------+

La tarea principal del método es actualizar los tickets de las posiciones que forman parte de la cobertura. En la composición entran las posiciones de mercado, si su símbolo coincide con el símbolo de la propia cobertura. Asimismo, podemos añadir un filtro de selección, como un número mágico.


3. Ejemplos

Bien, vamos a crear una clase que funcione con propiedades de posición de cobertura. En este apartado, proponemos al lector trabajar con ejemplos prácticos. Vamos a comenzar con un script sencillo.


3.1 Script de prueba

Por motivos ilustrativos, hemos creado el script Test_hedge_properties.mq5, que muestra en el diario de registro información sobre las propiedades de cobertura, en la pestaña "Expertos".

En la primera variante de cálculo del margen, teníamos 5 posiciones del símbolo USDCHF (Fig.1). Iniciamos el script y obtenemos en el diario de registro la siguiente información:

2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       ---== Hedge properties==---
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Symbol: USDCHF
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Positions total = 5
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)               1) #293972991 buy 1.75 USDCHF 0.97160000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)               2) #293974150 buy 2.55 USDCHF 0.97142000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)               3) #293974889 sell 3.00 USDCHF 0.97157000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)               4) #293975329 sell 4.50 USDCHF 0.97164000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)               5) #293976289 buy 1.25 USDCHF 0.97205000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Magic: 0
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Time: 2018.08.29 17:15:44
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Time in msc: 1535562944628
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Update time: 2018.08.29 17:20:35
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Update time in msc: 1535563235034
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Type: HEDGE_NETTING_SELL
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Type description: hedge netting sell
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Volume: -1.95
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Buy volume: 5.55
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Sell volume: 7.50
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Open price: 0.97159
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Sl-price: -1.00000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Tp-price: -1.00000
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Current price: 0.96956
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Commission: 0.00
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Swap: -35.79
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Profit: 409.77
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1)       Margin: 7500.00

En la segunda variante, hemos procesado las propiedades de las posiciones de EURUSD (Fig.2). Después de iniciar el script, obtenemos la siguiente información:

2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       ---== Hedge properties==---
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Symbol: EURUSD
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Positions total = 5
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)               1) #119213986 buy 1.75 EURUSD 1.16329000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)               2) #119214003 buy 2.55 EURUSD 1.16329000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)               3) #119214004 buy 1.25 EURUSD 1.16322000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)               4) #119214011 sell 3.00 EURUSD 1.16323000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)               5) #119214021 sell 4.50 EURUSD 1.16320000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Magic: 0
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Time: 2018.08.31 16:38:10
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Time in msc: 1535733490531
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Update time: 2018.08.31 16:38:49
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Update time in msc: 1535733529678
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Type: HEDGE_NETTING_SELL
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Type description: hedge netting sell
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Volume: -1.95
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Buy volume: 5.55
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Sell volume: 7.50
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Open price: 1.16303
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Sl-price: -1.00000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Tp-price: -1.00000
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Current price: 1.16198
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Commission: 0.00
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Swap: -37.20
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Profit: 206.60
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1)       Margin: 1832.08

En la tercera variante, se ha trabajado con las posiciones del símbolo AUDNZD (Fig.3). El script ha mostrado en el diario de registro esta información:

2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       ---== Hedge properties==---
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Symbol: AUDNZD
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Positions total = 5
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)               1) #119214062 buy 1.75 AUDNZD 1.08781000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)               2) #119214068 buy 2.55 AUDNZD 1.08783000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)               3) #119214071 buy 1.25 AUDNZD 1.08785000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)               4) #119214083 sell 3.00 AUDNZD 1.08773000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)               5) #119214092 sell 4.50 AUDNZD 1.08757000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Magic: 0
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Time: 2018.08.31 16:39:41
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Time in msc: 1535733581113
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Update time: 2018.08.31 16:40:07
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Update time in msc: 1535733607241
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Type: HEDGE_NETTING_SELL
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Type description: hedge netting sell
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Volume: -1.95
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Buy volume: 5.55
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Sell volume: 7.50
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Open price: 1.08708
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Sl-price: -1.00000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Tp-price: -1.00000
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Current price: 1.09314
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Commission: 0.00
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Swap: -21.06
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Profit: -779.45
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1)       Margin: 1136.23

El código del script Test_hedge_properties.mq5 se puede descargar del archivo adjunto.


3.1 Panel de propiedades de cobertura

Ahora vamos a complicar la tarea. Con la ayuda de la Biblioteca Estándar, escribiremos el asesor HedgePropertiesEA.mq5, que muestra en el gráfico un panel que representa las propiedades de la posición de cobertura seleccionada.

Con este objetivo, crearemos la clase CHedgeDialog, que será descendiente de la clase estándar CAppDialog. Con la ayuda de esta última, ya no será necesario programar tareas típicas, por ejemplo: minimizar y restaurar la ventana del panel, procesar los cambios en los elementos del panel, etc.

//+------------------------------------------------------------------+
//| Class CHedgeDialog                                               |
//| Purpose: Class for displaying a hedge position info.             |  
//|              Derives from class CAppDialog.                      |
//+------------------------------------------------------------------+
class CHedgeDialog : private CAppDialog
  {
   //--- === Data members === --- 
private:
   CArrayString      m_symbols_arr;
   //--- controls
   CLabel            m_labels[FIELDS_NUM+1];
   CEdit             m_edits[FIELDS_NUM];
   CComboBox         m_combo;
   bool              m_to_refresh;
   //--- === Methods === --- 
public:
   //--- constructor/destructor
   void              CHedgeDialog(void) {};
   void             ~CHedgeDialog(void) {};
   //--- initialization
   bool              Init(void);
   void              Deinit(const int _reason);
   //--- processing
   void              OnChartEvent(const int _id,
                                  const long &_lparam,
                                  const double &_dparam,
                                  const string &_sparam);
   void              OnTradeEvent(void);
   //---
private:
   int               HedgeSymbols(void);
   void              RefreshPanel(void);
  };
//+------------------------------------------------------------------+

Un ejemplar de la clase en el código del asesor llamará y procesará los eventos de inicialización y desinicialización, los eventos del gráfico y los eventos relacionados con las transacciones comerciales.

El panel de propiedades de la posición de cobertura se representa en la Fig.4.

Panel de propiedades de la posición de cobertura

Fig.4 Panel de propiedades de la posición de cobertura

Uno de los principales métodos es CHedgeDialog::RefreshPanel(). Es, por así de decirlo, nuestra "mula de carga". De ser necesario, actualiza los campos informativos del panel. Para nosotros ha sido un tanto complicado codificar y poner a prueba la situación que implica el cambio del número de coberturas. En este caso, debemos cambiar los símbolos únicos en la lista desplegable y no entrar en un ciclo infinito de llamadas al manejador OnChartEvent(). Para ello, hemos usado un límite de llamadas consecutivas al manejador, con una duración de 1 seg.

//--- check the limit for refreshing
if(!m_to_refresh)
  {
   uint last_cnt=GetTickCount();
   static uint prev_cnt=0;
   uint msc_elapsed=last_cnt-prev_cnt;
   prev_cnt=last_cnt;
   if(msc_elapsed>1000)
      m_to_refresh=true;
   else
      return;
  }

El código completo del asesor HedgePropertiesEA.mq5 se muestra en el archivo adjunto.


Conclusión

El terminal comercial MetaTrader 5 en la presente etapa no solo es una plataforma multimercado: también permite utilizar distintos sistemas de registro de posiciones. Estas posibilidades amplian considerablemente el instrumental para la implementación y formalización de las ideas comerciales.

Esperamos que el presente artículo despierte el interés de aquellos que deseen trasladar sus estrategias de MetaTrader 4 a MetaTrader 5.


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

Archivos adjuntos |
Hedge.zip (15.37 KB)
Reversión: ¿es el Santo Grial o una peligrosa equivocación? Reversión: ¿es el Santo Grial o una peligrosa equivocación?
En el presente artículo intentaremos aclarar lo siguiente: ¿qué es una reversión, si merece la pena usarla y si podemos mejorar nuestra estrategia comercial a través de ella? Vamos a crear un Asesor Experto, y veremos en los datos históricos qué indicadores convienen mejor para la reversión, además, si podemos usarla sin indicadores como un sistema comercial independiente. Veremos si es posible convertir un sistema comercial no rentable en un sistema rentable a través de la reversión.
Modelo de continuación de movimiento - búsqueda en el gráfico y estadísticas de ejecución Modelo de continuación de movimiento - búsqueda en el gráfico y estadísticas de ejecución
En este artículo vamos a describir la definición programática de uno de los modelos de continuación del movimiento. La base del trabajo viene constituida por dos ondas: la principal y la de corrección. Como extremos se usarán fractales, además de los llamados fractales potenciales, los extremos que no se han formado aún como fractales.
Implementación de Take Profit en forma de órdenes limitadas sin cambiar el código fuente del EA Implementación de Take Profit en forma de órdenes limitadas sin cambiar el código fuente del EA
Desde hace mucho en el foro se discute la cuestión del uso de órdenes limitadas en vez de colocar el Take Profit estándar para la posición. ¿En qué consiste la ventaja de este enfoque y cómo se puede implementarlo en la negociación? En este artículo me gustaría proponerles mi propia visión de las respuestas a estas preguntas.
Usando indicadores para la optimización RealTime de EAs Usando indicadores para la optimización RealTime de EAs
No es ningún secreto que el éxito del funcionamiento de cualquier robot comercial depende de la correcta elección de sus parámetros (su optimización). Pero los parámetros óptimos para un intervalo temporal no siempre resultan los mejores en otro intervalo de la historia. Con frecuencia, asesores que son rentables en la simulación, dan pérdidas en tiempo real. Aquí nos surje la pregunta concerniente a la necesidad de optimizar continuamente. Allá donde aparece mucho trabajo rutinario, el hombre busca la forma de automatizarlo. En este artículo proponemos nuestro enfoque particular para solucionar esta tarea.