轻松快捷开发 MetaTrader 程序的函数库(第十三部分):帐户对象事件·进阶篇
(2/3)· 单账户事件无需独立类,比较前后属性差异即可捕获变更并推送图表
◍ 账户事件枚举里的监控位
MT5 的账户事件体系把经纪商侧变动拆成了两类标记:一类是阈值越界标志(FLAG 位),一类是具体事件枚举(ENUM)。写 EA 时若想只在保证金预警或停板水平变动时触发逻辑,直接比对 FLAG 位比轮询账户属性更省资源。
FLAG 段里值得盯的几个硬数字:维持保证金预警位是 4096,Margin Call 水平变动是 8192,Stop Out 水平变动是 16384,资产越界是 32768,负债越界是 65536,冻结佣金越界是 131072。这些常量按 2 的幂递增,做位运算过滤时不会互相覆盖。
ENUM_ACCOUNT_EVENT 里从 ACCOUNT_EVENT_NO_EVENT 起,列了杠杆升降、挂单上限增减、账户交易开关、EA 自动交易开关,以及余额/净值越过设定值的增减速事件。实盘外汇与贵金属波动剧烈、杠杆风险高,用这些事件做风控钩子可能比每秒查一次 AccountInfo 更及时。
开 MT5 按 F4 进 MetaEditor,把下面两段贴进新 include 文件,编译后就能在 OnTradeTransaction 里用 if(flags & ACCOUNT_EVENT_FLAG_MARGIN_SO_SO) 捕获爆仓线变动。
ACCOUNT_EVENT_FLAG_MARGIN_MAINTENANCE = class="num">4096, class=class="str">"cmt">// The funds reserved on an account to ensure a minimum amount for all open positions exceed the specified change value +/- ACCOUNT_EVENT_FLAG_MARGIN_SO_CALL = class="num">8192, class=class="str">"cmt">// Changing the Margin Call level ACCOUNT_EVENT_FLAG_MARGIN_SO_SO = class="num">16384, class=class="str">"cmt">// Changing the Stop Out level ACCOUNT_EVENT_FLAG_ASSETS = class="num">32768, class=class="str">"cmt">// The current assets on an account exceed the specified change value +/- ACCOUNT_EVENT_FLAG_LIABILITIES = class="num">65536, class=class="str">"cmt">// The current liabilities on an account exceed the specified change value +/- ACCOUNT_EVENT_FLAG_COMISSION_BLOCKED = class="num">131072, class=class="str">"cmt">// The current sum of blocked commissions on an account exceeds the specified change value +/- }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| List of possible account events | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_ACCOUNT_EVENT { ACCOUNT_EVENT_NO_EVENT = TRADE_EVENTS_NEXT_CODE, class=class="str">"cmt">// No event ACCOUNT_EVENT_LEVERAGE_INC, class=class="str">"cmt">// Increasing the leverage ACCOUNT_EVENT_LEVERAGE_DEC, class=class="str">"cmt">// Decreasing the leverage ACCOUNT_EVENT_LIMIT_ORDERS_INC, class=class="str">"cmt">// Increasing the maximum allowed number of active pending orders ACCOUNT_EVENT_LIMIT_ORDERS_DEC, class=class="str">"cmt">// Decreasing the maximum allowed number of active pending orders ACCOUNT_EVENT_TRADE_ALLOWED_ON, class=class="str">"cmt">// Enabling trading for the account ACCOUNT_EVENT_TRADE_ALLOWED_OFF, class=class="str">"cmt">// Disabling trading for the account ACCOUNT_EVENT_TRADE_EXPERT_ON, class=class="str">"cmt">// Enabling auto trading for the account ACCOUNT_EVENT_TRADE_EXPERT_OFF, class=class="str">"cmt">// Disabling auto trading for the account ACCOUNT_EVENT_BALANCE_INC, class=class="str">"cmt">// The balance exceeds the specified value ACCOUNT_EVENT_BALANCE_DEC, class=class="str">"cmt">// The balance falls below the specified value ACCOUNT_EVENT_EQUITY_INC, class=class="str">"cmt">// The equity exceeds the specified value ACCOUNT_EVENT_EQUITY_DEC, class=class="str">"cmt">// The equity falls below the specified value
「账户事件枚举里的保证金与盈亏触发点」
在 MT5 的账户事件回调里,这一组枚举专门描述账户状态越过阈值时的触发类型。从盈利、信用到各类保证金字段,每个都带 _INC(超过/增加)与 _DEC(跌破/减少)两个方向,共 22 个常量。 比如 ACCOUNT_EVENT_PROFIT_INC 表示账户利润超过指定值,ACCOUNT_EVENT_MARGIN_LEVEL_DEC 则是保证金水平(以百分比计)下降。做风控面板时,最该盯的是 MARGIN_LEVEL 与 MARGIN_SO_CALL 系列——它们直接关联爆仓前兆。 外汇和贵金属杠杆高,保证金水平从 200% 掉到 100% 以下可能只在几根分钟 K 内发生。把这些事件接进 EA 的 OnAccountEvent,比轮询 AccountInfoDouble 更省资源,也更快捕捉临界变化。
ACCOUNT_EVENT_PROFIT_INC, class=class="str">"cmt">// The profit exceeds the specified value ACCOUNT_EVENT_PROFIT_DEC, class=class="str">"cmt">// The profit falls below the specified value ACCOUNT_EVENT_CREDIT_INC, class=class="str">"cmt">// The credit exceeds the specified value ACCOUNT_EVENT_CREDIT_DEC, class=class="str">"cmt">// The credit falls below the specified value ACCOUNT_EVENT_MARGIN_INC, class=class="str">"cmt">// Increasing the reserved margin on an account in the deposit currency ACCOUNT_EVENT_MARGIN_DEC, class=class="str">"cmt">// Decreasing the reserved margin on an account in the deposit currency ACCOUNT_EVENT_MARGIN_FREE_INC, class=class="str">"cmt">// Increasing the free funds available for opening a position in a deposit currency ACCOUNT_EVENT_MARGIN_FREE_DEC, class=class="str">"cmt">// Decreasing the free funds available for opening a position in a deposit currency ACCOUNT_EVENT_MARGIN_LEVEL_INC, class=class="str">"cmt">// Increasing the margin level on an account in % ACCOUNT_EVENT_MARGIN_LEVEL_DEC, class=class="str">"cmt">// Decreasing the margin level on an account in % ACCOUNT_EVENT_MARGIN_INITIAL_INC, class=class="str">"cmt">// Increasing the funds reserved on an account to ensure a guarantee amount for all pending orders ACCOUNT_EVENT_MARGIN_INITIAL_DEC, class=class="str">"cmt">// Decreasing the funds reserved on an account to ensure a guarantee amount for all pending orders ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC, class=class="str">"cmt">// Increasing the funds reserved on an account to ensure a minimum amount for all open positions ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC, class=class="str">"cmt">// Decreasing the funds reserved on an account to ensure a minimum amount for all open positions ACCOUNT_EVENT_MARGIN_SO_CALL_INC, class=class="str">"cmt">// Increasing the Margin Call level ACCOUNT_EVENT_MARGIN_SO_CALL_DEC, class=class="str">"cmt">// Decreasing the Margin Call level ACCOUNT_EVENT_MARGIN_SO_SO_INC, class=class="str">"cmt">// Increasing the Stop Out level ACCOUNT_EVENT_MARGIN_SO_SO_DEC, class=class="str">"cmt">// Decreasing the Stop Out level ACCOUNT_EVENT_ASSETS_INC, class=class="str">"cmt">// Increasing the current asset size on the account ACCOUNT_EVENT_ASSETS_DEC class=class="str">"cmt">// Decreasing the current asset size on the account
账户事件与整数属性的枚举定义
在 MT5 的账号事件体系里,除了常见的权益变动,还有负债与冻结佣金两条容易被忽略的线索。ACCOUNT_EVENT_LIABILITIES_INC / DEC 对应账户当前负债的增减,ACCOUNT_EVENT_COMISSION_BLOCKED_INC / DEC 则跟踪被冻结佣金的体量变化;这些事件码常出现在 OnEvent 回调中,用来感知券商端资金的异动。 宏 ACCOUNT_EVENTS_NEXT_CODE 被定义为最后一个事件码加 1,即 ACCOUNT_EVENT_COMISSION_BLOCKED_DEC+1,相当于给自定义扩展事件留了一个起始偏移位。写 EA 时若想挂私有事件,从这个值往后排能避开系统占用区间。 账户整数属性由 ENUM_ACCOUNT_PROP_INTEGER 枚举罗列,从 ACCOUNT_PROP_LOGIN 到 ACCOUNT_PROP_SERVER_TYPE 共覆盖 10 项。其中 ACCOUNT_PROP_SERVER_TYPE 区分交易服务器是 MT5 还是 MT4 内核,跨版本桥接时这一项能直接决定某些接口调用是否合法。 宏 ACCOUNT_PROP_INTEGER_TOTAL 硬编码为 10,ACCOUNT_PROP_INTEGER_SKIP 为 0,说明这 10 个整数属性全部参与排序或遍历,没有预留跳过位。开 MT5 在 MetaEditor 里搜 ENUM_ACCOUNT_PROP_INTEGER,对照 AccountInfoInteger 函数逐个打印,就能验证你当前账户返回的实际值。
ACCOUNT_EVENT_LIABILITIES_INC, class=class="str">"cmt">// Increasing the current liabilities on the account ACCOUNT_EVENT_LIABILITIES_DEC, class=class="str">"cmt">// Decreasing the current liabilities on the account ACCOUNT_EVENT_COMISSION_BLOCKED_INC, class=class="str">"cmt">// Increasing the current sum of blocked commissions on an account ACCOUNT_EVENT_COMISSION_BLOCKED_DEC, class=class="str">"cmt">// Decreasing the current sum of blocked commissions on an account }; class="macro">#define ACCOUNT_EVENTS_NEXT_CODE(ACCOUNT_EVENT_COMISSION_BLOCKED_DEC+class="num">1) class=class="str">"cmt">// The code of the next event after the last account event code class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Account integer properties | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_ACCOUNT_PROP_INTEGER { ACCOUNT_PROP_LOGIN, class=class="str">"cmt">// Account number ACCOUNT_PROP_TRADE_MODE, class=class="str">"cmt">// Trading account type ACCOUNT_PROP_LEVERAGE, class=class="str">"cmt">// Provided leverage ACCOUNT_PROP_LIMIT_ORDERS, class=class="str">"cmt">// Maximum allowed number of active pending orders ACCOUNT_PROP_MARGIN_SO_MODE, class=class="str">"cmt">// Mode of setting the minimum available margin level ACCOUNT_PROP_TRADE_ALLOWED, class=class="str">"cmt">// Permission to trade for the current account from the server side ACCOUNT_PROP_TRADE_EXPERT, class=class="str">"cmt">// Permission to trade for an EA from the server side ACCOUNT_PROP_MARGIN_MODE, class=class="str">"cmt">// Margin calculation mode ACCOUNT_PROP_CURRENCY_DIGITS, class=class="str">"cmt">// Number of digits for an account currency necessary for accurate display of trading results ACCOUNT_PROP_SERVER_TYPE class=class="str">"cmt">// Trade server type(MetaTrader class="num">5, MetaTrader class="num">4) }; class="macro">#define ACCOUNT_PROP_INTEGER_TOTAL(class="num">10) class=class="str">"cmt">// Total number of account&class="macro">#x27;s integer properties class="macro">#define ACCOUNT_PROP_INTEGER_SKIP(class="num">0) class=class="str">"cmt">// Number of account&class="macro">#x27;s integer properties not used in sorting
◍ 账户真实属性在 MQL5 里的枚举落点
MT5 把账户的真实状态拆成了整数、双精度、字符串三类属性,其中双精度这组专门承载余额、净值、保证金等连续数值。下面这段枚举定义了 14 个双精度账户属性,从 ACCOUNT_PROP_BALANCE 到 ACCOUNT_PROP_COMMISSION_BLOCKED,覆盖了交易者盯盘时最该关心的资金面字段。 宏 ACCOUNT_PROP_DOUBLE_TOTAL 写死为 14,说明这一组属性数量固定;ACCOUNT_PROP_DOUBLE_SKIP 为 0 则表示没有预留不参与排序的废弃位。开 MT5 按 F4 进 MetaEditor,搜 ENUM_ACCOUNT_PROP_DOUBLE 能直接看到这套定义,比对你的 broker 实际返回字段有无缺漏。 字符串属性里 ACCOUNT_PROP_NAME 的起始偏移很有讲究:它等于整数属性总数加双精度属性总数(即 ACCOUNT_PROP_INTEGER_TOTAL + 14),这意味着三类枚举在底层是连续排布的,写通用遍历器时别漏掉这个偏移量。
enum ENUM_ACCOUNT_PROP_DOUBLE { ACCOUNT_PROP_BALANCE = ACCOUNT_PROP_INTEGER_TOTAL, class=class="str">"cmt">// Account balance in a deposit currency ACCOUNT_PROP_CREDIT, class=class="str">"cmt">// Credit in a deposit currency ACCOUNT_PROP_PROFIT, class=class="str">"cmt">// Current profit on an account in the account currency ACCOUNT_PROP_EQUITY, class=class="str">"cmt">// Equity on an account in the deposit currency ACCOUNT_PROP_MARGIN, class=class="str">"cmt">// Reserved margin on an account in a deposit currency ACCOUNT_PROP_MARGIN_FREE, class=class="str">"cmt">// Free funds available for opening a position in a deposit currency ACCOUNT_PROP_MARGIN_LEVEL, class=class="str">"cmt">// Margin level on an account in % ACCOUNT_PROP_MARGIN_SO_CALL, class=class="str">"cmt">// Margin Call level ACCOUNT_PROP_MARGIN_SO_SO, class=class="str">"cmt">// Stop Out level ACCOUNT_PROP_MARGIN_INITIAL, class=class="str">"cmt">// Funds reserved on an account to ensure a guarantee amount for all pending orders ACCOUNT_PROP_MARGIN_MAINTENANCE, class=class="str">"cmt">// Funds reserved on an account to ensure a minimum amount for all open positions ACCOUNT_PROP_ASSETS, class=class="str">"cmt">// Current assets on an account ACCOUNT_PROP_LIABILITIES, class=class="str">"cmt">// Current liabilities on an account ACCOUNT_PROP_COMMISSION_BLOCKED class=class="str">"cmt">// Current sum of blocked commissions on an account }; class="macro">#define ACCOUNT_PROP_DOUBLE_TOTAL(class="num">14) class=class="str">"cmt">// Total number of account&class="macro">#x27;s real properties class="macro">#define ACCOUNT_PROP_DOUBLE_SKIP(class="num">0) class=class="str">"cmt">// Number of account&class="macro">#x27;s real properties not used in sorting enum ENUM_ACCOUNT_PROP_STRING { ACCOUNT_PROP_NAME = (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Client name
「账户枚举与排序字段的偏移算法」
在批量扫描多账户时,字符串类属性只有 4 个:服务器名、入金货币、公司名,以及预留的占位,因此 ACCOUNT_PROP_STRING_TOTAL 被宏定义为 4,而 SKIP 为 0 表示没有需要排除在排序外的字符串属性。 整数与双精度属性的总数减去各自 SKIP 后,用 FIRST_ACC_DBL_PROP 和 FIRST_ACC_STR_PROP 两个宏算出字符串排序字段在全局枚举中的起始偏移。例如整数属性若总计 9 个、SKIP 0,双精度总计 5 个、SKIP 0,则字符串属性从索引 14 起排。 ENUM_SORT_ACCOUNT_MODE 把登录号、杠杆、保证金模式等直接映射为 0~9 的连续整型。写 EA 做账户横向比对时,按这个枚举下标取 AccountInfo 系列函数返回值,就能避开硬编码属性 ID。外汇与贵金属账户杠杆和保证金模式差异大,多账户回测前先按 SORT_BY_ACCOUNT_LEVERAGE 分组,可能减少爆仓逻辑的误判。
enum ENUM_ACCOUNT_STRING_PROP { ACCOUNT_PROP_SERVER, class=class="str">"cmt">// Trade server name ACCOUNT_PROP_CURRENCY, class=class="str">"cmt">// Deposit currency ACCOUNT_PROP_COMPANY class=class="str">"cmt">// Name of a company serving an account }; class="macro">#define ACCOUNT_PROP_STRING_TOTAL(class="num">4) class=class="str">"cmt">// Total number of account&class="macro">#x27;s class="type">class="kw">string properties class="macro">#define ACCOUNT_PROP_STRING_SKIP(class="num">0) class=class="str">"cmt">// Number of account class="type">class="kw">string properties not used in sorting class="macro">#define FIRST_ACC_DBL_PROP(ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP) class="macro">#define FIRST_ACC_STR_PROP(ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP+ACCOUNT_PROP_DOUBLE_TOTAL-ACCOUNT_PROP_DOUBLE_SKIP) enum ENUM_SORT_ACCOUNT_MODE { SORT_BY_ACCOUNT_LOGIN = class="num">0, class=class="str">"cmt">// Sort by account number SORT_BY_ACCOUNT_TRADE_MODE = class="num">1, class=class="str">"cmt">// Sort by trading account type SORT_BY_ACCOUNT_LEVERAGE = class="num">2, class=class="str">"cmt">// Sort by leverage SORT_BY_ACCOUNT_LIMIT_ORDERS = class="num">3, class=class="str">"cmt">// Sort by maximum acceptable number of existing pending orders SORT_BY_ACCOUNT_MARGIN_SO_MODE = class="num">4, class=class="str">"cmt">// Sort by mode for setting the minimum acceptable margin level SORT_BY_ACCOUNT_TRADE_ALLOWED = class="num">5, class=class="str">"cmt">// Sort by permission to trade for the current account SORT_BY_ACCOUNT_TRADE_EXPERT = class="num">6, class=class="str">"cmt">// Sort by permission to trade for an EA SORT_BY_ACCOUNT_MARGIN_MODE = class="num">7, class=class="str">"cmt">// Sort by margin calculation mode SORT_BY_ACCOUNT_CURRENCY_DIGITS = class="num">8, class=class="str">"cmt">// Sort by number of digits for an account currency SORT_BY_ACCOUNT_SERVER_TYPE = class="num">9 class=class="str">"cmt">// Sort by trade server type(MetaTrader class="num">5, MetaTrader class="num">4) };
账户排序枚举的收尾与类骨架
上面这组枚举把账户维度的双精度与字符串属性都映射成了排序键:从 FIRST_ACC_DBL_PROP 起算,余额、信用、利润、权益、保证金占用、自由保证金、保证金水平百分比、Margin Call 与 Stop Out 阈值、初始及维持保证金、资产、负债、冻结佣金依次占 0 到 13 偏移;字符串类则从 FIRST_ACC_STR_PROP 起,客户名、服务器名、存款币种、服务公司名占 0 到 3 偏移。 实际写 EA 时,若想按保证金水平对多账户面板排序,直接传 SORT_BY_ACCOUNT_MARGIN_LEVEL 即可,它的底层值就是 FIRST_ACC_DBL_PROP+6,比手写数字更不易在改枚举时错位。 枚举块闭花括号后,代码立即切入 CAccount 类声明:继承 CObject,private 里先压了一个 SData 结构体,注释只留了一句「Account integer properties」,说明下一步要在结构体内收纳账户的整数型属性字段。外汇与贵金属账户下的这些数值波动剧烈,用程序拉取排序时须意识到高风险,某个账户触发 Stop Out 偏移量不代表其他账户安全。
SORT_BY_ACCOUNT_BALANCE = FIRST_ACC_DBL_PROP, class=class="str">"cmt">// Sort by an account balance in the deposit currency SORT_BY_ACCOUNT_CREDIT = FIRST_ACC_DBL_PROP+class="num">1, class=class="str">"cmt">// Sort by credit in a deposit currency SORT_BY_ACCOUNT_PROFIT = FIRST_ACC_DBL_PROP+class="num">2, class=class="str">"cmt">// Sort by the current profit on an account in the deposit currency SORT_BY_ACCOUNT_EQUITY = FIRST_ACC_DBL_PROP+class="num">3, class=class="str">"cmt">// Sort by an account equity in the deposit currency SORT_BY_ACCOUNT_MARGIN = FIRST_ACC_DBL_PROP+class="num">4, class=class="str">"cmt">// Sort by an account reserved margin in the deposit currency SORT_BY_ACCOUNT_MARGIN_FREE = FIRST_ACC_DBL_PROP+class="num">5, class=class="str">"cmt">// Sort by account free funds available for opening a position in the deposit currency SORT_BY_ACCOUNT_MARGIN_LEVEL = FIRST_ACC_DBL_PROP+class="num">6, class=class="str">"cmt">// Sort by account margin level in % SORT_BY_ACCOUNT_MARGIN_SO_CALL = FIRST_ACC_DBL_PROP+class="num">7, class=class="str">"cmt">// Sort by margin level requiring depositing funds to an account(Margin Call) SORT_BY_ACCOUNT_MARGIN_SO_SO = FIRST_ACC_DBL_PROP+class="num">8, class=class="str">"cmt">// Sort by margin level, at which the most loss-making position is closed(Stop Out) SORT_BY_ACCOUNT_MARGIN_INITIAL = FIRST_ACC_DBL_PROP+class="num">9, class=class="str">"cmt">// Sort by funds reserved on an account to ensure a guarantee amount for all pending orders SORT_BY_ACCOUNT_MARGIN_MAINTENANCE = FIRST_ACC_DBL_PROP+class="num">10, class=class="str">"cmt">// Sort by funds reserved on an account to ensure a minimum amount for all open positions SORT_BY_ACCOUNT_ASSETS = FIRST_ACC_DBL_PROP+class="num">11, class=class="str">"cmt">// Sort by the amount of the current assets on an account SORT_BY_ACCOUNT_LIABILITIES = FIRST_ACC_DBL_PROP+class="num">12, class=class="str">"cmt">// Sort by the current liabilities on an account SORT_BY_ACCOUNT_COMMISSION_BLOCKED = FIRST_ACC_DBL_PROP+class="num">13, class=class="str">"cmt">// Sort by the current amount of blocked commissions on an account SORT_BY_ACCOUNT_NAME = FIRST_ACC_STR_PROP, class=class="str">"cmt">// Sort by a client name SORT_BY_ACCOUNT_SERVER = FIRST_ACC_STR_PROP+class="num">1, class=class="str">"cmt">// Sort by a trade server name SORT_BY_ACCOUNT_CURRENCY = FIRST_ACC_STR_PROP+class="num">2, class=class="str">"cmt">// Sort by a deposit currency SORT_BY_ACCOUNT_COMPANY = FIRST_ACC_STR_PROP+class="num">3 class=class="str">"cmt">// Sort by a name of a company serving an account }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Account class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CAccount : class="kw">public CObject { class="kw">private: class="kw">struct SData { class=class="str">"cmt">//--- Account integer properties