保证金要求

对于交易者而言,关于金融工具最重要的信息之一是开仓所需的资金量。如果不知道买入或卖出给定数量的手数需要多少钱,就不可能在 EA 交易中实现资金管理系统并控制账户余额。

由于 MetaTrader 5 用于交易各种工具(货币、商品、股票、债券、期权和期货),因此保证金计算原则有很大不同。本文档提供了详细信息,特别是 外汇和期货以及 交易所

MQL5 API 的几个特性允许你定义市场类型和特定金融工具的保证金计算方法。

未来,假设对于给定的参数组合,如交易运算类型、工具、交易量和价格,MQL5 允许你使用 OrderCalcMargin 函数计算保证金。这是最简单的方法,但它有一个明显的局限性:该函数不考虑当前的持仓和挂单。特别是,当账户上允许相反头寸时,会忽略对重叠交易量的可能调整。

因此,为了获得当前用作持仓和订单保证金的账户资金明细,MQL 程序可能需要使用公式分析以下特性和计算。此外,禁止在指标中使用 OrderCalcMargin 函数。你可以使用 OrderCheck预先估算拟定交易完成后可用保证金。

标识符

说明

SYMBOL_TRADE_CALC_MODE

计算保证金和利润的方法(参见 ENUM_SYMBOL_CALC_MODE)

SYMBOL_MARGIN_HEDGED_USE_LEG

启用 (true) 或禁用 (false) 最大重叠头寸(买入和卖出)的对冲保证金计算模式的布尔标志

SYMBOL_MARGIN_INITIAL

交易所工具的初始保证金

SYMBOL_MARGIN_MAINTENANCE

交易所金融工具的维持保证金

SYMBOL_MARGIN_HEDGED

一手已覆盖的合约规模或保证金(一个交易品种的相反头寸)头寸

前两个特性包含在 ENUM_SYMBOL_INFO_INTEGER 枚举中,后三个特性包含 在ENUM_SYMBOL_INFO_DOUBLE 中,它们可以分别通过函数 SymbolInfoIntegerSymbolInfoDouble 读取。

具体的保证金计算公式取决于 SYMBOL_TRADE_CALC_MODE 特性,如下表所示。更完整的信息可以在 MQL5 文档中找到。

请注意,初始保证金和维持保证金不用于外汇金融工具,这些特性对它们而言始终为 0。

初始保证金表示必需的 保证金货币 存款金额,用于开立 一手交易量。初始保证金用于在入市前检查客户资金是否充足。要根据订单类型和方向获得收取的最终保证金金额,可使用 SymbolInfoMarginRate 函数计算保证金比率。因此,经纪商可以为每种金融工具设置单独的杠杆或折扣。

维持保证金表示金融工具保证金货币中维持一手持仓的最低资金值。用于在账户状态(交易条件)发生变化时检查客户资金是否充足。如果资金水平低于所有持仓的维持保证金金额,经纪商将开始强制平仓。

如果维持保证金特性为 0,则使用初始保证金。与初始保证金的情况一样,要获得根据类型和方向收取的最终保证金金额,应使用 SymbolInfoMarginRate 函数检查保证金费比率。

对冲头寸,即同一交易品种的多向头寸,只能存在于 对冲 交易账户。显然,对冲保证金的计算以及 SYMBOL_MARGIN_HEDGED_USE_LEG 和 SYMBOL_MARGIN_HEDGED 的特性只有在此类账户上才有意义。对冲保证金适用于覆盖的交易量。

经纪商可以为每种工具选择两种现有方法中的一种来计算覆盖头寸的保证金:

  • 当禁用最长边计算模式时,即 SYMBOL_MARGIN_HEDGED_USE_LEG 特性等于 false 时,应用基础计算。在这种情况下,保证金由三个部分组成:现有头寸未覆盖量的保证金,覆盖量的保证金(如果有相反头寸并且 SYMBOL _ MARGIN _ HEDGED 特性不为零)以及挂单的保证金。如果为金融工具设置了初始保证金(SYMBOL_MARGIN_INITIAL 特性不为零),则对冲保证金被指定为绝对值(以货币为单位)。如果初始保证金未设置(等于 0),则 SYMBOL_MARGIN_HEDGED 指定根据交易金融工具类型 (SYMBOL_TRADE_CALC_MODE) 对应的公式计算保证金时应使用的合约规模。
  • 当 SYMBOL_MARGIN_HEDGED_USE_LEG 特性等于 true 时,应用最高头寸计算。在这种情况下,SYMBOL_MARGIN_HEDGED 的值被忽略。取而代之的是,计算金融工具上所有短仓和长仓的交易量,并计算每一方的加权平均开盘价。此外,使用对应于金融工具类型 (SYMBOL_TRADE_CALC_MODE) 的公式,计算空头方和多头方的保证金。将最大值用作最终值。

下表列出了 ENUM_SYMBOL_CALC_MODE 元素及其各自的保证金计算方法。同样的特性 (SYMBOL_TRADE_CALC_MODE) 也负责计算头寸的盈亏,但是我们将在后面的 MQL5 交易函数一章中考虑这个方面。

标识符

公式

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

非交易资产(保证金不适用)

上述公式中使用了以下符号:

  • Lots - 手数中的头寸或订单量(合约份额)
  • ContractSize - 合约规模(一手, SYMBOL_TRADE_CONTRACT_SIZE
  • Leverage - 交易账户杠杆 (ACCOUNT_LEVERAGE
  • InitialMargin – 初始保证金 (SYMBOL_MARGIN_INITIAL)
  • MaintenanceMargin - 维持保证金 (SYMBOL _ MARGIN _ MAINTENANCE)
  • TickPrice - 分时报价价格 (SYMBOL_TRADE_TICK_VALUE
  • TickSize - 分时报价规模 (SYMBOL_TRADE_TICK_SIZE
  • MarketPrice – 最新已知的 买入/卖出 价格,取决于交易类型
  • LastPrice - 最后已知的 最新 价格
  • OpenPrice - 头寸或订单开盘价的加权平均价格
  • FaceValue - 债券的面值
  • MarginRate - 根据 SymbolInfoMarginRate 函数的保证金比率,也可以有 2 个不同的值:初始保证金和维持保证金

MarginProfitMeter.mqh 文档给出了公式计算大多数类型交易品种的另一种实现方式(参见 估算交易运算的利润率)。该实现方式也可以用在指标中。

让我们对一些模式做一些注释。

上表中,只有三种期货公式使用了初始保证金 (SYMBOL_MARGIN_INITIAL)。但是,如果该特性在任何其他交易品种的规范中具有非零值,则其可以确定保证金。

一些交易所可能会对保证金调整施加自己的具体要求,例如 FORTS (SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS) 的折扣系统。有关详细信息,请参见 MQL5 文档并咨询你的经纪商。

在 SYMBOL_CALC_MODE_SERV_COLLATERAL 模式中,金融工具的价值被计入资产,并被纳入净值中。因此,此类金融工具的持仓增加了可用保证金金额,并作为交易金融工具持仓的额外抵押。根据交易量、合约规模、当前市场价格和流动性比率计算持仓的市场价值:Lots * ContractSize * MarketPrice * LiquidityRate(后者的值可以作为 SYMBOL_TRADE_LIQUIDITY_RATE 特性获得)

作为使用保证金相关特性的示例,请考虑脚本 SymbolFilterMarginStats.mq5。其作用是计算所选交易品种列表中保证金计算方法的统计数据,并选择性地记录每个交易品种的这些特性。我们将使用已知的筛选类 SymbolFilter 以及通过输入变量为其提供的条件来选择用于分析的交易品种。

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

默认情况下,会请求所有可用交易品种的信息。为了将上下文仅限于市场概要,我们应将 UseMarketWatch 设置为 true

参数 ShowPerSymbolDetails 允许你输出每个交易品种的详细信息(默认情况下,该参数为 false,且仅显示统计数据)。

最后三个参数用于根据零保证金值的条件(分别为初始、维持和对冲)筛选交易品种。

为了在日志中收集并方便地显示每个交易品种的完整特性集(当 ShowPerSymbolDetails 打开时),在代码中定义了结构体 MarginSettings

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

由于一些特性为整数(SYMBOL_TRADE_CALC_MODE 和 SYMBOL_MARGIN_HEDGED_USE_LEG),而一些特性为实数(SYMBOL_MARGIN_INITIAL、SYMBOL_MARGIN_MAINTENANCE 和 SYMBOL_MARGIN_HEDGED),这些特性必须通过筛选对象单独请求。

现在让我们直接进入 OnStart 中的工作代码。此处,像往常一样,我们定义筛选对象 (f)、字符名称的输出数组 (symbols) 以及请求特性的值 (flags,values)。除此之外,我们还添加了一个结构体数组 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
   ...

引入了 stats 数组映射来计算统计数据,使用 ENUM_SYMBOL_CALC_MODE 这样的关键字和 int 整数值来计算遇到每种方法的次数。此外,零保证金的所有情况和长边启用的计算模式应记录在相应的计数器变量中。

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

接下来,我们指定与保证金相关的重要特性,这些特性将从交易品种设置中读取。首先是 ints 数组中的整数,然后是 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
   };
   ...

根据输入参数,我们可设置筛选条件。

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

现在一切都准备好了,可以按条件选择交易品种,并将其特性放入数组。我们执行此操作两次,分别针对整数和实数特性。

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

在第一次应用筛选器之后,必须将带有交易品种的数组清零,这样名称才不会重复。尽管有两次独立的查询,但所有输出数组(intsdoubles)中元素的顺序是相同的,因为筛选条件没有改变。

如果用户启用了详细日志,我们会为结构体的 margins 数组分配内存。

   if(ShowPerSymbolDetailsArrayResize(marginsn);

最后,我们通过迭代结果数组的所有元素来计算统计数据,并可选地填充结构数组。

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

现在,我们可以在日志中显示统计数据。

   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();
   ...

由于 ENUM_SYMBOL_CALC_MODE 枚举的元素显示为整数(信息不多),我们还显示了一个文本,其中每个值都有一个名称(来自 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)));
   }
   ...

如果需要所选字符的详细信息,我们可以输出结构体数组 margins

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

我们用不同的设置运行这个脚本几次。从默认设置开始。

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

第二次运行时,我们将 ShowPerSymbolDetailsExcludeZeroInitMargin 设置为 true。这需要关于初始保证金值不为零的所有交易品种的详细信息。

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