您应当知道的 MQL5 向导技术(第 49 部分):搭配近端政策优化的强化学习(基础篇)
📘

您应当知道的 MQL5 向导技术(第 49 部分):搭配近端政策优化的强化学习(基础篇)

第 1/2 篇

◍ 用近端政策优化给 EA 注入强化学习

MQL5 向导从 build 4410 起支持把强化学习智能体直接编译进 EA 框架,本篇接入的是 PPO(Proximal Policy Optimization)算法,而不是更早常见的 DQN。PPO 通过对旧策略与新策略的概率比做裁剪(clip),把单步策略更新限制在 0.8~1.2 倍区间,降低了训练崩坏的概率。 在外汇与贵金属实盘里跑这类自学习 EA 属于高风险行为,历史回测亮眼不等于未来样本外同样有效,任何信号都只是概率倾向。建议先在 MT5 策略测试器用 2020—2024 年 XAUUSD 的 M15 数据做离线训练,观察 reward 曲线是否在第 120 万步后进入平缓。 向导生成的智能体默认观测维度含持仓浮盈、ATR(14) 与过去 20 根收盘价归一化序列;动作空间是离散的三档:观望 / 开多 / 开空。想验证 PPO 是否过拟合,可把测试切到 Walk Forward,看 OOS 夏普是否还能维持在 0.6 以上。

「PPO 为什么成了 RL 里的常驻主角」

近端政策优化(PPO)大约 7 年前首次发布,后来成了 ChatGPT 首选的强化学习算法。它的核心任务只有一个:在更新策略函数时,防止参数发生剧烈跳动把之前学到的东西洗掉。做法不是单打独斗,而是和基于价值的算法(如 Q-学习、SARSA、时态差异)以及基于策略的算法协同。 具体机制是用一个 clipping 函数卡住更新幅度。新策略和旧策略的概率比 r_t(θ) 乘上优势估计 Â_t,再被 ε(常取 0.1 或 0.2)限幅。优势估计我们用 Q(s_t,a_t) 减 V(s_t) 来算,也就是某动作的预期回报减去该状态的平均预期回报。这样每次只调一点点,避免过拟合最近几步的数据。 和更早的信任区域政策优化比,PPO 容易实现、更新稳、能和现代神经网络配合,连续空间和离散空间通吃。直观类比:学玩游戏时如果每局之后彻底改打法,早先的好战术就丢了;PPO 强制你只做微小、渐进、深思熟虑的调整。 它不适合探索期刚开始的阶段——那时需要根本性偏转来促进探索。但多数实盘调优场景处在利用多于发现的阶段,所以 PPO 在机器人行走、Dota 训练里被广泛使用。外汇与贵金属市场属高风险环境,任何 RL 策略迁移到场内都只能当作概率倾向,不能视为稳定胜率来源。

PPO 为什么比 DQN、A3C、TRPO 更适合交易盘

PPO 在强化学习交易系统里基本没有太多平替。能拿来说的也就是深度 Q 网络(DQN)、还没展开过的 A3C,以及前面提过的 TRPO。三者各有硬伤,而 PPO 的裁剪机制恰好绕开了大部分。 DQN 走 Q-learning,在连续动作空间里容易因为政策更新太频繁而失稳。连续动作空间指持仓规模这种用浮点数表达的决策,不是“买/卖/持”的枚举。PPO 不用单独目标网络、也不依赖经验回放,训练和落地都更轻,离散和连续空间通吃。 A3C 靠多个智服异步更新共享政策,复杂度随并行数直线上升。PPO 只用同步更新加重裁剪,避免激进步长引发政策崩溃。TRPO 则要解带约束的优化题来限制政策变化,PPO 用裁剪近似,算力省了,稳定与性能却不差太多。 对外汇、贵金属这类高波动品种,PPO 的裁剪在利用与探索间留了缓冲,过度利用奖励反而是傻事,保持“干燥”看长期更合适。若仍需探索,可上熵正则化防模型对单一动作过早自信。其优势估测让有限数据也能学——经纪商真实 tick 常缺年份级连续数据,而 PPO 不靠大回放池,小样本里从罕见崩盘或繁荣中逐步提纯好坏交易。 奖励延迟是交易 RL 的共性坑:今天开多,利润几周后才兑现,市场噪声又混淆因果。PPO 的优势函数借 V(s_t) 与 Q(s_t,a_t) 估算长期回报,把早期动作的贡献在两端拉平,归因更稳。外汇与贵金属杠杆高、回撤快,用 PPO 建模前应先以 MT5 策略测试器跑一小段真实 tick 验证归因逻辑。

◍ 把 Cql 类改造成 PPO 信号类的实操落点

在 MT5 里落地 PPO 信号,核心是把一直用的 Cql 类扩出两个受保护函数:SetPolicy 与 GetClipping。前者覆盖政策权重更新的三步,后者算裁剪比例,两者都不在选动作时直接调用,而是由 On/Off 策略函数间接触发。 数据结构上,Sppo 用 policy[] 和 gradient[] 两个矩阵数组,大小随动作数与状态数走。本系列沿用 3 种横盘环境状态(看涨、看跌、拉锯),短期与长期各记一份,因此状态索引最大为 3×3 = 9,_probabilities 向量也就定长 9。 SetPolicy 第一步用 GetMarkov(ii,iii) 把双索引压成单索引,把每个动作的累积政策权重填进 _policies;第二步用 SoftMax 把可能为负的值归一化到 0–1,得到概率分布;第三步借助优势量(旧文“状态-动作”矩阵减 Q 值矩阵)和 _clip 调节梯度符号与幅度,更新方式接近神经网络权重。 _getClipping 很短:旧概率不能为 0,否则分母加 ε;算出的归一化分数乘概率差,结果可正可负,直接决定梯度方向。改完的 Action 只取权重最大的动作,GetOutput 里用 m_scale(即 Signal_PPO_RL_Scale 滞后参数)在同一时间帧切分短长期趋势,做多/做空条件则引用 q_ppo_act 而非纯马尔可夫抽样。外汇与贵金属杠杆高、滑点跳空频繁,这套机制仅提供概率倾向,实盘前请用策略测试器以不同 m_scale 回测验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| PPO                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">struct Sppo
{   matrix              policy[];
    matrix              gradient[];
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| PPO policy update function                                       | 
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Cql::SetPolicy()
{   matrix _policies;
    _policies.Init(THIS.actions, Q_PPO.policy[acts[class="num">0]].Rows()*Q_PPO.policy[acts[class="num">0]].Cols());
    _policies.Fill(class="num">0.0);
    for(class="type">int ii = class="num">0; ii < class="type">int(Q_PPO.policy[acts[class="num">0]].Rows()); ii++)
    {   for(class="type">int iii = class="num">0; iii < class="type">int(Q_PPO.policy[acts[class="num">0]].Cols()); iii++)
        {   for(class="type">int i = class="num">0; i < THIS.actions; i++)
            {   _policies[i][GetMarkov(ii, iii)] += Q_PPO.policy[i][ii][iii];
            }
        }
    }
    vector _probabilities;
    _probabilities.Init(Q_PPO.policy[acts[class="num">0]].Rows()*Q_PPO.policy[acts[class="num">0]].Cols());
    _probabilities.Fill(class="num">0.0);
    for(class="type">int ii = class="num">0; ii < class="type">int(Q_PPO.policy[acts[class="num">0]].Rows()); ii++)
    {   for(class="type">int iii = class="num">0; iii < class="type">int(Q_PPO.policy[acts[class="num">0]].Cols()); iii++)
        {   for(class="type">int i = class="num">0; i < THIS.actions; i++)

常见问题

优先看近端政策优化(PPO),它对超参没那么敏感,比 DQN、A3C 更适合不稳定行情,先跑通基础信号类再迭代。
大概率是因为旧算法对分布偏移太敏感;PPO 靠裁剪目标函数限制单步更新幅度,实盘回撤失控的概率会低一些,但仍属高风险。
可以,小布盯盘的 AIGC 会基于历史波动和样本量给出适配倾向提示,打开对应品种页就能看到诊断,不用自己先写代码试错。
是把原先确定性开仓逻辑改成策略网络输出概率分布,并接上奖励函数;建议先只改信号生成,别动仓位管理。
不是,PPO 只是让策略在训练分布内更稳,遇到极端事件仍可能失效;外汇贵金属高风险,必须留人工干预通道。