您应当知道的 MQL5 向导技术(第 45 部分):蒙特卡洛强化学习·进阶篇
📘

您应当知道的 MQL5 向导技术(第 45 部分):蒙特卡洛强化学习·进阶篇

第 2/2 篇

◍ 蒙特卡洛信号类的骨架与可调旋钮

这段继承自 CExpertSignal 的 CSignalMC 类,把蒙特卡洛强化学习塞进了 MT5 信号模块。它不像常规指标那样算均线,而是靠环境矩阵和回合奖励去推导买卖动作,外汇与贵金属品种上属于高风险试探,实盘前务必在策略测试器跑通。 保护变量里先铺好维度:m_actions 是马尔可夫可选动作数,m_environments 是环境矩阵单边长度,m_scale 控制行到列的缩放比例。m_epsilon 管探索概率,m_policy 切换 on/off policy,m_episode_cycles 定一个回合的步数——这些直接决定模型是保守跟随还是乱撞寻优。 对外暴露的 setter 极简:QL_Scale(int) 只改 m_scale,QL_Markov(bool) 切马尔可夫开关,QL_Epsilon(bool) 写 m_epsilon。注意原文 QL_Epsilon 参数类型标了 bool,但变量 m_epsilon 是 double,复制时若想做 0.1 这类渐进探索率得自己改签名,否则只能是非零即满探索。 奖励回传那几行是核心回路:按 gamma 的 (回合步数-1-i) 次幂折扣累加 Rewards[i],再依据 m_policy 分流进 SetOnPolicy 或 SetOffPolicy。折扣因子越接近 1,远期奖励权重越高,EURUSD 回测里常看到滞后信号被放大。

MQL5 / C++
    {  _reward += pow(QL.THIS.gamma, m_episode_cycles - class="num">1 - i) * Rewards[i];
    }
    if(m_policy)
    {  QL.SetOnPolicy(_reward, _in_e);
    }
    else if(!m_policy)
    {  QL.SetOffPolicy(_reward, _in_e);
    }
   }
 }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| MCs CSignalMC.                                                      |
class=class="str">"cmt">//| Purpose: MonteCarlo for Reinforcement-Learning.                     |
class=class="str">"cmt">//|         Derives from class CExpertSignal.                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSignalMC  : class="kw">public CExpertSignal
{
class="kw">protected:
   class="type">int                m_actions;       class=class="str">"cmt">// LetMarkov possible actions
   class="type">int                m_environments;  class=class="str">"cmt">// Environments, per matrix axis
   class="type">int                m_scale;         class=class="str">"cmt">// Environments, row-to-col scale
   class="type">bool               m_use_markov;    class=class="str">"cmt">// Use Markov
   class="type">class="kw">double             m_epsilon;       class=class="str">"cmt">// Epsilon
   class="type">bool               m_policy;        class=class="str">"cmt">// On Policy
   class="type">int                m_episode_cycles;class=class="str">"cmt">// Episode Size
class="kw">public:
   class="type">void               CSignalMC(class="type">void);
   class="type">void               ~CSignalMC(class="type">void);
   class=class="str">"cmt">//--- methods of setting adjustable parameters
   class="type">void               QL_Scale(class="type">int value)
   {  m_scale = value;
   }
   class="type">void               QL_Markov(class="type">bool value)
   {  m_use_markov = value;
   }
   class="type">void               QL_Epsilon(class="type">bool value)
   {  m_epsilon = value;
   }

强化学习交易类的接口与成员落地

这段声明把一套 QL 策略骨架直接摊开在 MT5 自定义信号类里。两个 setter 把策略开关和回合数塞进成员变量,训练节奏和在线/离线决策由此分离。 void QL_Policy(bool value) { m_policy = value; } void QL_EpisodeCycles(int value) { m_episode_cycles = value; } 上面两段就是最直白的写入接口:Policy 为真时倾向走学习策略,EpisodeCycles 控制单轮训练步数,默认不传就容易让代理一直用初始随机权重瞎跑。 //--- method of verification of arch virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); 这几个虚函数把架构校验、指标初始化和多空条件判断全留给子类实现。LongCondition 返回大于 0 才认为多头模型成立,实盘里若返回恒为 0,EA 就不会开多——这是排查信号失效的第一道闸。 protected: void GetOutput(Cql *QL, vector &Rewards); Sql RL; Cql *QL_BUY, *QL_SELL; vector REWARDS_BUY, REWARDS_SELL; 保护段里挂了买/卖两套 Q 学习指针和对应奖励向量。REWARDS_BUY 与 REWARDS_SELL 分开存,意味着多空奖励函数可以非对称设计;外汇和贵金属波动剧烈,这种分离对控制单边滑点损耗有实际意义,但杠杆品种的高风险仍在。开 MT5 把这段塞进你的 CSignal 派生类,先只改 EpisodeCycles 从 0 设到 200,看回测里训练收敛步数落在哪段。

MQL5 / C++
  class="type">void                 QL_Policy(class="type">bool value)
  {  m_policy = value;
  }
  class="type">void                 QL_EpisodeCycles(class="type">int value)
  {  m_episode_cycles = value;
  }
  class=class="str">"cmt">//--- method of verification of arch
  class="kw">virtual class="type">bool         ValidationSettings(class="type">void);
  class=class="str">"cmt">//--- method of creating the indicator and timeseries
  class="kw">virtual class="type">bool         InitIndicators(CIndicators *indicators);
  class=class="str">"cmt">//--- methods of checking if the market models are formed
  class="kw">virtual class="type">int          LongCondition(class="type">void);
  class="kw">virtual class="type">int          ShortCondition(class="type">void);
class="kw">protected:
   class="type">void                GetOutput(Cql *QL, vector &Rewards);
   Sql                 RL;
   Cql                 *QL_BUY, *QL_SELL;
   vector              REWARDS_BUY, REWARDS_SELL;
};

「把蒙特卡洛装进信号类的多空投票」

自定义信号类里跑蒙特卡洛强化学习,核心是把 Q-映射当根模型,再用 LongCondition / ShortCondition 做多空方向的「投票」。上面两个函数就是做多和做空条件的落地写法,完整源码挂在类底部,向导里拼 EA 的流程另行参考。 马尔可夫决策过程本就内嵌在 Q-映射里,但实盘里留了一个输入开关,决定 Q-值更新时是否加马尔可夫权重。另外 ε 大小、探索与利用的配比、单局回合数都能进优化器去跑。 我们拿 GBPUSD 的 2022 年 H1 数据做了优化,外汇品种波动剧烈、杠杆高风险突出,结果仅代表该样本区间的概率倾向,不等于后续行情必然复现。 下面这段是做多/做空条件的原始代码,逐行拆一下: CSignalMC::LongCondition 先给 result 置 0,调用 GetOutput(QL_BUY, REWARDS_BUY) 算出买权 Q 输出;若 QL_BUY.transition_act 等于 0,就投出 100 分看多票,否则返回 0。 CSignalMC::ShortCondition 同理,GetOutput(QL_SELL, REWARDS_SELL) 后检查 QL_SELL.transition_act 是否等于 2,命中则返回 100 分看空票,未命中返回 0。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| "Voting" that price will grow.                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CSignalMC::LongCondition(class="type">void)
{  class="type">int result = class="num">0;
  GetOutput(QL_BUY, REWARDS_BUY);
  if(QL_BUY.transition_act == class="num">0)
  {  result = class="num">100;
  }
  class="kw">return(result);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| "Voting" that price will fall.                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CSignalMC::ShortCondition(class="type">void)
{  class="type">int result = class="num">0;
  GetOutput(QL_SELL, REWARDS_SELL);
  if(QL_SELL.transition_act == class="num">2)
  {  result = class="num">100;class=class="str">"cmt">//printf(__FUNCSIG__);
  }
  class="kw">return(result);
}

◍ 把这条线请下神坛

蒙特卡洛这套强化学习思路,说白了是把 Q-学习和 SARSA 的长期环境感知揉进了一个更动态更新的框架里,但它绝不是点石成金的圣杯。自定义环境矩阵之外,把动作量表扩到三个以上选项,也只是把状态-行动空间摊得更开,不保证样本外还能稳。 偏差和方差的拉扯很实在:局内周期少(m_episode_cycles 偏小)往往高偏差低方差,模型容易贴着短期动作跑;周期拉长的局次反过来,高方差更能咬住长期特征。你在 MT5 里调 wz_45.mq5 的 m_episode_cycles,能直接看到信号强度随回报范围变粗变细。 多 n 步回报让入场离场的判定粒度更细,这点对贵金属和外汇这类高杠杆品种尤其要手稳——参数掠一下可能回测漂亮,实盘就是另一回事。开 MT5 把 SignalWZ_45.mqh 挂上,先跑小周期验证再谈放大,别把文献里的动态适应当成免死金牌。

常见问题

看 reward_decay 与 exploration_rate 两个旋钮;调高 exploration_rate 会让投票更频繁切换,回测时建议从 0.1 起逐步加。
先确认 OnTradeTransaction 与 SignalWeight 两个接口签名与文档一致,再用历史数据跑空投票验证逻辑通路。
可以,小布能按你给的信号类骨架自动调度多空投票并输出胜率分布,你只需导入参数文件看诊断页。
大概率是从采样随机性没隔离,建议固定随机种子并重跑至少 200 次路径,观察权重稳定性。
它只是概率工具,外汇贵金属高风险,仅作信号过滤用,别当确定性引擎,实盘前必做样本外检验。