EA 交易中的资金管理函数(基础篇)
「EA 里的资金管理函数到底管什么」
在 MT5 写 EA 时,资金管理不是事后加的模块,而是下单逻辑的前置闸门。很多新手把 lot size 写死成 0.1,回测看着平稳,一上实盘遇到贵金属跳空就直接爆仓,这类事故在 MQL5 社区 2013 年的旧帖里已被反复验证。 MT5 提供了一组账户状态函数,让你在发单前先问清楚:现在净值多少、保证金占用多少、自由保证金还剩多少。CTrade 类配合 AccountInfoDouble 拿到的数据,才是动态仓位的计算地基。 外汇和贵金属自带高杠杆高风险,硬扛固定手数等于把账户命运交给行情波动。用函数读实时账户状态再算 lot,至少能让单笔风险控制在净值的可承受区间,概率上更不容易被一次异常波动清出局。
◍ 先摸清账户和品种的属性接口
MQL5 里想做资金管理,第一步不是写下单逻辑,而是先把「金融工具」和「交易账户」这两类属性读出来。客户端、程序状态那些先放一边,盯盘和风控真正用得上的是品种与账户侧的实时数据。 语言层面给了三组配对函数,按返回类型拆开:整数类用 SymbolInfoInteger / AccountInfoInteger,双精度浮点用 SymbolInfoDouble / AccountInfoDouble,字符串用 SymbolInfoString / AccountInfoString。品种函数带 Symbol 前缀,账户函数带 Account 前缀,命名规律直接对应数据归属。 这套接口不是 EA 专属。本文虽以 EA 交易为落点,但指标和脚本里调用同样的函数也能拿到完全一致的结果,验证时开个脚本跑一遍最快。 外汇与贵金属杠杆高、点值浮动,读账户杠杆和品种点差前先确认当前品种已就绪,否则返回的属性可能是默认值,误导后续仓位计算。
读取余额、净值与止损线的账户接口
在 MT5 里盯盘,最先要搞清楚的是账户层面的两个基准值:余额(Balance)和净值(Equity)。前者是已平仓的累计结算,后者是余额加所有未平仓位的浮动盈亏。两者都用入金货币计价,直接调 AccountInfoDouble() 就能拿到。 除了余额和净值,未平仓占用的保证金(Margin)、整体浮动盈利或亏损(Profit)、以及还能开仓的可用保证金(Free Margin)也是实时决策依据。特别是 Free Margin,决定你能否加仓,外汇和贵金属杠杆高,这个数值归零前往往就已经触发追加保证金风险。 账户还有一个隐蔽但致命的特征:止损线(Stop Out Level)。当净值不足以维持持仓,平台会强制平仓,这个阈值可能是百分比也可能是固定金额。用 AccountInfoDouble(ACCOUNT_MARGIN_SO_SO) 只拿到数值,必须再用 AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE) 判断单位,否则容易误读。 下面这段 MQL5 把关键字段一次取齐,注意 AccountInfoString(ACCOUNT_CURRENCY) 出现了两次,实际写 EA 时合并一次即可: double balance=AccountInfoDouble(ACCOUNT_BALANCE); // 取账户余额,已平仓累计结算,入金货币计价 double equity=AccountInfoDouble(ACCOUNT_EQUITY); // 取账户净值,余额加浮动盈亏 double margin=AccountInfoDouble(ACCOUNT_MARGIN); // 取已用保证金,未平仓占用 double float_profit=AccountInfoDouble(ACCOUNT_PROFIT); // 取浮动盈亏,全体未平仓滚动值 double free_margin=AccountInfoDouble(ACCOUNT_FREEMARGIN); // 取可用保证金,可开新仓的剩余资金 string account_currency=AccountInfoString(ACCOUNT_CURRENCY); // 取入金货币代码,如 USD double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); // 取止损线数值,单位未知需配合模式判断 //--- 获取帐户的币种 string account_currency=AccountInfoString(ACCOUNT_CURRENCY); // 重复获取入金货币,实战应去重 //--- 停止水平 double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); // 再次取止损线数值 //--- 停止模式 double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); // 同上行,冗余 ENUM_ACCOUNT_STOPOUT_MODE so_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); // 取止损模式枚举:百分比或货币 if(so_mode==ACCOUNT_STOPOUT_MODE_PERCENT) // 若模式为百分比 PrintFormat("Stop Out level in percents %.2f%%",stopout_level); // 打印百分比形式止损线,如 50.00% else // 否则为货币模式 PrintFormat("Stop Out level in currency %.2f %s",stopout_level,account_currency); // 打印货币形式止损线,如 200.00 USD
class="type">class="kw">double balance=AccountInfoDouble(ACCOUNT_BALANCE); class="type">class="kw">double equity=AccountInfoDouble(ACCOUNT_EQUITY); class="type">class="kw">double margin=AccountInfoDouble(ACCOUNT_MARGIN); class="type">class="kw">double float_profit=AccountInfoDouble(ACCOUNT_PROFIT); class="type">class="kw">double free_margin=AccountInfoDouble(ACCOUNT_FREEMARGIN); class="type">class="kw">string account_currency=AccountInfoString(ACCOUNT_CURRENCY); class="type">class="kw">double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); class=class="str">"cmt">//--- 获取帐户的币种 class="type">class="kw">string account_currency=AccountInfoString(ACCOUNT_CURRENCY); class=class="str">"cmt">//--- 停止水平 class="type">class="kw">double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); class=class="str">"cmt">//--- 停止模式 ENUM_ACCOUNT_STOPOUT_MODE so_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); if(so_mode==ACCOUNT_STOPOUT_MODE_PERCENT) PrintFormat("Stop Out level in percents %.2f%%",stopout_level); else PrintFormat("Stop Out level in currency %.2f %s",stopout_level,account_currency);
「EA 上线前先读帐户属性」
写 EA 最怕的不是逻辑错,而是把没管控的机器人直接丢进实盘。开跑前用 AccountInfoInteger() 把杠杆和帐户类型读出来,能挡掉一大半低级事故。 外汇和贵金属自带高杠杆高风险,不同帐户类型行为差异明显:竞价帐户在竞价开始前根本无法下单。下面这段代码把杠杆、帐户模式、交易权限一次性拉出来,复制到 MT5 脚本里就能跑。 int leverage=(int)AccountInfoInteger(ACCOUNT_LEVERAGE); // 把帐户杠杆读成整数,例如 1:100 返回 100 ENUM_ACCOUNT_TRADE_MODE mode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE); // 取帐户交易模式枚举:demo / contest / real switch(mode) // 按模式分支,在图上提示帐户类别 { case ACCOUNT_TRADE_MODE_DEMO: Comment("Account demo"); break; // 模拟帐户,不会动真钱 case ACCOUNT_TRADE_MODE_CONTEST: Comment(com,"Account Contest"); break; // 比赛帐户,资金规则特殊 case ACCOUNT_TRADE_MODE_REAL: Comment(com,"Account Real"); break; // 真实帐户,下单前必须人工确认 default: Comment(com,"Account unknown type"); // 未知类型,宁可停手 } bool trade_allowed=(bool)AccountInfoInteger(ACCOUNT_TRADE_ALLOWED); // 帐户层面是否允许交易 if(trade_allowed) Print("Trade is allowed"); // 允许才打印,否则提示封锁 else Print(com,"Trade is not allowed"); if(trade_allowed) { bool trade_expert=(bool)AccountInfoInteger(ACCOUNT_TRADE_EXPERT); // 单独查 EA 是否被授权交易 if(trade_expert) Print("Experts are allowed to trade"); // EA 有权,逻辑可继续 else Print("Experts are not allowed to trade"); // EA 被禁,强制退出订单发送 } 哪怕帐户显示可交易,也不代表你的 EA 有下单权。把这段检查塞进 OnInit 或前几行 OnTick,能在实盘事故前拦住机器人。
class="type">int leverage=(class="type">int)AccountInfoInteger(ACCOUNT_LEVERAGE); ENUM_ACCOUNT_TRADE_MODE mode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE); class="kw">switch(mode) { case ACCOUNT_TRADE_MODE_DEMO: Comment("Account demo"); break; case ACCOUNT_TRADE_MODE_CONTEST: Comment(com,"Account Contest"); break; case ACCOUNT_TRADE_MODE_REAL: Comment(com,"Account Real"); break; class="kw">default: Comment(com,"Account unknown type"); } class="type">bool trade_allowed=(class="type">bool)AccountInfoInteger(ACCOUNT_TRADE_ALLOWED); if(trade_allowed) Print("Trade is allowed"); else Print(com,"Trade is not allowed"); if(trade_allowed) { class="type">bool trade_expert=(class="type">bool)AccountInfoInteger(ACCOUNT_TRADE_EXPERT); if(trade_expert) Print("Experts are allowed to trade"); else Print("Experts are not allowed to trade"); }