MQL5 向导:如何创建风险和资金管理模块·进阶篇
⚖️

MQL5 向导:如何创建风险和资金管理模块·进阶篇

(2/3)· 接基础信号篇,深挖 EA 四大支柱里的资金阀值与敞口控制类如何落地

含代码示例偏理论 第 2/3 篇
不少交易者直接套用向导默认资金类,却没意识到风险百分比和挂单逻辑是两套独立接口。改一处不改另一处,回测和实盘的手数会悄悄漂移。先把 CExpertMoney 的虚方法看清,再谈调参。

「从零搭一个资金管理类的壳」

MT5 里做风控模块,第一步往往是继承系统自带的 CExpertMoney,而不是从头写仓位计算。下面这段头文件骨架演示了最小可用形态:只声明一个 CSampleMoney 类,公有继承自 CExpertMoney,类体暂时为空,纯粹先把编译链路跑通。 文件头部用 #property copyright 和 #property link 标注了 2010 年 MetaQuotes 的版权信息,并通过 #include <Expert\ExpertMoney.mqh> 把官方资金管理基类拉进来。实际你自己在 MT5 里新建 .mqh 时,这两行可以改成你自己的署名,但 include 路径必须指向标准库里的 Expert 目录,否则编译器会报找不到 CExpertMoney。 注释里还残留了 DLL 和 EX5 的 import 示例(SendMessageA、ExpertRecalculate、ErrorDescription),它们不是本类必须的,只是提示你后续若要把风控信号抛给外部 DLL 或调用 stdlib.ex5 的错误描述函数,导入语法长这样。先不动它们,把空类编译过,再往里塞计算逻辑才是稳妥路径。

MQL5 / C++
class=class="str">"cmt">//   class="type">int       SendMessageA(class="type">int hWnd,class="type">int Msg,class="type">int wParam,class="type">int lParam);
class=class="str">"cmt">// class="macro">#class="kw">import "my_expert.dll"
class=class="str">"cmt">//   class="type">int       ExpertRecalculate(class="type">int wParam,class="type">int lParam);
class=class="str">"cmt">// class="macro">#class="kw">import
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EX5 imports                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// class="macro">#class="kw">import "stdlib.ex5"
class=class="str">"cmt">//   class="type">class="kw">string ErrorDescription(class="type">int error_code);
class=class="str">"cmt">// class="macro">#class="kw">import
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                 SampleMoney.mqh |
class=class="str">"cmt">//|               Copyright class="num">2010, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Expert\ExpertMoney.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CSampleMoney.                                                |
class=class="str">"cmt">//| Purpose: Class for risk and money management.                      |
class=class="str">"cmt">//|        It is derived from the CExpertMoney class.                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSampleMoney : class="kw">public CExpertMoney
  {
  };
class=class="str">"cmt">//+------------------------------------------------------------------+

亏损后翻倍仓位的资金管理类骨架

在 MT5 的 EA 框架里,想做「亏一笔就下一笔双倍手数」的马丁式风控,通常从标准库 CExpertMoney 派生一个子类最省事。下面这段头文件只搭了空壳,但已经把基础参数位留出来了。 CSampleMoney 继承自 CExpertMoney,protected 区里放了 m_lots 这个 double 变量,注释写明它是「正常市况下的成交手数」。public 区给了构造函数声明和 Lots() 设值方法,用内联写法把传入的 lots 直接赋给 m_lots。 注意此时类体里还没有任何加仓翻倍的逻辑,只是把「基准手数」这一个点钉死了。真要跑翻倍,得在 CheckOpen/CheckClose 的派生实现里读持仓盈亏后改写下单量——外汇与贵金属杠杆高,这类序列加码在连亏 6~7 笔后保证金占用会指数级膨胀,实盘前务必在策略测试器里用历史数据验一遍爆仓距离。

MQL5 / C++
里能直接看到从 #include <Expert\ExpertMoney.mqh> 到类定义的完整写法,复制进 MetaEditor 建个 SampleMoney.mqh 就能编译通过,方便你接着填自己的翻倍规则。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Expert\ExpertMoney.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CSampleMoney.                                              |
class=class="str">"cmt">//| Purpose: Class for risk and money management                     |
class=class="str">"cmt">//|            doubling the volume after a loss deal.                |
class=class="str">"cmt">//|            It is derived from the CExpertMoney class.             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSampleMoney : class="kw">public CExpertMoney
  {
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                         SampleMoney.mqh          |
class=class="str">"cmt">//|                         Copyright class="num">2010, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                         [MQL5官方文档]     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Expert\ExpertMoney.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CSampleMoney.                                              |
class=class="str">"cmt">//| Purpose: Class for risk and money management                     |
class=class="str">"cmt">//|            doubling the volume after a loss deal.                |
class=class="str">"cmt">//|            It is derived from the CExpertMoney class.             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSampleMoney : class="kw">public CExpertMoney
  {
class="kw">protected:
   class=class="str">"cmt">//--- setup parameters
   class="type">class="kw">double            m_lots;   class=class="str">"cmt">// deal volume for "normal" conditions
class="kw">public:
                     CSampleMoney();
   class=class="str">"cmt">//--- methods to set the parameters
   class="type">void              Lots(class="type">class="kw">double lots) { m_lots=lots; }
  };
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 手搓资金管理类的构造与参数校验

在 MT5 的 EA 框架里,自定义头寸管理类通常继承自 CExpertMoney。下面这段 CSampleMoney 的实现,展示了构造函数如何给默认手数落地,以及校验逻辑怎么拦住不合规的下单量。 构造函数 CSampleMoney() 只做一件事:把成员变量 m_lots 设为 0.1。也就是说,如果你不在外部调用 Lots(0.2) 之类的方法,策略默认每次按 0.1 手去跑。 校验函数 ValidationSettings() 先调用父类的同名方法,再对 m_lots 做两道闸:一是必须在当前品种的 LotsMin() 与 LotsMax() 区间内,二是必须能被 LotsStep() 整除(容差 1e-10)。任意一道不过,就 printf 报错并返回 false,EA 不会继续初始化。 [CODE] 里的类声明还暴露了一个受保护方法 CheckPrevLoss(),从命名看是为连亏加仓或减仓预留的钩子,本小节未给出实现,但你在改写自己的资金管理器时可以照这个骨架接进去。外汇与贵金属杠杆高,手数越界可能直接被经纪商拒单或强平,参数校验不是可选项。

MQL5 / C++
class=class="str">"cmt">//| Constructor CSampleMoney.                                                                 |//| INPUT:  no.                                                                                 |//| OUTPUT: no.                                                                                 |//| REMARK: no.                                                                                 |//+------------------------------------------------------------------+
class="type">void CSampleMoney::CSampleMoney()
  {
class=class="str">"cmt">//--- setting the class="kw">default values
   m_lots=class="num">0.1;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Validation of the setup parameters.                                                            |//| INPUT:  no.                                                                                 |//| OUTPUT: true if the settings are correct, otherwise false.                                   |//| REMARK: no.                                                                                 |//+------------------------------------------------------------------+
class="type">bool CSampleMoney::ValidationSettings()
  {
class=class="str">"cmt">//--- Call the base class method
   if(!CExpertMoney::ValidationSettings()) class="kw">return(false);
class=class="str">"cmt">//--- Validation of parameters
   if(m_lots<m_symbol.LotsMin() || m_lots>m_symbol.LotsMax())
     {
      printf(__FUNCTION__+": the deal volume must be in the range %f to %f",m_symbol.LotsMin(),m_symbol.LotsMax());
      class="kw">return(false);
     }
   if(MathAbs(m_lots/m_symbol.LotsStep()-MathRound(m_lots/m_symbol.LotsStep()))>class="num">1.0E-10)
     {
      printf(__FUNCTION__+": the volume of the deal must be multiple of %f",m_symbol.LotsStep());
      class="kw">return(false);
     }
class=class="str">"cmt">//--- Successful completion
   class="kw">return(true);
  }
class CSampleMoney : class="kw">public CExpertMoney
  {
class="kw">protected:
   class=class="str">"cmt">//--- Setup parameters
   class="type">class="kw">double            m_lots;   class=class="str">"cmt">// deal volume for "normal" conditions
class="kw">public:
                     CSampleMoney();
   class=class="str">"cmt">//--- Methods to set parameters
   class="type">void              Lots(class="type">class="kw">double lots) { m_lots=lots; }
   class=class="str">"cmt">//--- Methods to validate parameters
   class="kw">virtual class="type">bool      ValidationSettings();
class="kw">protected:
   class="type">class="kw">double            CheckPrevLoss();
  };
class=class="str">"cmt">//+------------------------------------------------------------------+

「亏损后翻倍手数的检测逻辑」

在马丁类资金管理中,判断上一笔同品种交易是否亏损并取回其手数,是加仓倍数的起点。下面这段方法从账户历史里倒序扫描成交记录,只认当前品种、且利润为负的那一笔,返回它的成交量;若没有亏损单则返回 0.0。

MQL5 / C++
class="type">class="kw">double CSampleMoney::CheckPrevLoss()
  {
   class="type">class="kw">double lot=class="num">0.0;
   HistorySelect(class="num">0,TimeCurrent());
   class="type">int deals=HistoryDealsTotal();
   CDealInfo deal;
   for(class="type">int i=deals-class="num">1;i>=class="num">0;i--)
     {
      if(!deal.SelectByIndex(i))
        {
         printf(__FUNCTION__+": Error of deal selection by index");
         break;
        }
      if(deal.Symbol()!=m_symbol.Name()) class="kw">continue;
      if(deal.Profit()<class="num">0.0) lot=deal.Volume();
      break;
     }
   class="kw">return(lot);
  }
逐行拆解:HistorySelect(0,TimeCurrent()) 拉取从账户开启到当前时刻的全部历史;HistoryDealsTotal() 拿到成交总笔数。for 循环从最后一笔往前找,SelectByIndex(i) 按序号取单,符号不匹配就跳过,利润小于 0 就把该笔 Volume() 赋给 lot 并 break 退出。 这个类继承自 CExpertMoney,m_lots 存常规手数,CheckPrevLoss 的返回值会用于「亏一次就翻倍下注」的乘子计算。外汇与贵金属杠杆高,这种序列加仓在连亏时可能快速放大敞口,实盘前应在 MT5 策略测试器用至少 1 年 tick 数据回测最大回撤。

MQL5 / C++
class="type">class="kw">double CSampleMoney::CheckPrevLoss()
  {
   class="type">class="kw">double lot=class="num">0.0;
   HistorySelect(class="num">0,TimeCurrent());
   class="type">int deals=HistoryDealsTotal();
   CDealInfo deal;
   for(class="type">int i=deals-class="num">1;i>=class="num">0;i--)
     {
      if(!deal.SelectByIndex(i))
        {
         printf(__FUNCTION__+": Error of deal selection by index");
         break;
        }
      if(deal.Symbol()!=m_symbol.Name()) class="kw">continue;
      if(deal.Profit()<class="num">0.0) lot=deal.Volume();
      break;
     }
   class="kw">return(lot);
  }

马丁类加仓的手数约束写法

这段代码给出的是多头与空头开仓手数的两套取量逻辑,核心思路是:若上笔止损有记录,则本次手数取 CheckPrevLoss() 返回值的 2 倍,否则回落到基础手数 m_lots。外汇与贵金属杠杆高,这种翻倍取量在连亏情形下会快速放大敞口,实盘前务必在 MT5 策略测试器跑极端行情样本。 两个函数都先判 m_symbol 是否为空,空则直接返 0.0 不开仓;随后用 m_symbol.LotsMax() 截断交易所允许上限,再用 m_account.MaxLotCheck() 按账户保证金百分比 m_percent 复核可开手数。多空差异仅在价格源:多单用 Ask(),空单用 Bid()。 下面把多头函数逐行拆一下,方便你直接复制到自己的 CSampleMoney 类里改参数验证。

MQL5 / C++
class="type">class="kw">double CSampleMoney::CheckOpenLong(class="type">class="kw">double price,class="type">class="kw">double sl)
  {
  if(m_symbol==NULL) class="kw">return(class="num">0.0);
class=class="str">"cmt">//--- Select the lot size
  class="type">class="kw">double lot=class="num">2*CheckPrevLoss();
  if(lot==class="num">0.0) lot=m_lots;
class=class="str">"cmt">//--- Check the limits
  class="type">class="kw">double maxvol=m_symbol.LotsMax();
  if(lot>maxvol) lot=maxvol;
class=class="str">"cmt">//--- Check the margin requirements
  if(price==class="num">0.0) price=m_symbol.Ask();
  maxvol=m_account.MaxLotCheck(m_symbol.Name(),ORDER_TYPE_BUY,price,m_percent);
  if(lot>maxvol) lot=maxvol;
class=class="str">"cmt">//--- Return the trade volume
  class="kw">return(lot);
  }
把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 EA 资金类的敞口预警,你专注决策而非翻代码。

常见问题

Percent 一般在 EA 初始化或参数刷新时设置风险百分比基准;Validation 在每次计算交易量前做账户与品种条件校验,返回 false 会阻断下单。
可以,将 EA 的 CExpertMoney 派生类日志输出接入小布盯盘品种页,AIGC 会自动标注手数越界与风险百分比异常,省去人工比对。
百分比模式让仓位随净值浮动,避免净值增长后实际风险被稀释或缩水后爆仓,属于基础的资金曲线自适应思路。
两者通过 CExpert 主类桥接,资金类只回报建议交易量,信号类只回报方向,重写时保持虚方法签名即可互不侵入。