Appearance
交易账户信息
在本章中,我们将研究 MQL 程序(特别是智能交易系统)交易环境的最后一个重要方面,并且在接下来的几章中我们会对智能交易系统展开详细的开发探讨。现在让我们来谈谈交易账户。
拥有一个有效的账户并且与之建立活跃的连接是大多数 MQL 程序正常运行的必要条件。到目前为止,我们还没有重点关注这一点,但获取行情报价、报价数据,以及总体而言,能够打开一个可用的图表,都意味着要成功连接到一个交易账户。
在智能交易系统的背景下,账户还反映了客户的财务状况,积累了交易历史记录,并确定了允许进行交易的特定模式。
MQL5 API 允许您获取账户的属性,从账户号码到当前的盈利情况等。在终端中,所有这些属性都是只读的,并且是由经纪商在服务器上设置的。
终端一次只能连接到一个账户。所有的 MQL 程序都与这个账户进行交互。正如我们在 “各种类型程序的启动和停止特性” 部分中已经提到的,切换账户会引发附加到图表上的指标和智能交易系统的重新加载。不过,在 OnDeinit 处理函数中,程序可以找到初始化终止的原因,当切换账户时,这个原因将等于 REASON_ACCOUNT。
获取账户属性的函数概述
根据账户属性的类型,从逻辑上可将其完整集合分为三组。字符串类型的属性汇总在 ENUM_ACCOUNT_INFO_STRING
枚举中,并通过 AccountInfoString
函数进行查询。实型属性组合在 ENUM_ACCOUNT_INFO_DOUBLE
枚举中,用于查询这些属性的函数是 AccountInfoDouble
。AccountInfoInteger
函数中使用的 ENUM_ACCOUNT_INFO_INTEGER
枚举包含整数和布尔属性(标志)的标识符,以及几个应用的 ENUM_ACCOUNT_INFO
枚举。
c
double AccountInfoDouble(ENUM_ACCOUNT_INFO_DOUBLE property)
long AccountInfoInteger(ENUM_ACCOUNT_INFO_INTEGER property)
string AccountInfoString(ENUM_ACCOUNT_INFO_STRING property)
我们创建了 AccountMonitor
类(AccountMonitor.mqh
)来简化属性的读取操作。通过重载 get
方法,该类根据传入参数中特定枚举的元素,自动调用所需的 API 函数。
c
class AccountMonitor
{
public:
long get(const ENUM_ACCOUNT_INFO_INTEGER property) const
{
return AccountInfoInteger(property);
}
double get(const ENUM_ACCOUNT_INFO_DOUBLE property) const
{
return AccountInfoDouble(property);
}
string get(const ENUM_ACCOUNT_INFO_STRING property) const
{
return AccountInfoString(property);
}
long get(const int property, const long) const
{
return AccountInfoInteger((ENUM_ACCOUNT_INFO_INTEGER)property);
}
double get(const int property, const double) const
{
return AccountInfoDouble((ENUM_ACCOUNT_INFO_DOUBLE)property);
}
string get(const int property, const string) const
{
return AccountInfoString((ENUM_ACCOUNT_INFO_STRING)property);
}
...
此外,它还有几个 stringify
方法的重载,用于将属性值格式化为用户友好的字符串表示形式(特别是对于应用枚举,否则它们将显示为无实际意义的数字)。每个属性的特性将在以下部分进行讨论。
c
static string boolean(const long v)
{
return v? "true" : "false";
}
template<typename E>
static string enumstr(const long v)
{
return EnumToString((E)v);
}
// 根据整数值内的子类型“解码”属性
static string stringify(const long v, const ENUM_ACCOUNT_INFO_INTEGER property)
{
switch(property)
{
case ACCOUNT_TRADE_ALLOWED:
case ACCOUNT_TRADE_EXPERT:
case ACCOUNT_FIFO_CLOSE:
return boolean(v);
case ACCOUNT_TRADE_MODE:
return enumstr<ENUM_ACCOUNT_TRADE_MODE>(v);
case ACCOUNT_MARGIN_MODE:
return enumstr<ENUM_ACCOUNT_MARGIN_MODE>(v);
case ACCOUNT_MARGIN_SO_MODE:
return enumstr<ENUM_ACCOUNT_STOPOUT_MODE>(v);
}
return (string)v;
}
string stringify(const ENUM_ACCOUNT_INFO_INTEGER property) const
{
return stringify(AccountInfoInteger(property), property);
}
string stringify(const ENUM_ACCOUNT_INFO_DOUBLE property, const string format = NULL) const
{
if(format == NULL) return DoubleToString(AccountInfoDouble(property),
(int)get(ACCOUNT_CURRENCY_DIGITS));
return StringFormat(format, AccountInfoDouble(property));
}
string stringify(const ENUM_ACCOUNT_INFO_STRING property) const
{
return AccountInfoString(property);
}
...
最后,还有一个模板方法 list2log
,它允许获取有关账户的全面信息。
c
// 枚举类型 E 的所有属性的名称和值列表
template<typename E>
void list2log()
{
E e = (E)0; // 抑制“可能使用未初始化变量”的警告
int array[];
const int n = EnumToArray(e, array, 0, USHORT_MAX);
Print(typename(E), " Count=", n);
for(int i = 0; i < n; ++i)
{
e = (E)array[i];
PrintFormat("% 3d %s=%s", i, EnumToString(e), stringify(e));
}
}
};
我们将在下一部分测试这个新类的实际运行情况。
获取账户属性的函数概述
根据账户属性的类型,从逻辑上可将其完整集合分为三组。字符串类型的属性汇总在 ENUM_ACCOUNT_INFO_STRING
枚举中,并通过 AccountInfoString
函数进行查询。实型属性组合在 ENUM_ACCOUNT_INFO_DOUBLE
枚举中,用于查询这些属性的函数是 AccountInfoDouble
。AccountInfoInteger
函数中使用的 ENUM_ACCOUNT_INFO_INTEGER
枚举包含整数和布尔属性(标志)的标识符,以及几个应用的 ENUM_ACCOUNT_INFO
枚举。
c
double AccountInfoDouble(ENUM_ACCOUNT_INFO_DOUBLE property)
long AccountInfoInteger(ENUM_ACCOUNT_INFO_INTEGER property)
string AccountInfoString(ENUM_ACCOUNT_INFO_STRING property)
我们创建了 AccountMonitor
类(AccountMonitor.mqh
)来简化属性的读取操作。通过重载 get
方法,该类根据传入参数中特定枚举的元素,自动调用所需的 API 函数。
c
class AccountMonitor
{
public:
long get(const ENUM_ACCOUNT_INFO_INTEGER property) const
{
return AccountInfoInteger(property);
}
double get(const ENUM_ACCOUNT_INFO_DOUBLE property) const
{
return AccountInfoDouble(property);
}
string get(const ENUM_ACCOUNT_INFO_STRING property) const
{
return AccountInfoString(property);
}
long get(const int property, const long) const
{
return AccountInfoInteger((ENUM_ACCOUNT_INFO_INTEGER)property);
}
double get(const int property, const double) const
{
return AccountInfoDouble((ENUM_ACCOUNT_INFO_DOUBLE)property);
}
string get(const int property, const string) const
{
return AccountInfoString((ENUM_ACCOUNT_INFO_STRING)property);
}
...
此外,它还有几个 stringify
方法的重载,用于将属性值格式化为用户友好的字符串表示形式(特别是对于应用枚举,否则它们将显示为无实际意义的数字)。每个属性的特性将在以下部分进行讨论。
c
static string boolean(const long v)
{
return v? "true" : "false";
}
template<typename E>
static string enumstr(const long v)
{
return EnumToString((E)v);
}
// 根据整数值内的子类型“解码”属性
static string stringify(const long v, const ENUM_ACCOUNT_INFO_INTEGER property)
{
switch(property)
{
case ACCOUNT_TRADE_ALLOWED:
case ACCOUNT_TRADE_EXPERT:
case ACCOUNT_FIFO_CLOSE:
return boolean(v);
case ACCOUNT_TRADE_MODE:
return enumstr<ENUM_ACCOUNT_TRADE_MODE>(v);
case ACCOUNT_MARGIN_MODE:
return enumstr<ENUM_ACCOUNT_MARGIN_MODE>(v);
case ACCOUNT_MARGIN_SO_MODE:
return enumstr<ENUM_ACCOUNT_STOPOUT_MODE>(v);
}
return (string)v;
}
string stringify(const ENUM_ACCOUNT_INFO_INTEGER property) const
{
return stringify(AccountInfoInteger(property), property);
}
string stringify(const ENUM_ACCOUNT_INFO_DOUBLE property, const string format = NULL) const
{
if(format == NULL) return DoubleToString(AccountInfoDouble(property),
(int)get(ACCOUNT_CURRENCY_DIGITS));
return StringFormat(format, AccountInfoDouble(property));
}
string stringify(const ENUM_ACCOUNT_INFO_STRING property) const
{
return AccountInfoString(property);
}
...
最后,还有一个模板方法 list2log
,它允许获取有关账户的全面信息。
c
// 枚举类型 E 的所有属性的名称和值列表
template<typename E>
void list2log()
{
E e = (E)0; // 抑制“可能使用未初始化变量”的警告
int array[];
const int n = EnumToArray(e, array, 0, USHORT_MAX);
Print(typename(E), " Count=", n);
for(int i = 0; i < n; ++i)
{
e = (E)array[i];
PrintFormat("% 3d %s=%s", i, EnumToString(e), stringify(e));
}
}
};
我们将在下一部分测试这个新类的实际运行情况。
账户类型:真实账户、模拟账户或竞赛账户
MetaTrader 5 支持为客户开设几种不同类型的账户。ACCOUNT_TRADE_MODE
属性是 ENUM_ACCOUNT_INFO_INTEGER
的一部分,通过它可以查询当前的账户类型。该属性的可能取值在 ENUM_ACCOUNT_TRADE_MODE
枚举类型中有详细说明。
标识符 | 描述 |
---|---|
ACCOUNT_TRADE_MODE_DEMO | 模拟交易账户 |
ACCOUNT_TRADE_MODE_CONTEST | 竞赛交易账户 |
ACCOUNT_TRADE_MODE_REAL | 真实交易账户 |
这个属性对于构建 MQL 程序的模拟(免费)版本非常方便。功能齐全的付费版本可能需要与一个账户号码关联,并且该账户必须是真实账户。
正如我们在上一节运行 AccountInfo.mq5
脚本的示例中所看到的,“MetaQuotes-Demo” 服务器上的账户类型为 ACCOUNT_TRADE_MODE_DEMO
(模拟交易账户)。
账户货币
余额、盈利、保证金、佣金以及其他财务指标最终总是会换算成账户货币,即便某些交易的规定要求以其他货币进行结算,比如以外汇货币对的保证金货币来结算。
MQL5 API 提供了两个用于描述账户货币的属性:账户货币的名称以及表示的精度,也就是最小度量单位的大小(比如美分)。
标识符 | 描述 |
---|---|
ACCOUNT_CURRENCY | 存入资金的货币(字符串) |
ACCOUNT_CURRENCY_DIGITS | 为准确显示交易结果,账户货币所需的小数位数(整数) |
例如,在 “账户识别” 部分用于测试 AccountInfo 脚本的模拟账户中,ACCOUNT_CURRENCY 属性值为 “USD”,而 ACCOUNT_CURRENCY_DIGITS 的精度为 2 位小数。我们在 AccountMonitor 类的 stringify 方法中,针对 double 类型的值(在账户的各项特征中,这些值都与资金相关)使用了 ACCOUNT_CURRENCY_DIGITS 属性。
账户类型:净额结算或对冲
MetaTrader 5 支持多种类型的账户,特别是净额结算(netting)和对冲(hedging)账户。
净额结算账户
在净额结算账户中,对于每个交易品种只允许持有一个头寸。也就是说,针对某个特定的交易品种,不能同时存在多个不同方向或相同方向的头寸,只能有一个综合的头寸状态。
对冲账户
而在对冲账户中,你可以为一个交易品种开立多个头寸,这些头寸甚至可以是不同方向的。例如,针对同一交易品种,你既可以持有多头头寸,同时也能持有空头头寸。
确定账户类型
MQL 程序可以通过使用 AccountInfoInteger
函数查询 ACCOUNT_MARGIN_MODE
属性来确定账户类型。从属性名称可以看出,它不仅描述了账户类型,还涉及保证金的计算模式。该属性的可能值在 ENUM_ACCOUNT_MARGIN_MODE
枚举中定义,具体如下:
标识符 | 描述 |
---|---|
ACCOUNT_MARGIN_MODE_RETAIL_NETTING | 场外交易(OTC)市场,采用净额结算模式处理头寸。保证金的计算基于 SYMBOL_TRADE_CALC_MODE 属性。 |
ACCOUNT_MARGIN_MODE_EXCHANGE | 交易所市场,采用净额结算模式处理头寸。保证金根据交易所的规则进行计算,同时可能会有经纪商在交易品种设置中规定的折扣。 |
ACCOUNT_MARGIN_MODE_RETAIL_HEDGING | 场外交易(OTC)市场,采用对冲模式独立处理头寸。保证金计算基于 SYMBOL_TRADE_CALC_MODE 交易品种属性,同时会考虑对冲保证金 SYMBOL_MARGIN_HEDGED 的大小。 |
例如,在“账户识别”部分运行 AccountInfo
脚本后,显示账户类型为 ACCOUNT_MARGIN_MODE_RETAIL_HEDGING
,这表明该账户是一个场外交易的对冲账户,在计算保证金时会综合考虑交易品种的相关属性和对冲保证金情况。
账户操作的限制与权限
在账户的各项属性中,存在着对交易操作的限制,其中包括完全禁止交易的情况。除了 ACCOUNT_LIMIT_ORDERS
之外,这些属性都属于 ENUM_ACCOUNT_INFO_INTEGER
枚举,并且是布尔标志。
标识符 | 描述 |
---|---|
ACCOUNT_TRADE_ALLOWED | 当前账户的交易权限 |
ACCOUNT_TRADE_EXPERT | 使用智能交易系统和脚本进行算法交易的权限 |
ACCOUNT_LIMIT_ORDERS | 允许的有效挂单的最大数量 |
ACCOUNT_FIFO_CLOSE | 仅按照先进先出(FIFO)规则平仓的要求 |
由于本书聚焦于 MQL5 编程,其中涵盖了算法交易,所以需要注意的是,当 ACCOUNT_TRADE_EXPERT
权限被禁用时,其严重性等同于 ACCOUNT_TRADE_ALLOWED
为 false
时的全面禁止交易。经纪商能够在允许手动交易的同时,禁止使用智能交易系统和脚本进行交易。
若使用投资密码连接账户,ACCOUNT_TRADE_ALLOWED
属性通常为 false
。
若 ACCOUNT_FIFO_CLOSE
属性的值为 true
,那么每个品种的仓位只能按照开仓的顺序平仓,也就是先平掉最旧的订单,接着是较新的订单,依此类推,直至最后一个订单。若尝试以不同顺序平仓,将会收到错误提示。对于不支持仓位对冲的账户,即当 ACCOUNT_MARGIN_MODE
属性不等于 ACCOUNT_MARGIN_MODE_RETAIL_HEDGING
时,ACCOUNT_FIFO_CLOSE
属性始终为 false
。
在“交易和报价时段的权限与时间表”部分,我们已经着手开发一个用于检测 MQL 程序可用交易操作的类。现在,我们可以为其添加账户权限检查功能,并将其完善至最终版本(Permissions.mqh
)。
限制级别在 TRADE_RESTRICTIONS
枚举中给出,在添加了两个与账户属性相关的新元素后,其形式如下:
c
class Permissions
{
enum TRADE_RESTRICTIONS
{
NO_RESTRICTIONS = 0,
TERMINAL_RESTRICTION = 1, // 用户对所有程序的限制
PROGRAM_RESTRICTION = 2, // 用户对特定程序的限制
SYMBOL_RESTRICTION = 4, // 根据规格该品种不可交易
SESSION_RESTRICTION = 8, // 根据交易时段表市场处于关闭状态
ACCOUNT_RESTRICTION = 16, // 投资密码或经纪商限制
EXPERTS_RESTRICTION = 32, // 经纪商限制了算法交易
};
...
在检查过程中,MQL 程序可能会因为各种原因检测到多种限制,因此这些元素采用单独的位进行编码。最终结果可以表示它们的叠加。
最后两个限制恰好对应新属性,并在 getTradeRestrictionsOnAccount
方法中设置。检测到的限制(如果有的话)的通用位掩码存储在 lastRestrictionBitMask
变量中。
c
private:
static uint lastRestrictionBitMask;
static bool pass(const uint bitflag)
{
lastRestrictionBitMask |= bitflag;
return lastRestrictionBitMask == 0;
}
public:
static uint getTradeRestrictionsOnAccount()
{
return (AccountInfoInteger(ACCOUNT_TRADE_ALLOWED) ? 0 : ACCOUNT_RESTRICTION)
| (AccountInfoInteger(ACCOUNT_TRADE_EXPERT) ? 0 : EXPERTS_RESTRICTION);
}
static bool isTradeOnAccountEnabled()
{
lastRestrictionBitMask = 0;
return pass(getTradeRestrictionsOnAccount());
}
...
如果调用代码不关心限制的原因,而只需要确定是否可以执行交易操作,那么使用 isTradeOnAccountEnabled
方法会更方便,该方法返回一个布尔值(true
/false
)。
对品种和终端属性的检查也按照类似的原则进行了重构。例如,getTradeRestrictionsOnSymbol
方法包含了该类先前版本中已经熟悉的源代码(检查品种的交易时段和交易模式),但返回一个标志位掩码。如果至少有一位被设置,则表示存在限制的来源。
c
static uint getTradeRestrictionsOnSymbol(const string symbol, datetime now = 0,
const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
{
if(now == 0) now = TimeTradeServer();
bool found = false;
// 检查品种的交易时段,如果 'now' 时间在某个交易时段内,则将 'found' 设置为 'true'
...
// 除了交易时段,还要检查交易模式
const ENUM_SYMBOL_TRADE_MODE m = (ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
return (found ? 0 : SESSION_RESTRICTION)
| (((m & mode) != 0) || (m == SYMBOL_TRADE_MODE_FULL) ? 0 : SYMBOL_RESTRICTION);
}
static bool isTradeOnSymbolEnabled(const string symbol, const datetime now = 0,
const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
{
lastRestrictionBitMask = 0;
return pass(getTradeRestrictionsOnSymbol(symbol, now, mode));
}
...
最后,在 getTradeRestrictions
和 isTradeEnabled
方法中对所有潜在的“实例”进行全面检查,包括(除了之前的级别)终端和程序的设置。
c
static uint getTradeRestrictions(const string symbol = NULL, const datetime now = 0,
const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
{
return (TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? 0 : TERMINAL_RESTRICTION)
| (MQLInfoInteger(MQL_TRADE_ALLOWED) ? 0 : PROGRAM_RESTRICTION)
| getTradeRestrictionsOnSymbol(symbol == NULL ? _Symbol : symbol, now, mode)
| getTradeRestrictionsOnAccount();
}
static bool isTradeEnabled(const string symbol = NULL, const datetime now = 0,
const ENUM_SYMBOL_TRADE_MODE mode = SYMBOL_TRADE_MODE_FULL)
{
lastRestrictionBitMask = 0;
return pass(getTradeRestrictions(symbol, now, mode));
}
脚本 AccountPermissions.mq5
展示了如何使用新类对交易权限进行全面检查。
c
#include <MQL5Book/Permissions.mqh>
void OnStart()
{
PrintFormat("Run on %s", _Symbol);
if(!Permissions::isTradeEnabled()) // 检查当前品种,使用默认设置
{
Print("Trade is disabled for the following reasons:");
Print(Permissions::explainLastRestrictionBitMask());
}
else
{
Print("Trade is enabled");
}
}
如果发现存在限制,可以使用 explainLastRestrictionBitMask
方法将其位掩码以清晰的字符串形式显示出来。
以下是一些脚本运行结果。在前两种情况下,终端的全局设置中禁止了交易(TERMINAL_TRADE_ALLOWED
和 MQL_TRADE_ALLOWED
属性为 false
,对应于 TERMINAL_RESTRICTION
和 PROGRAM_RESTRICTION
位)。
当在市场关闭时段对 USDRUB 运行脚本时,我们还会收到 SESSION_RESTRICTION
:
plaintext
Trade is disabled for USDRUB following reasons:
TERMINAL_RESTRICTION PROGRAM_RESTRICTION SESSION_RESTRICTION
对于完全禁止交易的品种 SP500m,会出现 SYMBOL_RESTRICTION
标志:
plaintext
Trade is disabled for SP500m following reasons:
TERMINAL_RESTRICTION PROGRAM_RESTRICTION SYMBOL_RESTRICTION SESSION_RESTRICTION
最后,在终端允许交易但使用投资密码登录账户的情况下,在任何品种上都会看到 ACCOUNT_RESTRICTION
:
plaintext
Run on XAUUSD
Trade is disabled for following reasons:
ACCOUNT_RESTRICTION
在 MQL 程序中提前检查权限有助于避免多次尝试发送交易订单失败的情况。
账户保证金设置
对于交易机器人而言,把控已占用保证金的数额以及可用于开新仓的可用保证金数额至关重要。特别是当可用资金不足时,程序将无法执行交易。在持有未盈利的开仓头寸时,首先会收到追加保证金通知(Margin Call),若未及时处理,经纪商会强制平仓(Stop Out)。所有与账户相关的保证金属性都包含在 ENUM_ACCOUNT_INFO_DOUBLE
枚举中。
标识符 | 描述 |
---|---|
ACCOUNT_MARGIN | 账户当前以存款货币计算的已占用保证金 |
ACCOUNT_MARGIN_FREE | 账户当前以存款货币计算的可用保证金,可用于开仓 |
ACCOUNT_MARGIN_LEVEL | 账户保证金水平(百分比,计算公式为:权益/保证金 * 100) |
ACCOUNT_MARGIN_SO_CALL | 要求补充账户资金的最低保证金水平(追加保证金通知) |
ACCOUNT_MARGIN_SO_SO | 强制平仓最不盈利头寸的最低保证金水平(强制平仓) |
ACCOUNT_MARGIN_INITIAL | 账户为所有挂单预留的保证金 |
ACCOUNT_MARGIN_MAINTENANCE | 账户为所有开仓头寸提供所需最低保证金而预留的资金 |
ACCOUNT_MARGIN_SO_CALL
和 ACCOUNT_MARGIN_SO_SO
可以按百分比或者存款货币来表示,具体取决于 ACCOUNT_MARGIN_SO_MODE
的设置(详见后文)。这个可用于设置追加保证金通知或强制平仓保证金阈值的属性包含在 ENUM_ACCOUNT_INFO_INTEGER
枚举中。此外,该枚举中还标明了总杠杆(用于计算某些类型工具的保证金)。
标识符 | 描述 |
---|---|
ACCOUNT_LEVERAGE | 杠杆倍数 |
ACCOUNT_MARGIN_SO_MODE | 从 ENUM_ACCOUNT_STOPOUT_MODE 枚举中选择的设置最低允许保证金水平的模式 |
以下是 ENUM_ACCOUNT_STOPOUT_MODE
枚举的元素:
标识符 | 描述 |
---|---|
ACCOUNT_STOPOUT_MODE_PERCENT | 保证金水平以百分比设置 |
ACCOUNT_STOPOUT_MODE_MONEY | 保证金水平以账户货币设置 |
例如,对于 ACCOUNT_STOPOUT_MODE_PERCENT
选项,需要将指定的百分比(追加保证金通知或强制平仓)与权益和 ACCOUNT_MARGIN
属性值的比率进行比较:
plaintext
AccountInfoDouble(ACCOUNT_EQUITY) / AccountInfoDouble(ACCOUNT_MARGIN) * 100 > AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL)
在下一节中,你将了解到关于 ACCOUNT_EQUITY
属性以及账户其他财务指标的更多详细信息。
不过,当前保证金水平的百分比已经在 ACCOUNT_MARGIN_LEVEL
属性中给出。可以通过 AccountInfo.mq5
脚本来轻松验证这一点,该脚本会记录所有账户属性,包括上述列出的属性。
我们在“账户识别”部分已经运行过这个脚本。当时开了一个仓位(1 手美元兑卢布,相当于 100,000 美元),财务状况如下:
plaintext
0 ACCOUNT_BALANCE=10000.00
1 ACCOUNT_CREDIT=0.00
2 ACCOUNT_PROFIT=-78.76
3 ACCOUNT_EQUITY=9921.24
4 ACCOUNT_MARGIN=1000.00
5 ACCOUNT_MARGIN_FREE=8921.24
6 ACCOUNT_MARGIN_LEVEL=992.12
7 ACCOUNT_MARGIN_SO_CALL=50.00
8 ACCOUNT_MARGIN_SO_SO=30.00
保证金为 1000.00 美元时,很容易验证账户的杠杆 ACCOUNT_LEVERAGE
确实是 100(根据外汇保证金计算和保证金比率公式,比率为 1.0)。由于保证金金额与工具的基础货币相同,所以无需按当前汇率将其转换为账户货币。
要得到 ACCOUNT_MARGIN_LEVEL
中的 992.12,只需将 9921.24 除以 1000.00 再乘以 100%。
随后又开了 1 手仓位,并且报价走势不利,结果情况发生了变化:
plaintext
0 ACCOUNT_BALANCE=10000.00
1 ACCOUNT_CREDIT=0.00
2 ACCOUNT_PROFIT=-1486.07
3 ACCOUNT_EQUITY=8513.93
4 ACCOUNT_MARGIN=2000.00
5 ACCOUNT_MARGIN_FREE=6513.93
6 ACCOUNT_MARGIN_LEVEL=425.70
我们可以看到 ACCOUNT_PROFIT
列中出现了亏损,相应地,权益 ACCOUNT_EQUITY
也减少了。保证金 ACCOUNT_MARGIN
从 1000 美元成比例增加到 2000 美元,可用保证金和保证金水平都降低了(但仍远高于 50% 和 30% 的限制)。同样,425.70 这个水平是通过计算 8513.93 / 2000.00 * 100
得到的。
在开新仓之前,使用以下公式来计算未来的保证金水平更具实用性。在这种情况下,需要将现有保证金增加额外的保证金 X。此外,如果开仓交易涉及立即扣除手续费 C,严格来说也应该考虑在内(尽管通常手续费远小于保证金,可以忽略不计,而且 API 没有提供在交易执行前预先了解手续费的方法,只能通过交易历史中已完成交易的手续费来估算)。
plaintext
(AccountInfoDouble(ACCOUNT_EQUITY) - C) / (AccountInfoDouble(ACCOUNT_MARGIN) + X) * 100 > AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL)
后续我们将学习如何使用 OrderCalcMargin
函数获取 X 值,但除此之外,可能还需要根据“保证金要求”部分公布的规则进行调整,特别是要考虑可能的头寸对冲、折扣和保证金调整。
对于以货币形式设置保证金限制的选项(ACCOUNT_STOPOUT_MODE_MONEY
),检查资金是否充足的方式有所不同:
plaintext
AccountInfoDouble(ACCOUNT_EQUITY) > AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL)
这里省略了手续费。请注意,准备“立即”开仓的新头寸的保证金 X 不会对“未来”保证金的评估产生任何影响。
然而,无论如何,都不建议过度占用存款,以免刚刚满足不等式条件。ACCOUNT_MARGIN_SO_CALL
和 ACCOUNT_MARGIN_SO_SO
的值非常接近,尽管 ACCOUNT_MARGIN_SO_CALL
水平的保证金只是对交易者的一个警告,但很容易导致强制平仓。这就是为什么公式中使用 ACCOUNT_MARGIN_SO_CALL
属性的原因。
账户当前财务状况
MQL5 API 允许通过其主要财务指标来查看多个账户属性。这些属性都包含在 ENUM_ACCOUNT_INFO_DOUBLE 枚举类型中。
标识符 | 描述 |
---|---|
ACCOUNT_BALANCE | 以存入资金货币计价的账户余额 |
ACCOUNT_PROFIT | 以存入资金货币计价的账户当前盈利金额 |
ACCOUNT_EQUITY | 以存入资金货币计价的账户净值 |
ACCOUNT_CREDIT | 经纪商提供的以存入资金货币计价的信贷金额 |
ACCOUNT_ASSETS | 账户当前的资产金额 |
ACCOUNT_LIABILITIES | 账户当前的负债金额 |
ACCOUNT_COMMISSION_BLOCKED | 账户当前被冻结的佣金金额 |
在前面的章节中,我们看到了在不同条件下运行 AccountInfo.mq5 脚本时这些属性的值的示例。您可以尝试比较不同账户的这些属性。
在交易过程中,我们主要关注前三个属性:余额、盈利(如果值为负则为亏损)和净值,它们共同涵盖了账户余额、信贷、盈利以及间接费用(掉期费和佣金)。
根据经纪商的设置,佣金的处理方式可能不同。如果在交易时佣金立即从账户余额中扣除,并反映在交易属性中,那么账户属性 ACCOUNT_COMMISSION_BLOCKED 将等于 0。然而,如果佣金的计算推迟到一个时期(例如一天或一个月)结束时进行,那么为佣金预留的冻结金额将显示在这个属性中。然后,当在该时期结束时确定了最终的佣金金额并从余额中扣除后,该属性的值将被重置为 0。
通常情况下,属性 ACCOUNT_ASSETS 和 ACCOUNT_LIABILITIES 仅在交易所交易时才会被填充值。它们反映了证券多头和空头头寸的当前价值。