Requisitos de margen

Una de las informaciones más importantes sobre un instrumento financiero para un operador es la cantidad de fondos necesarios para abrir una posición. Sin saber cuánto dinero se necesita para comprar o vender un número determinado de lotes, es imposible implementar un sistema de gestión de dinero dentro de un Asesor Experto y controlar el saldo de la cuenta.

Dado que MetaTrader 5 se utiliza para operar diversos instrumentos (divisas, materias primas, acciones, bonos, opciones y futuros), los principios de cálculo del margen difieren significativamente. La documentación proporciona detalles, en particular para el mercado de divisas y futuros, así como mercados bursátiles.

Varias propiedades de la API de MQL5 permiten definir el tipo de mercado y el método de cálculo del margen para un instrumento concreto.

Supongamos que, para una determinada combinación de parámetros, como el tipo de operación de trading, el instrumento, el volumen y el precio, MQL5 le permite calcular el margen utilizando la función OrderCalcMargin. Este es el método más sencillo, pero tiene una limitación importante: la función no tiene en cuenta las posiciones abiertas actuales ni las órdenes pendientes. Esto, en particular, ignora los posibles ajustes por solapamiento de volúmenes cuando se permiten posiciones opuestas en la cuenta.

Así, para obtener un desglose de los fondos de la cuenta utilizados actualmente como margen para las órdenes y posiciones abiertas, un programa MQL puede necesitar analizar las siguientes propiedades y cálculos mediante fórmulas. Además, está prohibido utilizar la función OrderCalcMargin en los indicadores. Puede estimar el margen libre por adelantado después de que se complete la transacción propuesta utilizando OrderCheck.

Identificador

Descripción

SYMBOL_TRADE_CALC_MODE

El método para calcular el margen y el beneficio (véase ENUM_SYMBOL_CALC_MODE)

SYMBOL_MARGIN_HEDGED_USE_LEG

Bandera booleana para activar (true) o desactivar (false) el modo de cálculo del margen de cobertura para la mayor de las posiciones solapadas (compra y venta).

SYMBOL_MARGIN_INITIAL

Margen inicial de un instrumento bursátil

SYMBOL_MARGIN_MAINTENANCE

Margen de mantenimiento de un instrumento bursátil

SYMBOL_MARGIN_HEDGED

Tamaño del contrato o margen para un lote de posiciones cubiertas (posiciones opuestas para un símbolo)

Las dos primeras propiedades están incluidas en la enumeración ENUM_SYMBOL_INFO_INTEGER, y las tres últimas en ENUM_SYMBOL_INFO_DOUBLE, y pueden ser leídas, respectivamente, por las funciones SymbolInfoInteger y SymbolInfoDouble.

Las fórmulas específicas de cálculo de márgenes dependen de la propiedad SYMBOL_TRADE_CALC_MODE y se muestran en la tabla que aparece más abajo. Encontrará información más completa en Documentación MQL5.

Tenga en cuenta que los márgenes inicial y de mantenimiento no se utilizan para los instrumentos Forex, y estas propiedades son siempre 0 para ellos.

El margen inicial indica el importe del depósito de garantía exigido en la divisa de margen para abrir una posición con un volumen de un lote. Se utiliza cuando se comprueba la suficiencia de los fondos del cliente antes de entrar en el mercado. Para obtener el importe final del margen cobrado en función del tipo y la dirección de la orden, compruebe los coeficientes de margen utilizando la función SymbolInfoMarginRate. Así, el bróker puede establecer un apalancamiento o descuento individual para cada instrumento.

El margen de mantenimiento indica el valor mínimo de fondos en la divisa del margen del instrumento para mantener una posición abierta de un lote. Se utiliza al comprobar la suficiencia de fondos del cliente cuando cambia el estado de la cuenta (condiciones de trading). Si el nivel de fondos cae por debajo del importe del margen de mantenimiento de todas las posiciones, el bróker comenzará a cerrarlas a la fuerza.

Si la propiedad de margen de mantenimiento es 0, se utiliza el margen inicial. Al igual que en el caso del margen inicial, para obtener el importe final del margen cobrado en función del tipo y la dirección, deberá comprobar los coeficientes de margen utilizando la función SymbolInfoMarginRate.

Las posiciones de cobertura, es decir, las posiciones multidireccionales para un mismo símbolo, sólo pueden existir en cuentas de cobertura en trading. Obviamente, el cálculo del margen de cobertura junto con las propiedades SYMBOL_MARGIN_HEDGED_USE_LEG, SYMBOL_MARGIN_HEDGED sólo tiene sentido en dichas cuentas. El margen de cobertura se aplica al volumen cubierto.

El bróker puede elegir para cada instrumento uno de los dos métodos existentes para calcular el margen de las posiciones cubiertas:

  • El cálculo base se aplica cuando el modo de cálculo del lado más largo está desactivado, es decir, la propiedad SYMBOL_MARGIN_HEDGED_USE_LEG es igual a false. En este caso, el margen consta de tres componentes: el margen para el volumen descubierto de la posición existente, el margen para el volumen cubierto (si hay posiciones opuestas y la propiedad SYMBOL_MARGIN_HEDGED es distinta de cero), el margen para órdenes pendientes. Si se establece el margen inicial para el instrumento (la propiedad SYMBOL_MARGIN_INITIAL es distinta de cero), entonces el margen de cobertura se especifica como un valor absoluto (en dinero). Si no se fija el margen inicial (igual a 0), entonces SYMBOL_MARGIN_HEDGED especifica el tamaño del contrato que se utilizará al calcular el margen según la fórmula correspondiente al tipo de instrumento de trading (SYMBOL_TRADE_CALC_MODE).
  • El cálculo de la posición más alta se aplica cuando la propiedad SYMBOL_MARGIN_HEDGED_USE_LEG es igual a true. El valor de SYMBOL_MARGIN_HEDGED se ignora en este caso. En lugar de ello se calcula el volumen de todas las posiciones cortas y largas en el instrumento, y se calcula el precio medio ponderado de apertura para cada lado. Además, utilizando las fórmulas correspondientes al tipo de instrumento (SYMBOL_TRADE_CALC_MODE), se calcula el margen para el lado corto y el lado largo. El valor mayor se utiliza como valor final.

En la siguiente tabla se enumeran los elementos ENUM_SYMBOL_CALC_MODE y sus respectivos métodos de cálculo de márgenes. La misma propiedad (SYMBOL_TRADE_CALC_MODE) es también responsable de calcular el beneficio/pérdida de una posición, pero consideraremos este aspecto más adelante, en el capítulo sobre las funciones de trading de MQL5.

Identificador

Fórmula

SYMBOL_CALC_MODE_FOREX

Forex

Lots * ContractSize * MarginRate / Leverage

SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE

Forex without leverage

Lots * ContractSize * MarginRate

SYMBOL_CALC_MODE_CFD

CFD

Lots * ContractSize * MarketPrice * MarginRate

SYMBOL_CALC_MODE_CFDLEVERAGE

CFD with leverage

Lots * ContractSize * MarketPrice * MarginRate / Leverage

SYMBOL_CALC_MODE_CFDINDEX

CFDs on indices

Lots * ContractSize * MarketPrice * TickPrice / TickSize * MarginRate

SYMBOL_CALC_MODE_EXCH_STOCKS

Securities on the stock exchange

Lots * ContractSize * LastPrice * MarginRate

SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX

Securities on MOEX

Lots * ContractSize * LastPrice * MarginRate

SYMBOL_CALC_MODE_FUTURES

Futures

Lots * InitialMargin * MarginRate

SYMBOL_CALC_MODE_EXCH_FUTURES

Futures on the stock exchange

Lots * InitialMargin * MarginRate or
Lots * MaintenanceMargin * MarginRate

SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS

Futures on FORTS

Lots * InitialMargin * MarginRate or
Lots * MaintenanceMargin * MarginRate

SYMBOL_CALC_MODE_EXCH_BONDS

Bonds on the stock exchange

Lots * ContractSize * FaceValue * OpenPrice / 100

SYMBOL_CALC_MODE_EXCH_BONDS_MOEX

Bonds on MOEX

Lots * ContractSize * FaceValue * OpenPrice / 100

SYMBOL_CALC_MODE_SERV_COLLATERAL

Activo no negociable (margen no aplicable)

En las fórmulas se utiliza la siguiente notación:

  • Lots: posición o volumen de la orden en lotes (acciones del contrato).
  • ContractSize: tamaño del contrato (un lote, SYMBOL_TRADE_CONTRACT_SIZE)
  • Leverage: apalancamiento de la cuenta de trading (ACCOUNT_LEVERAGE)
  • InitialMargin: margen inicial (SYMBOL_MARGIN_INITIAL)
  • MaintenanceMargin: margen de mantenimiento (SYMBOL_MARGIN_MAINTENANCE)
  • TickPrice: precio del tick (SYMBOL_TRADE_TICK_VALUE)
  • TickSize: tamaño del tick (SYMBOL_TRADE_TICK_SIZE)
  • MarketPrice: el último precio Bid/Ask conocido en función del tipo de transacción
  • LastPrice: el último precio Last conocido
  • OpenPrice: precio medio ponderado de apertura de una posición u orden
  • FaceValue: valor nominal del bono
  • MarginRate: coeficiente de margen según la función SymbolInfoMarginRate; puede tener también dos valores diferentes: para el margen inicial y para el margen de mantenimiento.

Encontrará una implementación alternativa de los cálculos de la fórmula para la mayoría de los tipos de símbolos en el archivo MarginProfitMeter.mqh (véase la sección Estimación del beneficio de una operación de trading) que también puede utilizarse en indicadores.

Formulemos un par de comentarios sobre algunos modos.

En la tabla anterior, sólo tres de las fórmulas de futuros utilizan el margen inicial (SYMBOL_MARGIN_INITIAL). Sin embargo, si esta propiedad tiene un valor distinto de cero en la especificación de cualquier otro símbolo, entonces determina el margen.

Algunas bolsas pueden imponer sus propias especificidades en el ajuste de márgenes, como el sistema de descuento para FORTS (SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS). Consulte la documentación de MQL5 y a su bróker para obtener más detalles.

En el modo SYMBOL_CALC_MODE_SERV_COLLATERAL, el valor de un instrumento se tiene en cuenta en los Activos, que se suman al Capital (Equity). Por lo tanto, las posiciones abiertas en dicho instrumento aumentan el importe del Margen Libre y sirven como garantía adicional para las posiciones abiertas en instrumentos negociados. El valor de mercado de una posición abierta se calcula en función del volumen, el tamaño del contrato, el precio actual de mercado y el coeficiente de liquidez: Lots * ContractSize * MarketPrice * LiquidityRate (este último valor puede obtenerse como la propiedad SYMBOL_TRADE_LIQUIDITY_RATE).

Como ejemplo de trabajo con propiedades relacionadas con los márgenes, vea el script SymbolFilterMarginStats.mq5. Su objetivo será calcular estadísticas sobre los métodos de cálculo de márgenes en la lista de símbolos seleccionados, así como, opcionalmente, registrar estas propiedades para cada símbolo. Seleccionaremos los símbolos para el análisis utilizando la clase de filtro ya conocida SymbolFilter y las condiciones para ello suministradas a partir de las variables de entrada.

#include <MQL5Book/SymbolFilter.mqh>
   
input bool UseMarketWatch = false;
input bool ShowPerSymbolDetails = false;
input bool ExcludeZeroInitMargin = false;
input bool ExcludeZeroMainMargin = false;
input bool ExcludeZeroHedgeMargin = false;

Por defecto, se solicita información para todos los símbolos disponibles. Para limitar el contexto únicamente a la visión general del mercado, debemos fijar UseMarketWatch en true.

El parámetro ShowPerSymbolDetails permite activar la salida de información detallada sobre cada símbolo (por defecto, el parámetro es false, y sólo se muestran estadísticas).

Los tres últimos parámetros sirven para filtrar los símbolos según las condiciones de los valores de margen cero (inicial, de mantenimiento y de cobertura, respectivamente).

Para recoger y mostrar convenientemente en el registro un conjunto completo de propiedades para cada símbolo (cuando ShowPerSymbolDetails está activada), se define en el código la estructura MarginSettings.

struct MarginSettings
{
   string name;
   ENUM_SYMBOL_CALC_MODE calcMode;
   bool hedgeLeg;
   double initial;
   double maintenance;
   double hedged;
};

Dado que algunas de las propiedades son de enteros (SYMBOL_TRADE_CALC_MODE, SYMBOL_MARGIN_HEDGED_USE_LEG), y algunas de reales (SYMBOL_MARGIN_INITIAL, SYMBOL_MARGIN_MAINTENANCE, SYMBOL_MARGIN_HEDGED), el objeto de filtro tendrá que solicitarlas por separado.

Ahora vamos directamente al código de trabajo en OnStart. Aquí, como de costumbre, definimos el objeto de filtro (f), los arrays de salida para los nombres de los caracteres (symbols) y los valores de las propiedades solicitadas (flags,values). Además de ellas, añadimos un array de estructuras MarginSettings.

void OnStart()
{
   SymbolFilter f;                // filter object
   string symbols[];              // array for names
   long flags[][2];               // array for integer vectors
   double values[][3];            // array for real vectors
   MarginSettings margins[];      // composite output array
   ...

Se ha introducido el mapa del array stats para calcular estadísticas con una clave como ENUM_SYMBOL_CALC_MODE y el valor entero int para el número de veces que se ha encontrado cada método. Además, todos los casos de margen cero y el modo de cálculo habilitado en el tramo más largo deben registrarse en las variables del contador correspondientes.

   MapArray<ENUM_SYMBOL_CALC_MODE,intstats// counters for each method/mode
   int hedgeLeg = 0;                          // and other counters
   int zeroInit = 0;                          // ...
   int zeroMaintenance = 0;
   int zeroHedged = 0;
   ...

A continuación, especificamos las propiedades que nos interesan relacionadas con el margen, que se leerán de la configuración del símbolo. Primero, los enteros del array ints y luego, los reales del array doubles.

   ENUM_SYMBOL_INFO_INTEGER ints[] =
   {
      SYMBOL_TRADE_CALC_MODE,
      SYMBOL_MARGIN_HEDGED_USE_LEG
   };
   
   ENUM_SYMBOL_INFO_DOUBLE doubles[] =
   {
      SYMBOL_MARGIN_INITIAL,
      SYMBOL_MARGIN_MAINTENANCE,
      SYMBOL_MARGIN_HEDGED
   };
   ...

En función de los parámetros de entrada, estableceremos las condiciones de filtrado.

   if(ExcludeZeroInitMarginf.let(SYMBOL_MARGIN_INITIAL0SymbolFilter::IS::GREATER);
   if(ExcludeZeroMainMarginf.let(SYMBOL_MARGIN_MAINTENANCE0SymbolFilter::IS::GREATER);
   if(ExcludeZeroHedgeMarginf.let(SYMBOL_MARGIN_HEDGED0SymbolFilter::IS::GREATER);
   ...

Ahora todo está listo para seleccionar símbolos por condiciones y obtener sus propiedades en arrays. Lo hacemos dos veces, por un lado para las propiedades de enteros y por otro, para las de reales.

   f.select(UseMarketWatchintssymbolsflags);
   const int n = ArraySize(symbols);
   ArrayResize(symbols0n);
   f.select(UseMarketWatchdoublessymbolsvalues);
   ...

Un array con símbolos debe ponerse a cero después de la primera aplicación del filtro para que los nombres no se dupliquen. A pesar de tratarse de dos consultas distintas, el orden de los elementos en todos los arrays de salida (ints y doubles) es el mismo, ya que las condiciones de filtrado no cambian.

Si el usuario activa un registro detallado, asignamos memoria para el array de estructuras margins.

   if(ShowPerSymbolDetailsArrayResize(marginsn);

Por último, calculamos las estadísticas iterando sobre todos los elementos de los arrays resultantes y, opcionalmente, rellenamos el array de estructuras.

   for(int i = 0i < n; ++i)
   {
      stats.inc((ENUM_SYMBOL_CALC_MODE)flags[i].value[0]);
      hedgeLeg += (int)flags[i].value[1];
      if(values[i].value[0] == 0zeroInit++;
      if(values[i].value[1] == 0zeroMaintenance++;
      if(values[i].value[2] == 0zeroHedged++;
      
      if(ShowPerSymbolDetails)
      {
         margins[i].name = symbols[i];
         margins[i].calcMode = (ENUM_SYMBOL_CALC_MODE)flags[i][0];
         margins[i].hedgeLeg = (bool)flags[i][1];
         margins[i].initial = values[i][0];
         margins[i].maintenance = values[i][1];
         margins[i].hedged = values[i][2];
      }
   }
   ...

Ahora mostramos las estadísticas en el registro.

   PrintFormat("===== Margin calculation modes for %s symbols %s=====",
      (UseMarketWatch ? "Market Watch" : "all available"),
      (ExcludeZeroInitMargin || ExcludeZeroMainMargin || ExcludeZeroHedgeMargin
         ? "(with conditions) " : ""));
   PrintFormat("Total symbols: %d"n);
   PrintFormat("Hedge leg used in: %d"hedgeLeg);
   PrintFormat("Zero margin counts: initial=%d, maintenance=%d, hedged=%d",
      zeroInitzeroMaintenancezeroHedged);
   
   Print("Stats per calculation mode:");
   stats.print();
   ...

Dado que los miembros de la enumeración ENUM_SYMBOL_CALC_MODE se muestran como números enteros (lo cual no es muy informativo), mostramos también un texto en el que cada valor tiene un nombre (de EnumToString).

   Print("Legend: key=calculation mode, value=count");
   for(int i = 0i < stats.getSize(); ++i)
   {
      PrintFormat("%d -> %s"stats.getKey(i), EnumToString(stats.getKey(i)));
   }
   ...

Si se necesita información detallada sobre los caracteres seleccionados, se emite el array de estructuras margins.

   if(ShowPerSymbolDetails)
   {
      Print("Settings per symbol:");
      ArrayPrint(margins);
   }
}

Vamos a ejecutar el script un par de veces con diferentes configuraciones. Empecemos con la configuración por defecto.

===== Margin calculation modes for all available symbols =====
Total symbols: 131
Hedge leg used in: 14
Zero margin counts: initial=123, maintenance=130, hedged=32
Stats per calculation mode:
    [key] [value]
[0]     0     101
[1]     4      16
[2]     1       1
[3]     2      11
[4]     5       2
Legend: key=calculation mode, value=count
0 -> SYMBOL_CALC_MODE_FOREX
4 -> SYMBOL_CALC_MODE_CFDLEVERAGE
1 -> SYMBOL_CALC_MODE_FUTURES
2 -> SYMBOL_CALC_MODE_CFD
5 -> SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE

Para la segunda ejecución, establezcamos ShowPerSymbolDetails y ExcludeZeroInitMargin en true. Así se solicita información detallada sobre todos los símbolos que tienen un valor distinto de cero del margen inicial.

===== Margin calculation modes for all available symbols (with conditions) =====
Total symbols: 8
Hedge leg used in: 0
Zero margin counts: initial=0, maintenance=7, hedged=0
Stats per calculation mode:
    [key] [value]
[0]     0       5
[1]     1       1
[2]     5       2
Legend: key=calculation mode, value=count
0 -> SYMBOL_CALC_MODE_FOREX
1 -> SYMBOL_CALC_MODE_FUTURES
5 -> SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE
Settings per symbol:
      [name] [calcMode] [hedgeLeg]    [initial] [maintenance]    [hedged]
[0] "XAUEUR"          0      false    100.00000       0.00000    50.00000
[1] "XAUAUD"          0      false    100.00000       0.00000   100.00000
[2] "XAGEUR"          0      false   1000.00000       0.00000  1000.00000
[3] "USDGEL"          0      false 100000.00000  100000.00000 50000.00000
[4] "SP500m"          1      false   6600.00000       0.00000  6600.00000
[5] "XBRUSD"          5      false    100.00000       0.00000    50.00000
[6] "XNGUSD"          0      false  10000.00000       0.00000 10000.00000
[7] "XTIUSD"          5      false    100.00000       0.00000    50.00000