轻松快捷开发 MetaTrader 程序的函数库(第十二部分)。(基础篇)
◍ 从零搭一个 MT5 函数库骨架
在 MT5 里做重复策略开发时,把通用功能抽成函数库能省掉大量复制粘贴。官方示例工程从 2019-09-02 发布至今累计浏览 3378 次,说明这套基础封装对实盘脚本编写者一直有参考价值。
新建一个 Include 目录下的 .mqh 文件,用 #include 在 EA 或指标里引用即可。库文件不编译成独立 exe,只在编译主程序时静态链入,因此改动库后需重新编译调用方。
外汇与贵金属杠杆高、滑点随机,任何封装都只是工程便利,不预示信号胜率。先开 MT5 在 MQL5 编辑器建个空 .mqh,写个返回均线句柄的小函数,比直接读教程更快建立直觉。
先把账户对象和集合搭起来
在写交易类之前,得先有账户和品种的数据底座。本文先把账户对象(Account)立起来:它不是一个静态快照,而是能在交易过程中感知变化的活对象。 考虑到同一终端可能切换或管理多个账户,我们把单个账户对象放进一个账户对象集合里统一管理。这样后续扩展多账户监控时,不用改底层结构。 账户对象内部要挂上事件跟踪:杠杆、余额、盈亏、净值、账户限制这几项一旦变动,对象能自己捕获。对 MT5 实盘或策略测试来说,这意味着你能在 OnTrade 之外,用对象状态差分拿到变化明细,而不是每次去 AccountInfoInteger 轮询。 外汇和贵金属杠杆可调、净值波动快,这类账户事件的高频变化本身就是风险信号,用对象封装后更容易做阈值告警。
「把账户拆成一个可比较的对象」
在 MT5 里做多账户横向比对时,最省事的做法是把每个交易账户封装成独立对象,而不是靠全局函数随用随取。账户对象不依赖后代类去设定状态,它没有 status 属性,构造函数也不接收参数,只在初始化时把 AccountInfoInteger / Double / String 三类接口拿到的字段一次性填进去。 账户属性覆盖整数、实数、字符串三套枚举。整数侧包含 ACCOUNT_LOGIN(账号,long)、ACCOUNT_LEVERAGE(杠杆,long)、ACCOUNT_LIMIT_ORDERS(最大活动挂单,int)、ACCOUNT_TRADE_ALLOWED(服务器侧交易许可,bool)等 9 项;实数侧有 ACCOUNT_BALANCE、ACCOUNT_EQUITY、ACCOUNT_MARGIN_LEVEL(保证金百分比)等 14 项;字符串侧含 ACCOUNT_NAME、ACCOUNT_SERVER、ACCOUNT_CURRENCY、ACCOUNT_COMPANY 共 4 项。 MQL4 没有 ENUM_ACCOUNT_MARGIN_MODE,要做跨平台编译就得手补。在 ToMQL4.mqh 末尾补上该枚举后,条件编译里给 MQL4 的“保证金计算模式”返回 ACCOUNT_MARGIN_MODE_RETAIL_HEDGING,账户货币小数位直接写死 2 位,MQL5 则取真实属性。 判断两个账户对象是否指向同一账户,只比三个不变字段:公司名、账号、客户名。任一不等就返回 false 视为不同账户,三次全过才返回 true。这样在账户集合里做查重和排序时,不会把同经纪商的不同登录搞混。 外汇与贵金属杠杆账户存在强平与滑点风险,对象里的 ACCOUNT_MARGIN_SO_SO(爆仓线)和 ACCOUNT_MARGIN_LEVEL 仅作监控参考,不预示账户安全边界。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Data for working with accounts | class=class="str">"cmt">//+------------------------------------------------------------------+ 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
◍ 账户双精度属性的枚举划分
在 MQL5 里,账户的实数类属性被单独收进 ENUM_ACCOUNT_PROP_DOUBLE 枚举,起点紧接整数属性的总数 ACCOUNT_PROP_INTEGER_TOTAL(定义为 9),也就是从下标 9 开始排。这样设计让整数与双精度属性共用一套索引空间,排序和遍历时不用分两套数组。 枚举里 ACCOUNT_PROP_BALANCE 对应下标 9,往后依次是 CREDIT、PROFIT、EQUITY、MARGIN、MARGIN_FREE、MARGIN_LEVEL(以百分比表示)、MARGIN_SO_CALL(触发追加保证金的阈值)、MARGIN_SO_SO(触发强制平仓的阈值)等。外汇和贵金属杠杆高,MARGIN_LEVEL 掉到券商设定的 SO 水平附近时,账户可能被迫砍仓,开 MT5 后在账户观测窗口能直接核对这些数值。 ACCOUNT_PROP_MARGIN_INITIAL 与 ACCOUNT_PROP_MARGIN_MAINTENANCE 分别描述挂单占用与持仓最低维护保证金,ASSETS 和 LIABILITIES 则给出账户资产负债快照。写 EA 时若想动态读取浮亏下的权益曲线,直接按枚举名调 AccountInfoDouble 即可,不用硬背下标。
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 level, at which a deposit to an account is required(Margin Call) ACCOUNT_PROP_MARGIN_SO_SO, class=class="str">"cmt">// Margin level, at which the most loss-making position is closed(Stop Out) 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 };
账户属性枚举与排序偏移的底层定义
在 MT5 的账户信息封装里,字符串类属性被安排在所有整型和双精度型属性之后。上面这段枚举把 ACCOUNT_PROP_NAME 的起始值显式写成 (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL),而这两个宏分别被定义为 14 和 4,所以字符串属性实际从索引 18 开始排,后面依次是 SERVER(19)、CURRENCY(20)、COMPANY(21)。 宏 FIRST_ACC_STR_PROP 的计算式为整型可用数(14-0)加双精度可用数(14-0),结果为 28,说明若把跳过项也算进来,字符串段在扁平数组里的逻辑起点是 28。实际取字符串属性时,用 ACCOUNT_PROP_NAME 等枚举值直接当索引即可,不用手算。 排序枚举 ENUM_SORT_ACCOUNT_MODE 从 0 到 4 映射了登录号、账户类型、杠杆、挂单上限、强平模式五种依据。写多账户扫描器时,若想按杠杆倒序,把模式设为 2 再对返回的账户数组做降序即可,外汇和贵金属品种受杠杆与保证金规则影响大,回测前务必确认券商实际参数。
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 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Account class="type">class="kw">string properties | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_ACCOUNT_PROP_STRING { ACCOUNT_PROP_NAME = (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Client name 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=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Possible account sorting criteria | class=class="str">"cmt">//+------------------------------------------------------------------+ 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 到 SORT_BY_ACCOUNT_CURRENCY,编号 5 至 8 是整型属性,后面整型与字符串属性分别挂在 FIRST_ACC_DBL_PROP 与 FIRST_ACC_STR_PROP 基址上递增。 其中 FIRST_ACC_DBL_PROP+6 对应 SORT_BY_ACCOUNT_MARGIN_LEVEL,即按账户保证金水平百分比排序;FIRST_ACC_DBL_PROP+7 和 +8 分别是 Margin Call 与 Stop Out 触发线,做多账户扫描时这几个值最常用。 字符串类从 FIRST_ACC_STR_PROP 起算:+0 为客户名,+1 为交易服务器名,+2 为入金货币。写 CTrade 或自定义账户面板时,直接传这些枚举给 AccountsInfoInteger 的排序参数即可,MT5 里编译能立刻验证。 外汇与贵金属杠杆高,账户保证金水平掉到 Stop Out 附近时仓位会被强平,用上述排序做监控脚本前先在模拟盘跑通逻辑。
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_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">// Served 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