神经网络变得轻松(第三十四部分):全部参数化的分位数函数·综合运用
🧠

神经网络变得轻松(第三十四部分):全部参数化的分位数函数·综合运用

(3/3)· 从 IQN 到 FQF,用 MQL5 落地免先验分布的分位数强化学习收尾篇

案例拆解新手友好 第 3/3 篇
很多交易者把分位数数量当成固定超参硬调,结果模型在行情突变时完全失效。用均匀分位数覆盖奖励分布,本质是用妥协换省事,真实分布一偏就漏判尾部风险。外汇贵金属高波动下,这种盲区可能直接吃掉一段回撤。

「FQF 模型在 EURUSD 上的训练与回测表现」

训练用的 FQF-learning.mq5 直接复用前一篇 QRDQN-learning.mq5 的框架,只换了文件名和底层模型对象,架构不再展开。训练数据取 EURUSD 过去 2 年 H1 周期,指标全默认,和本系列其他模型保持一致口径。 训练误差曲线平滑下行,没有剧烈抖动,说明分位数函数参数化这条路在收敛稳定性上没问题。 测试另起一个 FQF-learning-test.mq5,同样是上一篇测试 EA 的改文件名版本。回测结果:盈利因子 1.78,恢复因子 3.7,胜率 57% 以上。最大盈利单接近最大亏损单的 2.5 倍,最长连胜 10 笔、连亏 4 笔,平均盈利比平均亏损高约 ⅓。外汇与贵金属杠杆高,回测盈利不代表实盘概率可复制,上 MT5 跑相同周期前先压小仓位验证。

◍ 训练误差趋稳但别直接上实盘

这套分布式强化学习思路跑下来,参数化分位数函数类的拟合误差在训练期呈稳定收敛趋势,说明网络结构没崩。策略测试器里用训练好的模型回测,能产出正收益曲线,但这是脱离滑点、点差和实时流动性的离线环境。 外汇和贵金属是高风险品种,这种演示型 EA 当前只验证了算法链路可行,离实盘还差实时撮合与风控模块。把它当骨架,自己接行情接口和仓位约束才靠谱。 动手前先在 MT5 策略测试器用不同品种、不同周期重跑训练,确认误差曲线不发散;任何自研工具上真实账户前,都得把样本外数据测透,且默认存在本金回撤概率。

顺着这条线把 RL 读透

想把 MT5 里的强化学习策略真正跑通,光看一两篇入门不够,得按主题链补完底层脉络。从卷积网络打底,到舍弃法防过拟合,再进到 DQN 与政策梯度,这条线在原文系列里对应第 3、12、26、27、28 篇。 分布式 Q-学习把价值估计从期望值扩成完整分布,第 32、33 篇分别讲了基础版和分位数回归版,后者直接把回报分布切成若干分位来训,对尾部风险更敏感。 再往外延伸,分布视角下的隐式分位数网络(IQN)和完全参数化分位数函数,能把分位数做成连续采样,省掉固定分位数的离散约束。外汇与贵金属杠杆高、跳空频繁,用这类分布型 RL 做仓位决策前,建议先在历史 tick 上回测分位覆盖效果,实盘可能仍出现分布外偏离。

「随文发布的工程文件与一段“梦境”代码」

这套 LSTM 优化方案不是只给思路,作者把可直接跑的工程文件一并放出。核心包含 7 个部件:FQF-learning.mq5 与 FQF-learning-test.mq5 是两个 EA,前者做模型优化、后者专供策略测试器回测;FQF.mqh 封装了 FQF 模型类,NeuroNet.mqh 与 NeuroNet.cl 则分别提供神经网络 C++ 类库和 OpenCL 核函数,负责实际建网与加速;NetCreator.mq5 配 NetCreatotPanel.mqh 是可视化的模型构建小工具。 有读者在弱机环境下给每个训练周期末尾加了“睡眠”逻辑,预测准确率提升了约 3%,说明让网络在空数据上前向传播或许能轻微正则化。外汇与贵金属杠杆高,这类实验只代表概率改善,不代表实盘必然增效。 下面这段是原文里附的 Dream 函数片段,用全零缓冲喂给网络模拟“做梦”。逐行看:函数入口 dream 默认 0,注释打印提示;sleep 取 7 或传入值。内层循环按 HistoryBars 数量往 TempData 塞 12 个 0 字段(价、量、指标占位),若总长达不到 HistoryBars*12 就直接返回 0;通过后调 Net.feedForward 和 getResults,再用 TempData.Maximum(0,3) 分支记录信号。想验证就把它插进你自己 EA 的训练收尾,看回测曲线抖动有没有变小。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 梦|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int  Dream(class="type">int dream = class="num">0)
{
   Comment("!!! Dream !!! ");
   class="type">int sleep = (dream==class="num">0 ? class="num">7 : dream);
   for(class="type">int j=class="num">0;j<sleep;j++)
     {
        TempData.Clear();
        for(class="type">int b=class="num">0; b<(class="type">int)HistoryBars; b++)
          {
           if(
              !TempData.Add(class="num">0.0) || !TempData.Add(class="num">0.0) || !TempData.Add(class="num">0.0) ||
              !TempData.Add(class="num">0)   || !TempData.Add(class="num">0)   || !TempData.Add(class="num">0)   ||
              !TempData.Add(class="num">0.0) || !TempData.Add(class="num">0.0) ||
              !TempData.Add(class="num">0.0) || !TempData.Add(class="num">0.0) ||
              !TempData.Add(class="num">0.0) || !TempData.Add(class="num">0.0)
              )
              class="kw">break;
          }
        if(TempData.Total()<(class="type">int)HistoryBars*class="num">12)
           class="kw">return(class="num">0);
        Net.feedForward(TempData);
        Net.getResults(TempData);
             class=class="str">"cmt">//-- 你可以看看 NeuroNet 的 "梦境"。
              class="kw">switch(TempData.Maximum(class="num">0,class="num">3))
               {
                case class="num">0:
                  dPrevSignal=TempData[class="num">0];  
                  class="kw">break;

◍ 把这条线请下神坛

上面这段收尾代码把前一轮信号清零后,用三个 0.0 灌入 TempData 再跑一次 backProp,本质只是给网络一个空前向,并非实盘必需动作。Net.Save 在每轮 add_loop 时落盘 .nnw 并打出 Era N -> error X.XX % forecast Y.YY,你可以直接开 MT5 把日志里的 error 曲线拉出来,看它是否在 50 轮内掉到 5% 以下。 ChartScreenShot 固定截 750x400 像素,存成 FileName+count.png,方便你回看每一代「梦境」里的图形状态。Dream(SleepPeriod) 之后打印「梦想周期 = X.XX !」,SleepPeriod 随 Delta++ 递增,意味着越训越慢,这是典型的防过拟合冷却手法。 外汇与贵金属波动受事件驱动,神经网络给出的 forecast 只是概率倾向,实盘仍可能反向击穿。把这条线当参照而非圣旨,每次改 SleepPeriod 步长前先跑十轮空转验证耗时,再决定要不要上真金。

MQL5 / C++
 case class="num">1:
      dPrevSignal=-TempData[class="num">1];
      class="kw">break;
    class="kw">default:
      dPrevSignal=class="num">0;
      class="kw">break;
    }
    class=class="str">"cmt">//-- ...但这并不是必须的。
class=class="str">"cmt">//--???
    TempData.Clear();
    TempData.Add(class="num">0.0);
    TempData.Add(class="num">0.0);
    TempData.Add(class="num">0.0);
    Net.backProp(TempData);
class=class="str">"cmt">//--???
  }
  class="kw">return(class="num">0);
}
    if(add_loop)
      count++;
    if(!stop)
      {
       dError=Net.getRecentAverageError();
       if(add_loop)
         {
          Net.Save(FileName+".nnw",dError,dUndefine,dForecast,dtStudied,true);
          printf("Era %d -> error %.2f %% forecast %.2f",count,dError,dForecast);
         }
       ChartScreenShot(class="num">0,(class="type">class="kw">string)FileName+(class="type">class="kw">string)IntegerToString(count)+".png",class="num">750,class="num">400);
      }
      Dream(SleepPeriod); class=class="str">"cmt">//-- Sleep.
      printf("梦想周期 = %.2f !",SleepPeriod);
    }
SleepPeriod
SleepPeriod  + (Delta++)
让小布替你跑这套诊断
这些分位数分布诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到模型对奖励区间的覆盖密度,把重复劳动交给小布,你专注决策。

常见问题

IQN 用隐式采样生成分位数嵌入,FQF 则训练一个概率网络输出分位数权重,前馈时后者多一步权重归一化。
倾向不需要固定数量,模型自己学分布,但学习率和相关超参仍可能需在验证集上粗调。
概率上若训练集周期太短会过拟合,建议跨多个 regime 拼接样本再训练。
小布盯盘的品种页支持导入自定义指标和 EA 输出,把本文程序跑出的分布快照贴进去就能持续监控。
跳空让真实分布厚尾,均匀切分低估极端分位,FQF 自主加权可缓解但非消除该偏差。